Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

454 lines
13 KiB

  1. title "Compression and Decompression Engines"
  2. ;++
  3. ;
  4. ; Copyright (c) 1989 Microsoft Corporation
  5. ;
  6. ; Module Name:
  7. ;
  8. ; lzntx86.asm
  9. ;
  10. ; Abstract:
  11. ;
  12. ; This module implements the compression and decompression engines needed
  13. ; to support file system compression. Functions are provided to
  14. ; compress a buffer and decompress a buffer.
  15. ;
  16. ; Author:
  17. ;
  18. ; Mark Zbikowski (markz) 15-Mar-1994
  19. ;
  20. ; Environment:
  21. ;
  22. ; Any mode.
  23. ;
  24. ; Revision History:
  25. ;
  26. ; 15-Mar-1994 markz
  27. ;
  28. ; 386 version created
  29. ;
  30. ;--
  31. .386p
  32. .xlist
  33. include ks386.inc
  34. include callconv.inc ; calling convention macros
  35. .list
  36. IFDEF NTOS_KERNEL_RUNTIME
  37. _PAGE SEGMENT DWORD PUBLIC 'CODE'
  38. ELSE
  39. _TEXT SEGMENT DWORD PUBLIC 'CODE'
  40. ENDIF
  41. ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
  42. page
  43. subttl "Decompress a buffer"
  44. ;++
  45. ;
  46. ; NTSTATUS
  47. ; LZNT1DecompressChunk (
  48. ; OUT PUCHAR UncompressedBuffer,
  49. ; IN PUCHAR EndOfUncompressedBufferPlus1,
  50. ; IN PUCHAR CompressedBuffer,
  51. ; IN PUCHAR EndOfCompressedBufferPlus1,
  52. ; OUT PULONG FinalUncompressedChunkSize
  53. ; )
  54. ;
  55. ; Routine Description:
  56. ;
  57. ; This function decodes a stream of compression tokens and places the
  58. ; resultant output into the destination buffer. The format of the input
  59. ; is described ..\lznt1.c. As the input is decoded, checks are made to
  60. ; ensure that no data is read past the end of the compressed input buffer
  61. ; and that no data is stored past the end of the output buffer. Violations
  62. ; indicate corrupt input and are indicated by a status return.
  63. ;
  64. ; The following code takes advantage of two distinct observations.
  65. ; First, literal tokens occur at least twice as often as copy tokens.
  66. ; This argues for having a "fall-through" being the case where a literal
  67. ; token is found. We structure the main decomposition loop in eight
  68. ; pieces where the first piece is a sequence of literal-test fall-throughs
  69. ; and the remainder are a copy token followed by 7,6,...,0 literal-test
  70. ; fall-throughs. Each test examines a particular bit in the tag byte
  71. ; and jumps to the relevant code piece.
  72. ;
  73. ; The second observation involves performing bounds checking only
  74. ; when needed. Bounds checking the compressed buffer need only be done
  75. ; when fetching the tag byte. If there is not enough room left in the
  76. ; input for a tag byte and 8 (worst case) copy tokens, a branch is made
  77. ; to a second loop that handles a byte-by-byte "safe" copy to finish
  78. ; up the decompression. Similarly, at the head of the loop a check is
  79. ; made to ensure that there is enough room in the output buffer for 8
  80. ; literal bytes. If not enough room is left, then the second loop is
  81. ; used. Finally, after performing each copy, the output-buffer check
  82. ; is made as well since a copy may take the destination pointer
  83. ; arbitrarily close to the end of the destination.
  84. ;
  85. ; The register conventions used in the loops below are:
  86. ;
  87. ; (al) contains the current tag byte
  88. ; (ebx) contains the current width in bits of the length given
  89. ; the maximum offset
  90. ; that can be utilized in a copy token. We update this
  91. ; value only prior to performing a copy. This width is used
  92. ; both to index a mask table (for extracting the length) as
  93. ; well as shifting (for extracting the copy offset)
  94. ; (ecx) is used to contain counts during copies
  95. ; (edx) is used as a temp variable during copies
  96. ; (esi) is used mainly as the source of the next compressed token.
  97. ; It is also used for copies.
  98. ; (edi) is used as the destination of literals and copies
  99. ; (ebp) is used as a frame pointer
  100. ;
  101. ; Arguments:
  102. ;
  103. ; UncompressedBuffer (ebp+8) - pointer to destination of uncompression.
  104. ;
  105. ; EndOfUncompressedBufferPlus1 (ebp+12) - pointer just beyond the
  106. ; output buffer. This is used for consistency checking of the stored
  107. ; compressed data.
  108. ;
  109. ; CompressedBuffer (ebp+16) - pointer to compressed source. This pointer
  110. ; has been adjusted by the caller to point past the header word, so
  111. ; the pointer points to the first tag byte describing which of the
  112. ; following tokens are literals and which are copy groups.
  113. ;
  114. ; EndOfCompressedBufferPlus1 (ebp+20) - pointer just beyond end of input
  115. ; buffer. This is used to terminate the decompression.
  116. ;
  117. ; FinalUncompressedChunkSize (ebp+24) - pointer to a returned decompressed
  118. ; size. This has meaningful data ONLY when LZNT1DecompressChunk returns
  119. ; STATUS_SUCCESS
  120. ;
  121. ; Return Value:
  122. ;
  123. ; STATUS_SUCCESS is returned only if the decompression consumes thee entire
  124. ; input buffer and does not exceed the output buffer.
  125. ; STATUS_BAD_COMPRESSION_BUFFER is returned when the output buffer would be
  126. ; overflowed.
  127. ;
  128. ;--
  129. ; Decompression macros
  130. ;** TestLiteralAt - tests to see if there's a literal at a specific
  131. ; bit position. If so, it branches to the appropriate copy code
  132. ; (decorated by the bit being used).
  133. ;
  134. ; This code does no bounds checking
  135. TestLiteralAt macro CopyLabel,bit,IsMain
  136. test al,1 SHL bit ; is there a copy token at this position?
  137. jnz CopyLabel&bit ; yes, go copy it
  138. mov dl,[esi+bit+1] ; (dl) = literal byte from compressed stream
  139. ifidn <IsMain>,<Y>
  140. mov [edi+bit],dl ; store literal byte
  141. else
  142. mov [edi],dl ; store literal byte
  143. inc edi ; point to next literal
  144. endif
  145. endm
  146. ; Jump - allow specific jumps with computed labels.
  147. Jump macro lab,tag
  148. jmp lab&tag
  149. endm
  150. ;** DoCopy - perform a copy. If a bit position is specified
  151. ; then branch to the appropriate point in the "safe" tail when
  152. ; the copy takes us too close to the end of the output buffer
  153. ;
  154. ; This code checks the bounds of the copy token: copying before the
  155. ; beginning of the buffer and copying beyond the end of the buffer.
  156. DoCopy macro AdjustLabel,bit,IsMain
  157. ifidn <IsMain>,<Y>
  158. if bit ne 0
  159. add edi,bit
  160. endif
  161. endif
  162. Test&AdjustLabel&bit:
  163. cmp edi,WidthBoundary
  164. ja Adjust&AdjustLabel&bit
  165. xor ecx,ecx
  166. mov cx,word ptr [esi+bit+1] ; (ecx) = encoded length:offset
  167. lea edx,[esi+1] ; (edx) = next token location
  168. mov Temp,edx
  169. mov esi,ecx ; (esi) = encoded length:offset
  170. and ecx,MaskTab[ebx*4] ; (ecx) = length
  171. xchg ebx,ecx ; (ebx) = length/(ecx) = width
  172. shr esi,cl ; (esi) = offset
  173. xchg ebx,ecx ; (ebx) = width, (ecx) = length
  174. neg esi ; (esi) = negative real offset
  175. lea esi,[esi+edi-1] ; (esi) = pointer to previous string
  176. cmp esi,UncompressedBuffer ; off front of buffer?
  177. jb DOA ; yes, error
  178. add ecx,3 ; (ecx) = real length
  179. lea edx,[edi+ecx] ; (edx) = end of copy
  180. ifidn <IsMain>,<Y>
  181. cmp edx,EndOfSpecialDest ; do we exceed buffer?
  182. jae TailAdd&bit ; yes, handle in safe tail
  183. else
  184. cmp edx,EndOfUncompressedBufferPlus1
  185. ; do we exceed buffer?
  186. ja DOA ; yes, error
  187. endif
  188. rep movsb ; Copy the bytes
  189. mov esi,Temp ; (esi) = next token location
  190. ifidn <IsMain>,<Y>
  191. sub edi,bit+1
  192. endif
  193. endm
  194. ;** AdjustWidth - adjust width of length based upon current position of
  195. ; input buffer (max offset)
  196. AdjustWidth macro l,i
  197. Adjust&l&i:
  198. dec ebx ; (ebx) = new width pointer
  199. mov edx,UncompressedBuffer ; (edx) = pointer to dest buffer
  200. add edx,WidthTab[ebx*4] ; (edx) = new width boundary
  201. mov WidthBoundary,edx ; save boundary for comparison
  202. jmp Test&l&i
  203. endm
  204. ;** GenerateBlock - generates the unsafe block of copy/literal pieces.
  205. ;
  206. ; This code does no checking for simple input/output checking. Only
  207. ; the data referred to by the copy tokens is checked.
  208. GenerateBlock macro bit
  209. Copy&bit:
  210. DoCopy Body,bit,Y
  211. j = bit + 1
  212. while j lt 8
  213. TestLiteralAt Copy,%(j),Y
  214. j = j + 1
  215. endm
  216. add esi,9
  217. add edi,8
  218. jmp Top
  219. AdjustWidth Body,bit
  220. endm
  221. ;** GenerateTailBlock - generates safe tail block for compression. This
  222. ; code checks everything before each byte stored so it is expected
  223. ; to be executed only at the end of the buffer.
  224. GenerateTailBlock macro bit
  225. TailAdd&bit:
  226. add EndOfCompressedBufferPlus1,1+2*8
  227. ; restore buffer length to true length
  228. mov esi,Temp ; (esi) = source of copy token block
  229. dec esi
  230. Tail&bit:
  231. lea ecx,[esi+bit+1] ; (ecx) = source of next token
  232. cmp ecx,EndOfCompressedBufferPlus1 ; are we done?
  233. jz Done ; yes - we exactly match end of buffer
  234. ; ja DOA ; INTERNAL ERROR only
  235. cmp edi,EndOfUncompressedBufferPlus1
  236. jz Done ; go quit, destination is full
  237. ; ja DOA ; INTERNAL ERROR only
  238. TestLiteralAt TailCopy,bit,N
  239. Jump Tail,%(bit+1)
  240. ; We expect a copy token to be at [esi+bit+1]. This means that
  241. ; esi+bit+1+tokensize must be <= EndOfCompressedBufferPlus1
  242. TailCopy&bit:
  243. lea ecx,[esi+bit+3] ; (ecx) = next input position
  244. cmp ecx,EndOfCompressedBufferPlus1 ; do we go too far
  245. ja DOA ; yes, we are beyond the end of buffer
  246. DoCopy Tail,bit,N ; perform copy
  247. Jump Tail,%(bit+1)
  248. AdjustWidth Tail,bit
  249. endm
  250. cPublicProc _LZNT1DecompressChunk ,5
  251. push ebp ; (tos) = saved frame pointer
  252. mov ebp,esp ; (ebp) = frame pointer to arguments
  253. sub esp,12 ; Open up room for locals
  254. Temp equ dword ptr [ebp-12]
  255. WidthBoundary equ dword ptr [ebp-8]
  256. EndOfSpecialDest equ dword ptr [ebp-4]
  257. ;SavedEBP equ dword ptr [ebp]
  258. ;ReturnAddress equ dword ptr [ebp+4]
  259. UncompressedBuffer equ dword ptr [ebp+8]
  260. EndOfUncompressedBufferPlus1 equ dword ptr [ebp+12]
  261. CompressedBuffer equ dword ptr [ebp+16]
  262. EndOfCompressedBufferPlus1 equ dword ptr [ebp+20]
  263. FinalUncompressedChunkSize equ dword ptr [ebp+24]
  264. push ebx
  265. push esi
  266. push edi
  267. mov edi,UncompressedBuffer ; (edi) = destination of decompress
  268. mov esi,CompressedBuffer ; (esi) = header
  269. sub EndOfCompressedBufferPlus1,1+2*8 ; make room for special source
  270. mov eax,EndOfUncompressedBufferPlus1 ; (eax) = end of destination
  271. sub eax,8 ; (eax) = beginning of special tail
  272. mov EndOfSpecialDest,eax ; store special tail
  273. mov WidthBoundary,edi ; force initial width mismatch
  274. mov ebx,13 ; initial width of output
  275. Top: cmp esi,EndOfCompressedBufferPlus1 ; Will this be the last tag group in source?
  276. jae DoTail ; yes, go handle specially
  277. cmp edi,EndOfSpecialDest ; are we too close to end of buffer?
  278. jae DoTail ; yes, go skip to end
  279. mov al,byte ptr [esi] ; (al) = tag byte, (esi) points to token
  280. irpc i,<01234567>
  281. TestLiteralAt Copy,%(i),Y
  282. endm
  283. add esi,9
  284. add edi,8
  285. jmp Top
  286. ; ; Width of offset Width of length
  287. WidthTab dd 0FFFFh ; 16 0
  288. dd 0FFFFh ; 15 1
  289. dd 0FFFFh ; 14 2
  290. dd 0FFFFh ; 13 3
  291. dd 0FFFFh ; 12 4
  292. dd 2048 ; 11 5
  293. dd 1024 ; 10 6
  294. dd 512 ; 9 7
  295. dd 256 ; 8 8
  296. dd 128 ; 7 9
  297. dd 64 ; 6 10
  298. dd 32 ; 5 11
  299. dd 16 ; 4 12
  300. dd 0 ; 3 13
  301. dd 0 ; 2 14
  302. dd 0 ; 1 15
  303. dd 0 ; 0 16
  304. ; ;
  305. MaskTab dd 0000000000000000b ; 0
  306. dd 0000000000000001b ; 1
  307. dd 0000000000000011b ; 2
  308. dd 0000000000000111b ; 3
  309. dd 0000000000001111b ; 4
  310. dd 0000000000011111b ; 5
  311. dd 0000000000111111b ; 6
  312. dd 0000000001111111b ; 7
  313. dd 0000000011111111b ; 8
  314. dd 0000000111111111b ; 9
  315. dd 0000001111111111b ; 10
  316. dd 0000011111111111b ; 11
  317. dd 0000111111111111b ; 12
  318. dd 0001111111111111b ; 13
  319. dd 0011111111111111b ; 14
  320. dd 0111111111111111b ; 15
  321. dd 1111111111111111b ; 16
  322. irpc i,<01234567>
  323. GenerateBlock %(i)
  324. endm
  325. ; We're handling a tail specially for this. We must check at all
  326. ; spots for running out of input as well as overflowing output.
  327. ;
  328. ; (esi) = pointer to possible next tag
  329. DoTail: add EndOfCompressedBufferPlus1,1+2*8 ; point to end of compressed input
  330. TailLoop:
  331. cmp esi,EndOfCompressedBufferPlus1 ; are we totally done?
  332. jz Done ; yes, go return
  333. mov al,byte ptr [esi] ; (al) = tag byte
  334. jmp Tail0
  335. irpc i,<01234567>
  336. GenerateTailBlock i
  337. endm
  338. Tail8: add esi,9
  339. jmp TailLoop
  340. DOA: mov eax,STATUS_BAD_COMPRESSION_BUFFER
  341. jmp Final
  342. Done: mov eax,edi ; (eax) = pointer to next byte to store
  343. sub eax,UncompressedBuffer ; (eax) = length of uncompressed
  344. mov edi,FinalUncompressedChunkSize ; (edi) = user return value location
  345. mov [edi],eax ; return total transfer size to user
  346. xor eax,eax ; (eax) = STATUS_SUCCESS
  347. Final: pop edi
  348. pop esi
  349. pop ebx
  350. mov esp,ebp
  351. pop ebp
  352. stdRET _LZNT1DecompressChunk
  353. stdENDP _LZNT1DecompressChunk
  354. IFDEF NTOS_KERNEL_RUNTIME
  355. _PAGE ENDS
  356. ELSE
  357. _TEXT ENDS
  358. ENDIF
  359. end