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.

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