summaryrefslogtreecommitdiffstats
path: root/private/ntos/rtl/i386/lzntx86.asm
blob: cf21d5456f9e3750bbb654064d75a5d5353afb57 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
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