summaryrefslogtreecommitdiffstats
path: root/private/ntos/rtl/i386/lzntx86.asm
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/rtl/i386/lzntx86.asm
downloadNT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip
Diffstat (limited to 'private/ntos/rtl/i386/lzntx86.asm')
-rw-r--r--private/ntos/rtl/i386/lzntx86.asm445
1 files changed, 445 insertions, 0 deletions
diff --git a/private/ntos/rtl/i386/lzntx86.asm b/private/ntos/rtl/i386/lzntx86.asm
new file mode 100644
index 000000000..cf21d5456
--- /dev/null
+++ b/private/ntos/rtl/i386/lzntx86.asm
@@ -0,0 +1,445 @@
+ title "Compression and Decompression Engines"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; lzntx86.asm
+;
+; Abstract:
+;
+; This module implements the compression and decompression engines needed
+; to support file system compression. Functions are provided to
+; compress a buffer and decompress a buffer.
+;
+; Author:
+;
+; Mark Zbikowski (markz) 15-Mar-1994
+;
+; Environment:
+;
+; Any mode.
+;
+; Revision History:
+;
+; 15-Mar-1994 markz
+;
+; 386 version created
+;
+;--
+.386p
+
+ .xlist
+include ks386.inc
+include callconv.inc ; calling convention macros
+ .list
+
+_TEXT$01 SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+ page
+ subttl "Decompress a buffer"
+;++
+;
+; NTSTATUS
+; LZNT1DecompressChunk (
+; OUT PUCHAR UncompressedBuffer,
+; IN PUCHAR EndOfUncompressedBufferPlus1,
+; IN PUCHAR CompressedBuffer,
+; IN PUCHAR EndOfCompressedBufferPlus1,
+; OUT PULONG FinalUncompressedChunkSize
+; )
+;
+; Routine Description:
+;
+; This function decodes a stream of compression tokens and places the
+; resultant output into the destination buffer. The format of the input
+; is described ..\lznt1.c. As the input is decoded, checks are made to
+; ensure that no data is read past the end of the compressed input buffer
+; and that no data is stored past the end of the output buffer. Violations
+; indicate corrupt input and are indicated by a status return.
+;
+; The following code takes advantage of two distinct observations.
+; First, literal tokens occur at least twice as often as copy tokens.
+; This argues for having a "fall-through" being the case where a literal
+; token is found. We structure the main decomposition loop in eight
+; pieces where the first piece is a sequence of literal-test fall-throughs
+; and the remainder are a copy token followed by 7,6,...,0 literal-test
+; fall-throughs. Each test examines a particular bit in the tag byte
+; and jumps to the relevant code piece.
+;
+; The second observation involves performing bounds checking only
+; when needed. Bounds checking the compressed buffer need only be done
+; when fetching the tag byte. If there is not enough room left in the
+; input for a tag byte and 8 (worst case) copy tokens, a branch is made
+; to a second loop that handles a byte-by-byte "safe" copy to finish
+; up the decompression. Similarly, at the head of the loop a check is
+; made to ensure that there is enough room in the output buffer for 8
+; literal bytes. If not enough room is left, then the second loop is
+; used. Finally, after performing each copy, the output-buffer check
+; is made as well since a copy may take the destination pointer
+; arbitrarily close to the end of the destination.
+;
+; The register conventions used in the loops below are:
+;
+; (al) contains the current tag byte
+; (ebx) contains the current width in bits of the length given
+; the maximum offset
+; that can be utilized in a copy token. We update this
+; value only prior to performing a copy. This width is used
+; both to index a mask table (for extracting the length) as
+; well as shifting (for extracting the copy offset)
+; (ecx) is used to contain counts during copies
+; (edx) is used as a temp variable during copies
+; (esi) is used mainly as the source of the next compressed token.
+; It is also used for copies.
+; (edi) is used as the destination of literals and copies
+; (ebp) is used as a frame pointer
+;
+; Arguments:
+;
+; UncompressedBuffer (ebp+8) - pointer to destination of uncompression.
+;
+; EndOfUncompressedBufferPlus1 (ebp+12) - pointer just beyond the
+; output buffer. This is used for consistency checking of the stored
+; compressed data.
+;
+; CompressedBuffer (ebp+16) - pointer to compressed source. This pointer
+; has been adjusted by the caller to point past the header word, so
+; the pointer points to the first tag byte describing which of the
+; following tokens are literals and which are copy groups.
+;
+; EndOfCompressedBufferPlus1 (ebp+20) - pointer just beyond end of input
+; buffer. This is used to terminate the decompression.
+;
+; FinalUncompressedChunkSize (ebp+24) - pointer to a returned decompressed
+; size. This has meaningful data ONLY when LZNT1DecompressChunk returns
+; STATUS_SUCCESS
+;
+; Return Value:
+;
+; STATUS_SUCCESS is returned only if the decompression consumes thee entire
+; input buffer and does not exceed the output buffer.
+; STATUS_BAD_COMPRESSION_BUFFER is returned when the output buffer would be
+; overflowed.
+;
+;--
+
+; Decompression macros
+
+;** TestLiteralAt - tests to see if there's a literal at a specific
+; bit position. If so, it branches to the appropriate copy code
+; (decorated by the bit being used).
+;
+; This code does no bounds checking
+
+TestLiteralAt macro CopyLabel,bit,IsMain
+ test al,1 SHL bit ; is there a copy token at this position?
+ jnz CopyLabel&bit ; yes, go copy it
+
+ mov dl,[esi+bit+1] ; (dl) = literal byte from compressed stream
+ifidn <IsMain>,<Y>
+ mov [edi+bit],dl ; store literal byte
+else
+ mov [edi],dl ; store literal byte
+ inc edi ; point to next literal
+endif
+
+endm
+
+
+; Jump - allow specific jumps with computed labels.
+
+Jump macro lab,tag
+ jmp lab&tag
+endm
+
+
+
+;** DoCopy - perform a copy. If a bit position is specified
+; then branch to the appropriate point in the "safe" tail when
+; the copy takes us too close to the end of the output buffer
+;
+; This code checks the bounds of the copy token: copying before the
+; beginning of the buffer and copying beyond the end of the buffer.
+
+DoCopy macro AdjustLabel,bit,IsMain
+
+ifidn <IsMain>,<Y>
+if bit ne 0
+ add edi,bit
+endif
+endif
+
+Test&AdjustLabel&bit:
+ cmp edi,WidthBoundary
+ ja Adjust&AdjustLabel&bit
+
+ xor ecx,ecx
+ mov cx,word ptr [esi+bit+1] ; (ecx) = encoded length:offset
+ lea edx,[esi+1] ; (edx) = next token location
+ mov Temp,edx
+
+ mov esi,ecx ; (esi) = encoded length:offset
+ and ecx,MaskTab[ebx*4] ; (ecx) = length
+ xchg ebx,ecx ; (ebx) = length/(ecx) = width
+ shr esi,cl ; (esi) = offset
+ xchg ebx,ecx ; (ebx) = width, (ecx) = length
+
+ neg esi ; (esi) = negative real offset
+ lea esi,[esi+edi-1] ; (esi) = pointer to previous string
+
+ cmp esi,UncompressedBuffer ; off front of buffer?
+ jb DOA ; yes, error
+
+ add ecx,3 ; (ecx) = real length
+
+ lea edx,[edi+ecx] ; (edx) = end of copy
+ifidn <IsMain>,<Y>
+ cmp edx,EndOfSpecialDest ; do we exceed buffer?
+ jae TailAdd&bit ; yes, handle in safe tail
+else
+ cmp edx,EndOfUncompressedBufferPlus1
+ ; do we exceed buffer?
+ ja DOA ; yes, error
+endif
+
+ rep movsb ; Copy the bytes
+
+ mov esi,Temp ; (esi) = next token location
+
+ifidn <IsMain>,<Y>
+ sub edi,bit+1
+endif
+
+endm
+
+
+
+
+
+;** AdjustWidth - adjust width of length based upon current position of
+; input buffer (max offset)
+
+
+AdjustWidth macro l,i
+Adjust&l&i:
+ dec ebx ; (ebx) = new width pointer
+ mov edx,UncompressedBuffer ; (edx) = pointer to dest buffer
+ add edx,WidthTab[ebx*4] ; (edx) = new width boundary
+ mov WidthBoundary,edx ; save boundary for comparison
+ jmp Test&l&i
+
+endm
+
+
+;** GenerateBlock - generates the unsafe block of copy/literal pieces.
+;
+; This code does no checking for simple input/output checking. Only
+; the data referred to by the copy tokens is checked.
+
+GenerateBlock macro bit
+Copy&bit:
+
+ DoCopy Body,bit,Y
+
+ j = bit + 1
+ while j lt 8
+ TestLiteralAt Copy,%(j),Y
+ j = j + 1
+ endm
+
+ add esi,9
+ add edi,8
+
+ jmp Top
+
+ AdjustWidth Body,bit
+endm
+
+
+
+;** GenerateTailBlock - generates safe tail block for compression. This
+; code checks everything before each byte stored so it is expected
+; to be executed only at the end of the buffer.
+
+
+GenerateTailBlock macro bit
+TailAdd&bit:
+ add EndOfCompressedBufferPlus1,1+2*8
+ ; restore buffer length to true length
+ mov esi,Temp ; (esi) = source of copy token block
+ dec esi
+
+Tail&bit:
+ lea ecx,[esi+bit+1] ; (ecx) = source of next token
+ cmp ecx,EndOfCompressedBufferPlus1 ; are we done?
+ jz Done ; yes - we exactly match end of buffer
+; ja DOA ; INTERNAL ERROR only
+
+ cmp edi,EndOfUncompressedBufferPlus1
+ jz Done ; go quit, destination is full
+; ja DOA ; INTERNAL ERROR only
+
+ TestLiteralAt TailCopy,bit,N
+
+ Jump Tail,%(bit+1)
+
+
+; We expect a copy token to be at [esi+bit+1]. This means that
+; esi+bit+1+tokensize must be <= EndOfCompressedBufferPlus1
+TailCopy&bit:
+ lea ecx,[esi+bit+3] ; (ecx) = next input position
+ cmp ecx,EndOfCompressedBufferPlus1 ; do we go too far
+ ja DOA ; yes, we are beyond the end of buffer
+
+ DoCopy Tail,bit,N ; perform copy
+
+ Jump Tail,%(bit+1)
+
+ AdjustWidth Tail,bit
+
+endm
+
+
+
+cPublicProc _LZNT1DecompressChunk ,5
+ push ebp ; (tos) = saved frame pointer
+ mov ebp,esp ; (ebp) = frame pointer to arguments
+ sub esp,12 ; Open up room for locals
+
+Temp equ dword ptr [ebp-12]
+WidthBoundary equ dword ptr [ebp-8]
+EndOfSpecialDest equ dword ptr [ebp-4]
+
+;SavedEBP equ dword ptr [ebp]
+;ReturnAddress equ dword ptr [ebp+4]
+
+UncompressedBuffer equ dword ptr [ebp+8]
+EndOfUncompressedBufferPlus1 equ dword ptr [ebp+12]
+CompressedBuffer equ dword ptr [ebp+16]
+EndOfCompressedBufferPlus1 equ dword ptr [ebp+20]
+FinalUncompressedChunkSize equ dword ptr [ebp+24]
+
+
+ push ebx
+ push esi
+ push edi
+
+ mov edi,UncompressedBuffer ; (edi) = destination of decompress
+ mov esi,CompressedBuffer ; (esi) = header
+ sub EndOfCompressedBufferPlus1,1+2*8 ; make room for special source
+
+ mov eax,EndOfUncompressedBufferPlus1 ; (eax) = end of destination
+ sub eax,8 ; (eax) = beginning of special tail
+ mov EndOfSpecialDest,eax ; store special tail
+
+ mov WidthBoundary,edi ; force initial width mismatch
+ mov ebx,13 ; initial width of output
+
+
+Top: cmp esi,EndOfCompressedBufferPlus1 ; Will this be the last tag group in source?
+ jae DoTail ; yes, go handle specially
+ cmp edi,EndOfSpecialDest ; are we too close to end of buffer?
+ jae DoTail ; yes, go skip to end
+
+ mov al,byte ptr [esi] ; (al) = tag byte, (esi) points to token
+
+ irpc i,<01234567>
+ TestLiteralAt Copy,%(i),Y
+ endm
+
+ add esi,9
+ add edi,8
+
+ jmp Top
+; ; Width of offset Width of length
+WidthTab dd 0FFFFh ; 16 0
+ dd 0FFFFh ; 15 1
+ dd 0FFFFh ; 14 2
+ dd 0FFFFh ; 13 3
+ dd 0FFFFh ; 12 4
+ dd 2048 ; 11 5
+ dd 1024 ; 10 6
+ dd 512 ; 9 7
+ dd 256 ; 8 8
+ dd 128 ; 7 9
+ dd 64 ; 6 10
+ dd 32 ; 5 11
+ dd 16 ; 4 12
+ dd 0 ; 3 13
+ dd 0 ; 2 14
+ dd 0 ; 1 15
+ dd 0 ; 0 16
+
+
+; ;
+MaskTab dd 0000000000000000b ; 0
+ dd 0000000000000001b ; 1
+ dd 0000000000000011b ; 2
+ dd 0000000000000111b ; 3
+ dd 0000000000001111b ; 4
+ dd 0000000000011111b ; 5
+ dd 0000000000111111b ; 6
+ dd 0000000001111111b ; 7
+ dd 0000000011111111b ; 8
+ dd 0000000111111111b ; 9
+ dd 0000001111111111b ; 10
+ dd 0000011111111111b ; 11
+ dd 0000111111111111b ; 12
+ dd 0001111111111111b ; 13
+ dd 0011111111111111b ; 14
+ dd 0111111111111111b ; 15
+ dd 1111111111111111b ; 16
+
+
+ irpc i,<01234567>
+ GenerateBlock %(i)
+ endm
+
+; We're handling a tail specially for this. We must check at all
+; spots for running out of input as well as overflowing output.
+;
+; (esi) = pointer to possible next tag
+
+DoTail: add EndOfCompressedBufferPlus1,1+2*8 ; point to end of compressed input
+
+TailLoop:
+ cmp esi,EndOfCompressedBufferPlus1 ; are we totally done?
+ jz Done ; yes, go return
+ mov al,byte ptr [esi] ; (al) = tag byte
+
+ jmp Tail0
+
+ irpc i,<01234567>
+ GenerateTailBlock i
+ endm
+
+Tail8: add esi,9
+ jmp TailLoop
+
+
+
+DOA: mov eax,STATUS_BAD_COMPRESSION_BUFFER
+ jmp Final
+
+Done: mov eax,edi ; (eax) = pointer to next byte to store
+ sub eax,UncompressedBuffer ; (eax) = length of uncompressed
+ mov edi,FinalUncompressedChunkSize ; (edi) = user return value location
+ mov [edi],eax ; return total transfer size to user
+ xor eax,eax ; (eax) = STATUS_SUCCESS
+
+Final: pop edi
+ pop esi
+ pop ebx
+ mov esp,ebp
+ pop ebp
+
+
+ stdRET _LZNT1DecompressChunk
+
+stdENDP _LZNT1DecompressChunk
+
+_TEXT$01 ends
+ end