windows-nt/Source/XPSP1/NT/base/ntos/rtl/i386/lzntx86.asm
2020-09-26 16:20:57 +08:00

455 lines
13 KiB
NASM
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
IFDEF NTOS_KERNEL_RUNTIME
_PAGE SEGMENT DWORD PUBLIC 'CODE'
ELSE
_TEXT SEGMENT DWORD PUBLIC 'CODE'
ENDIF
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
IFDEF NTOS_KERNEL_RUNTIME
_PAGE ENDS
ELSE
_TEXT ENDS
ENDIF
end