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.

2866 lines
84 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. LZNT1.c
  5. Abstract:
  6. This module implements the LZNT1 compression engine.
  7. Author:
  8. Gary Kimura [GaryKi] 21-Jan-1994
  9. Revision History:
  10. --*/
  11. #include "ntrtlp.h"
  12. #include <stdio.h>
  13. //
  14. // Boolean which controls whether the asserts will fire.
  15. //
  16. #if DBG
  17. #if !BLDR_KERNEL_RUNTIME
  18. BOOLEAN Lznt1Break = TRUE;
  19. #else
  20. BOOLEAN Lznt1Break = FALSE;
  21. #endif
  22. #endif
  23. //
  24. // Declare the internal workspace that we need
  25. //
  26. typedef struct _LZNT1_STANDARD_WORKSPACE {
  27. PUCHAR UncompressedBuffer;
  28. PUCHAR EndOfUncompressedBufferPlus1;
  29. ULONG MaxLength;
  30. PUCHAR MatchedString;
  31. PUCHAR IndexPTable[4096][2];
  32. } LZNT1_STANDARD_WORKSPACE, *PLZNT1_STANDARD_WORKSPACE;
  33. typedef struct _LZNT1_MAXIMUM_WORKSPACE {
  34. PUCHAR UncompressedBuffer;
  35. PUCHAR EndOfUncompressedBufferPlus1;
  36. ULONG MaxLength;
  37. PUCHAR MatchedString;
  38. } LZNT1_MAXIMUM_WORKSPACE, *PLZNT1_MAXIMUM_WORKSPACE;
  39. typedef struct _LZNT1_FRAGMENT_WORKSPACE {
  40. UCHAR Buffer[0x1000];
  41. } LZNT1_FRAGMENT_WORKSPACE, *PLZNT1_FRAGMENT_WORKSPACE;
  42. typedef struct _LZNT1_HIBER_WORKSPACE {
  43. ULONG IndexTable[1<<12];
  44. } LZNT1_HIBER_WORKSPACE, *PLZNT1_HIBER_WORKSPACE;
  45. //
  46. // Now define the local procedure prototypes.
  47. //
  48. typedef ULONG (*PLZNT1_MATCH_FUNCTION) (
  49. );
  50. NTSTATUS
  51. LZNT1CompressChunk (
  52. IN PLZNT1_MATCH_FUNCTION MatchFunction,
  53. IN PUCHAR UncompressedBuffer,
  54. IN PUCHAR EndOfUncompressedBufferPlus1,
  55. OUT PUCHAR CompressedBuffer,
  56. IN PUCHAR EndOfCompressedBufferPlus1,
  57. OUT PULONG FinalCompressedChunkSize,
  58. IN PVOID WorkSpace
  59. );
  60. NTSTATUS
  61. LZNT1CompressChunkHiber (
  62. IN PUCHAR UncompressedBuffer,
  63. IN PUCHAR EndOfUncompressedBufferPlus1,
  64. OUT PUCHAR CompressedBuffer,
  65. IN PUCHAR EndOfCompressedBufferPlus1,
  66. OUT PULONG FinalCompressedChunkSize,
  67. IN PVOID WorkSpace
  68. );
  69. NTSTATUS
  70. LZNT1DecompressChunk (
  71. OUT PUCHAR UncompressedBuffer,
  72. IN PUCHAR EndOfUncompressedBufferPlus1,
  73. IN PUCHAR CompressedBuffer,
  74. IN PUCHAR EndOfCompressedBufferPlus1,
  75. OUT PULONG FinalUncompressedChunkSize
  76. );
  77. ULONG
  78. LZNT1FindMatchStandard (
  79. IN PUCHAR ZivString,
  80. IN PLZNT1_STANDARD_WORKSPACE WorkSpace
  81. );
  82. ULONG
  83. LZNT1FindMatchMaximum (
  84. IN PUCHAR ZivString,
  85. IN PVOID WorkSpace
  86. );
  87. NTSTATUS
  88. RtlCompressBufferLZNT1_HIBER (
  89. IN USHORT Engine,
  90. IN PUCHAR UncompressedBuffer,
  91. IN ULONG UncompressedBufferSize,
  92. OUT PUCHAR CompressedBuffer,
  93. IN ULONG CompressedBufferSize,
  94. IN ULONG UncompressedChunkSize,
  95. OUT PULONG FinalCompressedSize,
  96. IN PVOID WorkSpace
  97. );
  98. //
  99. // Local data structures
  100. //
  101. //
  102. // The compressed chunk header is the structure that starts every
  103. // new chunk in the compressed data stream. In our definition here
  104. // we union it with a ushort to make setting and retrieving the chunk
  105. // header easier. The header stores the size of the compressed chunk,
  106. // its signature, and if the data stored in the chunk is compressed or
  107. // not.
  108. //
  109. // Compressed Chunk Size:
  110. //
  111. // The actual size of a compressed chunk ranges from 4 bytes (2 byte
  112. // header, 1 flag byte, and 1 literal byte) to 4098 bytes (2 byte
  113. // header, and 4096 bytes of uncompressed data). The size is encoded
  114. // in a 12 bit field biased by 3. A value of 1 corresponds to a chunk
  115. // size of 4, 2 => 5, ..., 4095 => 4098. A value of zero is special
  116. // because it denotes the ending chunk header.
  117. //
  118. // Chunk Signature:
  119. //
  120. // The only valid signature value is 3. This denotes a 4KB uncompressed
  121. // chunk using with the 4/12 to 12/4 sliding offset/length encoding.
  122. //
  123. // Is Chunk Compressed:
  124. //
  125. // If the data in the chunk is compressed this field is 1 otherwise
  126. // the data is uncompressed and this field is 0.
  127. //
  128. // The ending chunk header in a compressed buffer contains the a value of
  129. // zero (space permitting).
  130. //
  131. typedef union _COMPRESSED_CHUNK_HEADER {
  132. struct {
  133. USHORT CompressedChunkSizeMinus3 : 12;
  134. USHORT ChunkSignature : 3;
  135. USHORT IsChunkCompressed : 1;
  136. } Chunk;
  137. USHORT Short;
  138. } COMPRESSED_CHUNK_HEADER, *PCOMPRESSED_CHUNK_HEADER;
  139. #define MAX_UNCOMPRESSED_CHUNK_SIZE (4096)
  140. //
  141. // USHORT
  142. // GetCompressedChunkSize (
  143. // IN COMPRESSED_CHUNK_HEADER ChunkHeader
  144. // );
  145. //
  146. // USHORT
  147. // GetUncompressedChunkSize (
  148. // IN COMPRESSED_CHUNK_HEADER ChunkHeader
  149. // );
  150. //
  151. // VOID
  152. // SetCompressedChunkHeader (
  153. // IN OUT COMPRESSED_CHUNK_HEADER ChunkHeader,
  154. // IN USHORT CompressedChunkSize,
  155. // IN BOOLEAN IsChunkCompressed
  156. // );
  157. //
  158. #define GetCompressedChunkSize(CH) ( \
  159. (CH).Chunk.CompressedChunkSizeMinus3 + 3 \
  160. )
  161. #define GetUncompressedChunkSize(CH) (MAX_UNCOMPRESSED_CHUNK_SIZE)
  162. #define SetCompressedChunkHeader(CH,CCS,ICC) { \
  163. ASSERT((CCS) >= 4 && (CCS) <= 4098); \
  164. (CH).Chunk.CompressedChunkSizeMinus3 = (CCS) - 3; \
  165. (CH).Chunk.ChunkSignature = 3; \
  166. (CH).Chunk.IsChunkCompressed = (ICC); \
  167. }
  168. //
  169. // Local macros
  170. //
  171. #define FlagOn(F,SF) ((F) & (SF))
  172. #define SetFlag(F,SF) { (F) |= (SF); }
  173. #define ClearFlag(F,SF) { (F) &= ~(SF); }
  174. #define Minimum(A,B) ((A) < (B) ? (A) : (B))
  175. #define Maximum(A,B) ((A) > (B) ? (A) : (B))
  176. #if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
  177. //
  178. // N.B. Several functions below are placed in the PAGELK section
  179. // because they need to be locked down in memory during Hibernation,
  180. // since they are used to enable compression of the Hiberfile.
  181. //
  182. #pragma alloc_text(PAGELK, RtlCompressWorkSpaceSizeLZNT1)
  183. #pragma alloc_text(PAGELK, RtlCompressBufferLZNT1)
  184. #pragma alloc_text(PAGELK, RtlCompressBufferLZNT1_HIBER)
  185. #pragma alloc_text(PAGE, RtlDecompressBufferLZNT1)
  186. #pragma alloc_text(PAGE, RtlDecompressFragmentLZNT1)
  187. #pragma alloc_text(PAGE, RtlDescribeChunkLZNT1)
  188. #pragma alloc_text(PAGE, RtlReserveChunkLZNT1)
  189. #pragma alloc_text(PAGELK, LZNT1CompressChunk)
  190. #pragma alloc_text(PAGELK, LZNT1CompressChunkHiber)
  191. #if !defined(_X86_)
  192. #pragma alloc_text(PAGE, LZNT1DecompressChunk)
  193. #endif
  194. #pragma alloc_text(PAGELK, LZNT1FindMatchStandard)
  195. #pragma alloc_text(PAGE, LZNT1FindMatchMaximum)
  196. #endif
  197. NTSTATUS
  198. RtlCompressWorkSpaceSizeLZNT1 (
  199. IN USHORT Engine,
  200. OUT PULONG CompressBufferWorkSpaceSize,
  201. OUT PULONG CompressFragmentWorkSpaceSize
  202. )
  203. /*++
  204. Routine Description:
  205. Arguments:
  206. Return Value:
  207. --*/
  208. {
  209. if (Engine == COMPRESSION_ENGINE_STANDARD) {
  210. *CompressBufferWorkSpaceSize = sizeof(LZNT1_STANDARD_WORKSPACE);
  211. *CompressFragmentWorkSpaceSize = sizeof(LZNT1_FRAGMENT_WORKSPACE);
  212. return STATUS_SUCCESS;
  213. } else if (Engine == COMPRESSION_ENGINE_MAXIMUM) {
  214. *CompressBufferWorkSpaceSize = sizeof(LZNT1_MAXIMUM_WORKSPACE);
  215. *CompressFragmentWorkSpaceSize = sizeof(LZNT1_FRAGMENT_WORKSPACE);
  216. return STATUS_SUCCESS;
  217. } else {
  218. return STATUS_NOT_SUPPORTED;
  219. }
  220. }
  221. NTSTATUS
  222. RtlCompressBufferLZNT1_HIBER (
  223. IN USHORT Engine,
  224. IN PUCHAR UncompressedBuffer,
  225. IN ULONG UncompressedBufferSize,
  226. OUT PUCHAR CompressedBuffer,
  227. IN ULONG CompressedBufferSize,
  228. IN ULONG UncompressedChunkSize,
  229. OUT PULONG FinalCompressedSize,
  230. IN PVOID WorkSpace
  231. )
  232. /*++
  233. Routine Description:
  234. This routine takes as input an uncompressed buffer and produces
  235. its compressed equivalent provided the compressed data fits within
  236. the specified destination buffer.
  237. An output variable indicates the number of bytes used to store
  238. the compressed buffer.
  239. This routine is only to be used on the hibernate path. It is faster than the normal
  240. compress code but 5% less space efficient.
  241. Arguments:
  242. UncompressedBuffer - Supplies a pointer to the uncompressed data.
  243. UncompressedBufferSize - Supplies the size, in bytes, of the
  244. uncompressed buffer.
  245. CompressedBuffer - Supplies a pointer to where the compressed data
  246. is to be stored.
  247. CompressedBufferSize - Supplies the size, in bytes, of the
  248. compressed buffer.
  249. UncompressedChunkSize - Ignored.
  250. FinalCompressedSize - Receives the number of bytes needed in
  251. the compressed buffer to store the compressed data.
  252. WorkSpace - Mind your own business, just give it to me.
  253. Return Value:
  254. STATUS_SUCCESS - the compression worked without a hitch.
  255. STATUS_BUFFER_ALL_ZEROS - the compression worked without a hitch and in
  256. addition the input buffer was all zeros.
  257. STATUS_BUFFER_TOO_SMALL - the compressed buffer is too small to hold the
  258. compressed data.
  259. --*/
  260. {
  261. NTSTATUS Status;
  262. PLZNT1_MATCH_FUNCTION MatchFunction;
  263. PUCHAR UncompressedChunk;
  264. PUCHAR CompressedChunk;
  265. LONG CompressedChunkSize;
  266. //
  267. // The following variable is used to tell if we have processed an entire
  268. // buffer of zeros and that we should return an alternate status value
  269. //
  270. BOOLEAN AllZero = TRUE;
  271. //
  272. // The following variables are pointers to the byte following the
  273. // end of each appropriate buffer.
  274. //
  275. PUCHAR EndOfUncompressedBuffer = UncompressedBuffer + UncompressedBufferSize;
  276. PUCHAR EndOfCompressedBuffer = CompressedBuffer + CompressedBufferSize;
  277. //
  278. // Only supports HIBER ENGINE
  279. //
  280. if (Engine != COMPRESSION_ENGINE_HIBER) {
  281. return STATUS_NOT_SUPPORTED;
  282. }
  283. //
  284. // For each uncompressed chunk (even the odd sized ending buffer) we will
  285. // try and compress the chunk
  286. //
  287. for (UncompressedChunk = UncompressedBuffer, CompressedChunk = CompressedBuffer;
  288. UncompressedChunk < EndOfUncompressedBuffer;
  289. UncompressedChunk += MAX_UNCOMPRESSED_CHUNK_SIZE, CompressedChunk += CompressedChunkSize) {
  290. ASSERT(EndOfUncompressedBuffer >= UncompressedChunk);
  291. ASSERT(EndOfCompressedBuffer >= CompressedChunk);
  292. //
  293. // Call the appropriate engine to compress one chunk. and
  294. // return an error if we got one.
  295. //
  296. if (!NT_SUCCESS(Status = LZNT1CompressChunkHiber( UncompressedChunk,
  297. EndOfUncompressedBuffer,
  298. CompressedChunk,
  299. EndOfCompressedBuffer,
  300. &CompressedChunkSize,
  301. WorkSpace ))) {
  302. return Status;
  303. }
  304. //
  305. // See if we stay all zeros. If not then all zeros will become
  306. // false and stay that way no matter what we later compress
  307. //
  308. AllZero = AllZero && (Status == STATUS_BUFFER_ALL_ZEROS);
  309. }
  310. //
  311. // If we are not within two bytes of the end of the compressed buffer then we
  312. // need to zero out two more for the ending compressed header and update
  313. // the compressed chunk pointer value. Don't include these bytes in
  314. // the count however, as that may force our caller to allocate an unneeded
  315. // cluster, since on decompress we will terminate either on these two
  316. // bytes of 0, or byte count.
  317. //
  318. if (CompressedChunk <= (EndOfCompressedBuffer - 2)) {
  319. *(CompressedChunk) = 0;
  320. *(CompressedChunk + 1) = 0;
  321. }
  322. //
  323. // The final compressed size is the difference between the start of the
  324. // compressed buffer and where the compressed chunk pointer was left
  325. //
  326. *FinalCompressedSize = (ULONG)(CompressedChunk - CompressedBuffer);
  327. //
  328. // Check if the input buffer was all zeros and return the alternate status
  329. // if appropriate
  330. //
  331. if (AllZero) { return STATUS_BUFFER_ALL_ZEROS; }
  332. return STATUS_SUCCESS;
  333. }
  334. NTSTATUS
  335. RtlCompressBufferLZNT1 (
  336. IN USHORT Engine,
  337. IN PUCHAR UncompressedBuffer,
  338. IN ULONG UncompressedBufferSize,
  339. OUT PUCHAR CompressedBuffer,
  340. IN ULONG CompressedBufferSize,
  341. IN ULONG UncompressedChunkSize,
  342. OUT PULONG FinalCompressedSize,
  343. IN PVOID WorkSpace
  344. )
  345. /*++
  346. Routine Description:
  347. This routine takes as input an uncompressed buffer and produces
  348. its compressed equivalent provided the compressed data fits within
  349. the specified destination buffer.
  350. An output variable indicates the number of bytes used to store
  351. the compressed buffer.
  352. Arguments:
  353. UncompressedBuffer - Supplies a pointer to the uncompressed data.
  354. UncompressedBufferSize - Supplies the size, in bytes, of the
  355. uncompressed buffer.
  356. CompressedBuffer - Supplies a pointer to where the compressed data
  357. is to be stored.
  358. CompressedBufferSize - Supplies the size, in bytes, of the
  359. compressed buffer.
  360. UncompressedChunkSize - Ignored.
  361. FinalCompressedSize - Receives the number of bytes needed in
  362. the compressed buffer to store the compressed data.
  363. WorkSpace - Mind your own business, just give it to me.
  364. Return Value:
  365. STATUS_SUCCESS - the compression worked without a hitch.
  366. STATUS_BUFFER_ALL_ZEROS - the compression worked without a hitch and in
  367. addition the input buffer was all zeros.
  368. STATUS_BUFFER_TOO_SMALL - the compressed buffer is too small to hold the
  369. compressed data.
  370. --*/
  371. {
  372. NTSTATUS Status;
  373. PLZNT1_MATCH_FUNCTION MatchFunction;
  374. PUCHAR UncompressedChunk;
  375. PUCHAR CompressedChunk;
  376. LONG CompressedChunkSize;
  377. //
  378. // The following variable is used to tell if we have processed an entire
  379. // buffer of zeros and that we should return an alternate status value
  380. //
  381. BOOLEAN AllZero = TRUE;
  382. //
  383. // The following variables are pointers to the byte following the
  384. // end of each appropriate buffer.
  385. //
  386. PUCHAR EndOfUncompressedBuffer = UncompressedBuffer + UncompressedBufferSize;
  387. PUCHAR EndOfCompressedBuffer = CompressedBuffer + CompressedBufferSize;
  388. //
  389. // Get the match function we are to be using
  390. //
  391. if (Engine == COMPRESSION_ENGINE_STANDARD) {
  392. MatchFunction = LZNT1FindMatchStandard;
  393. } else if (Engine == COMPRESSION_ENGINE_MAXIMUM) {
  394. MatchFunction = LZNT1FindMatchMaximum;
  395. } else {
  396. return STATUS_NOT_SUPPORTED;
  397. }
  398. //
  399. // For each uncompressed chunk (even the odd sized ending buffer) we will
  400. // try and compress the chunk
  401. //
  402. for (UncompressedChunk = UncompressedBuffer, CompressedChunk = CompressedBuffer;
  403. UncompressedChunk < EndOfUncompressedBuffer;
  404. UncompressedChunk += MAX_UNCOMPRESSED_CHUNK_SIZE, CompressedChunk += CompressedChunkSize) {
  405. ASSERT(EndOfUncompressedBuffer >= UncompressedChunk);
  406. ASSERT(EndOfCompressedBuffer >= CompressedChunk);
  407. //
  408. // Call the appropriate engine to compress one chunk. and
  409. // return an error if we got one.
  410. //
  411. if (!NT_SUCCESS(Status = LZNT1CompressChunk( MatchFunction,
  412. UncompressedChunk,
  413. EndOfUncompressedBuffer,
  414. CompressedChunk,
  415. EndOfCompressedBuffer,
  416. &CompressedChunkSize,
  417. WorkSpace ))) {
  418. return Status;
  419. }
  420. //
  421. // See if we stay all zeros. If not then all zeros will become
  422. // false and stay that way no matter what we later compress
  423. //
  424. AllZero = AllZero && (Status == STATUS_BUFFER_ALL_ZEROS);
  425. }
  426. //
  427. // If we are not within two bytes of the end of the compressed buffer then we
  428. // need to zero out two more for the ending compressed header and update
  429. // the compressed chunk pointer value. Don't include these bytes in
  430. // the count however, as that may force our caller to allocate an unneeded
  431. // cluster, since on decompress we will terminate either on these two
  432. // bytes of 0, or byte count.
  433. //
  434. if (CompressedChunk <= (EndOfCompressedBuffer - 2)) {
  435. *(CompressedChunk) = 0;
  436. *(CompressedChunk + 1) = 0;
  437. }
  438. //
  439. // The final compressed size is the difference between the start of the
  440. // compressed buffer and where the compressed chunk pointer was left
  441. //
  442. *FinalCompressedSize = (ULONG)(CompressedChunk - CompressedBuffer);
  443. //
  444. // Check if the input buffer was all zeros and return the alternate status
  445. // if appropriate
  446. //
  447. if (AllZero) { return STATUS_BUFFER_ALL_ZEROS; }
  448. return STATUS_SUCCESS;
  449. }
  450. NTSTATUS
  451. RtlDecompressBufferLZNT1 (
  452. OUT PUCHAR UncompressedBuffer,
  453. IN ULONG UncompressedBufferSize,
  454. IN PUCHAR CompressedBuffer,
  455. IN ULONG CompressedBufferSize,
  456. OUT PULONG FinalUncompressedSize
  457. )
  458. /*++
  459. Routine Description:
  460. This routine takes as input a compressed buffer and produces
  461. its uncompressed equivalent provided the uncompressed data fits
  462. within the specified destination buffer.
  463. An output variable indicates the number of bytes used to store the
  464. uncompressed data.
  465. Arguments:
  466. UncompressedBuffer - Supplies a pointer to where the uncompressed
  467. data is to be stored.
  468. UncompressedBufferSize - Supplies the size, in bytes, of the
  469. uncompressed buffer.
  470. CompressedBuffer - Supplies a pointer to the compressed data.
  471. CompressedBufferSize - Supplies the size, in bytes, of the
  472. compressed buffer.
  473. FinalUncompressedSize - Receives the number of bytes needed in
  474. the uncompressed buffer to store the uncompressed data.
  475. Return Value:
  476. STATUS_SUCCESS - the decompression worked without a hitch.
  477. STATUS_BAD_COMPRESSION_BUFFER - the input compressed buffer is
  478. ill-formed.
  479. --*/
  480. {
  481. NTSTATUS Status;
  482. PUCHAR CompressedChunk = CompressedBuffer;
  483. PUCHAR UncompressedChunk = UncompressedBuffer;
  484. COMPRESSED_CHUNK_HEADER ChunkHeader;
  485. LONG SavedChunkSize;
  486. LONG UncompressedChunkSize;
  487. LONG CompressedChunkSize;
  488. //
  489. // The following to variables are pointers to the byte following the
  490. // end of each appropriate buffer. This saves us from doing the addition
  491. // for each loop check
  492. //
  493. PUCHAR EndOfUncompressedBuffer = UncompressedBuffer + UncompressedBufferSize;
  494. PUCHAR EndOfCompressedBuffer = CompressedBuffer + CompressedBufferSize;
  495. //
  496. // Make sure that the compressed buffer is at least four bytes long to
  497. // start with, and then get the first chunk header and make sure it
  498. // is not an ending chunk header.
  499. //
  500. ASSERT(CompressedChunk <= EndOfCompressedBuffer - 4);
  501. RtlRetrieveUshort( &ChunkHeader, CompressedChunk );
  502. ASSERT( (ChunkHeader.Short != 0) || !Lznt1Break );
  503. //
  504. // Now while there is space in the uncompressed buffer to store data
  505. // we will loop through decompressing chunks
  506. //
  507. while (TRUE) {
  508. ASSERT( (ChunkHeader.Chunk.ChunkSignature == 3) || !Lznt1Break );
  509. CompressedChunkSize = GetCompressedChunkSize(ChunkHeader);
  510. //
  511. // Check that the chunk actually fits in the buffer supplied
  512. // by the caller
  513. //
  514. if (CompressedChunk + CompressedChunkSize > EndOfCompressedBuffer) {
  515. ASSERTMSG("CompressedBuffer is too small", !Lznt1Break);
  516. *FinalUncompressedSize = PtrToUlong(CompressedChunk);
  517. return STATUS_BAD_COMPRESSION_BUFFER;
  518. }
  519. //
  520. // First make sure the chunk contains compressed data
  521. //
  522. if (ChunkHeader.Chunk.IsChunkCompressed) {
  523. //
  524. // Decompress a chunk and return if we get an error
  525. //
  526. if (!NT_SUCCESS(Status = LZNT1DecompressChunk( UncompressedChunk,
  527. EndOfUncompressedBuffer,
  528. CompressedChunk + sizeof(COMPRESSED_CHUNK_HEADER),
  529. CompressedChunk + CompressedChunkSize,
  530. &UncompressedChunkSize ))) {
  531. *FinalUncompressedSize = UncompressedChunkSize;
  532. return Status;
  533. }
  534. } else {
  535. //
  536. // The chunk does not contain compressed data so we need to simply
  537. // copy over the uncompressed data
  538. //
  539. UncompressedChunkSize = GetUncompressedChunkSize( ChunkHeader );
  540. //
  541. // Make sure the data will fit into the output buffer
  542. //
  543. if (UncompressedChunk + UncompressedChunkSize > EndOfUncompressedBuffer) {
  544. UncompressedChunkSize = (ULONG)(EndOfUncompressedBuffer - UncompressedChunk);
  545. }
  546. //
  547. // Check that the compressed chunk has this many bytes to copy.
  548. //
  549. if (CompressedChunk + sizeof(COMPRESSED_CHUNK_HEADER) + UncompressedChunkSize > EndOfCompressedBuffer) {
  550. ASSERTMSG("CompressedBuffer is too small", !Lznt1Break);
  551. *FinalUncompressedSize = PtrToUlong(CompressedChunk);
  552. return STATUS_BAD_COMPRESSION_BUFFER;
  553. }
  554. RtlCopyMemory( UncompressedChunk,
  555. CompressedChunk + sizeof(COMPRESSED_CHUNK_HEADER),
  556. UncompressedChunkSize );
  557. }
  558. //
  559. // Now update the compressed and uncompressed chunk pointers with
  560. // the size of the compressed chunk and the number of bytes we
  561. // decompressed into, and then make sure we didn't exceed our buffers
  562. //
  563. CompressedChunk += CompressedChunkSize;
  564. UncompressedChunk += UncompressedChunkSize;
  565. ASSERT( CompressedChunk <= EndOfCompressedBuffer );
  566. ASSERT( UncompressedChunk <= EndOfUncompressedBuffer );
  567. //
  568. // Now if the uncompressed is full then we are done
  569. //
  570. if (UncompressedChunk == EndOfUncompressedBuffer) { break; }
  571. //
  572. // Otherwise we need to get the next chunk header. We first
  573. // check if there is one, save the old chunk size for the
  574. // chunk we just read in, get the new chunk, and then check
  575. // if it is the ending chunk header
  576. //
  577. if (CompressedChunk > EndOfCompressedBuffer - 2) { break; }
  578. SavedChunkSize = GetUncompressedChunkSize(ChunkHeader);
  579. RtlRetrieveUshort( &ChunkHeader, CompressedChunk );
  580. if (ChunkHeader.Short == 0) { break; }
  581. //
  582. // At this point we are not at the end of the uncompressed buffer
  583. // and we have another chunk to process. But before we go on we
  584. // need to see if the last uncompressed chunk didn't fill the full
  585. // uncompressed chunk size.
  586. //
  587. if (UncompressedChunkSize < SavedChunkSize) {
  588. LONG t1;
  589. PUCHAR t2;
  590. //
  591. // Now we only need to zero out data if the really are going
  592. // to process another chunk, to test for that we check if
  593. // the zero will go beyond the end of the uncompressed buffer
  594. //
  595. if ((t2 = (UncompressedChunk +
  596. (t1 = (SavedChunkSize -
  597. UncompressedChunkSize)))) >= EndOfUncompressedBuffer) {
  598. break;
  599. }
  600. RtlZeroMemory( UncompressedChunk, t1);
  601. UncompressedChunk = t2;
  602. }
  603. }
  604. //
  605. // If we got out of the loop with the compressed chunk pointer beyond the
  606. // end of compressed buffer then the compression buffer is ill formed.
  607. //
  608. if (CompressedChunk > EndOfCompressedBuffer) {
  609. *FinalUncompressedSize = PtrToUlong(CompressedChunk);
  610. return STATUS_BAD_COMPRESSION_BUFFER;
  611. }
  612. //
  613. // The final uncompressed size is the difference between the start of the
  614. // uncompressed buffer and where the uncompressed chunk pointer was left
  615. //
  616. *FinalUncompressedSize = (ULONG)(UncompressedChunk - UncompressedBuffer);
  617. //
  618. // And return to our caller
  619. //
  620. return STATUS_SUCCESS;
  621. }
  622. NTSTATUS
  623. RtlDecompressFragmentLZNT1 (
  624. OUT PUCHAR UncompressedFragment,
  625. IN ULONG UncompressedFragmentSize,
  626. IN PUCHAR CompressedBuffer,
  627. IN ULONG CompressedBufferSize,
  628. IN ULONG FragmentOffset,
  629. OUT PULONG FinalUncompressedSize,
  630. IN PLZNT1_FRAGMENT_WORKSPACE WorkSpace
  631. )
  632. /*++
  633. Routine Description:
  634. This routine takes as input a compressed buffer and extract an
  635. uncompressed fragment.
  636. Output bytes are copied to the fragment buffer until either the
  637. fragment buffer is full or the end of the uncompressed buffer is
  638. reached.
  639. An output variable indicates the number of bytes used to store the
  640. uncompressed fragment.
  641. Arguments:
  642. UncompressedFragment - Supplies a pointer to where the uncompressed
  643. fragment is to be stored.
  644. UncompressedFragmentSize - Supplies the size, in bytes, of the
  645. uncompressed fragment buffer.
  646. CompressedBuffer - Supplies a pointer to the compressed data buffer.
  647. CompressedBufferSize - Supplies the size, in bytes, of the
  648. compressed buffer.
  649. FragmentOffset - Supplies the offset (zero based) where the uncompressed
  650. fragment is being extract from. The offset is the position within
  651. the original uncompressed buffer.
  652. FinalUncompressedSize - Receives the number of bytes needed in
  653. the Uncompressed fragment buffer to store the data.
  654. WorkSpace - Stop looking.
  655. Return Value:
  656. STATUS_SUCCESS - the operation worked without a hitch.
  657. STATUS_BAD_COMPRESSION_BUFFER - the input compressed buffer is
  658. ill-formed.
  659. --*/
  660. {
  661. NTSTATUS Status;
  662. PUCHAR CompressedChunk = CompressedBuffer;
  663. COMPRESSED_CHUNK_HEADER ChunkHeader;
  664. ULONG UncompressedChunkSize;
  665. ULONG CompressedChunkSize;
  666. PUCHAR EndOfUncompressedFragment = UncompressedFragment + UncompressedFragmentSize;
  667. PUCHAR EndOfCompressedBuffer = CompressedBuffer + CompressedBufferSize;
  668. PUCHAR CurrentUncompressedFragment;
  669. ULONG CopySize;
  670. ASSERT(UncompressedFragmentSize > 0);
  671. //
  672. // Get the chunk header for the first chunk in the
  673. // compressed buffer and extract the uncompressed and
  674. // the compressed chunk sizes
  675. //
  676. ASSERT(CompressedChunk <= EndOfCompressedBuffer - 2);
  677. RtlRetrieveUshort( &ChunkHeader, CompressedChunk );
  678. ASSERT( (ChunkHeader.Short != 0) || !Lznt1Break );
  679. ASSERT( (ChunkHeader.Chunk.ChunkSignature == 3) || !Lznt1Break );
  680. UncompressedChunkSize = GetUncompressedChunkSize(ChunkHeader);
  681. CompressedChunkSize = GetCompressedChunkSize(ChunkHeader);
  682. //
  683. // Now we want to skip over chunks that precede the fragment
  684. // we're after. To do that we'll loop until the fragment
  685. // offset is within the current chunk. If it is not within
  686. // the current chunk then we'll skip to the next chunk and
  687. // subtract the uncompressed chunk size from the fragment offset
  688. //
  689. while (FragmentOffset >= UncompressedChunkSize) {
  690. //
  691. // Check that the chunk actually fits in the buffer supplied
  692. // by the caller
  693. //
  694. if (CompressedChunk + CompressedChunkSize > EndOfCompressedBuffer) {
  695. ASSERTMSG("CompressedBuffer is too small", !Lznt1Break);
  696. *FinalUncompressedSize = PtrToUlong(CompressedChunk);
  697. return STATUS_BAD_COMPRESSION_BUFFER;
  698. }
  699. //
  700. // Adjust the fragment offset and move the compressed
  701. // chunk pointer to the next chunk
  702. //
  703. FragmentOffset -= UncompressedChunkSize;
  704. CompressedChunk += CompressedChunkSize;
  705. //
  706. // Get the next chunk header and if it is not in use
  707. // then the fragment that the user wants is beyond the
  708. // compressed data so we'll return a zero sized fragment
  709. //
  710. if (CompressedChunk > EndOfCompressedBuffer - 2) {
  711. *FinalUncompressedSize = 0;
  712. return STATUS_SUCCESS;
  713. }
  714. RtlRetrieveUshort( &ChunkHeader, CompressedChunk );
  715. if (ChunkHeader.Short == 0) {
  716. *FinalUncompressedSize = 0;
  717. return STATUS_SUCCESS;
  718. }
  719. ASSERT( (ChunkHeader.Chunk.ChunkSignature == 3) || !Lznt1Break );
  720. //
  721. // Decode the chunk sizes for the new current chunk
  722. //
  723. UncompressedChunkSize = GetUncompressedChunkSize(ChunkHeader);
  724. CompressedChunkSize = GetCompressedChunkSize(ChunkHeader);
  725. }
  726. //
  727. // At this point the current chunk contains the starting point
  728. // for the fragment. Now we'll loop extracting data until
  729. // we've filled up the uncompressed fragment buffer or until
  730. // we've run out of chunks. Both test are done near the end of
  731. // the loop
  732. //
  733. CurrentUncompressedFragment = UncompressedFragment;
  734. while (TRUE) {
  735. //
  736. // Check that the chunk actually fits in the buffer supplied
  737. // by the caller
  738. //
  739. if (CompressedChunk + CompressedChunkSize > EndOfCompressedBuffer) {
  740. ASSERTMSG("CompressedBuffer is too small", !Lznt1Break);
  741. *FinalUncompressedSize = PtrToUlong(CompressedChunk);
  742. return STATUS_BAD_COMPRESSION_BUFFER;
  743. }
  744. //
  745. // Now we need to compute the amount of data to copy from the
  746. // chunk. It will be based on either to the end of the chunk
  747. // size or the amount of data the user specified
  748. //
  749. CopySize = Minimum( UncompressedChunkSize - FragmentOffset, UncompressedFragmentSize );
  750. //
  751. // Now check if the chunk contains compressed data
  752. //
  753. if (ChunkHeader.Chunk.IsChunkCompressed) {
  754. //
  755. // The chunk is compressed but now check if the amount
  756. // we need to get is the entire chunk and if so then
  757. // we can do the decompress straight into the caller's
  758. // buffer
  759. //
  760. if ((FragmentOffset == 0) && (CopySize == UncompressedChunkSize)) {
  761. if (!NT_SUCCESS(Status = LZNT1DecompressChunk( CurrentUncompressedFragment,
  762. EndOfUncompressedFragment,
  763. CompressedChunk + sizeof(COMPRESSED_CHUNK_HEADER),
  764. CompressedChunk + CompressedChunkSize,
  765. &CopySize ))) {
  766. *FinalUncompressedSize = CopySize;
  767. return Status;
  768. }
  769. } else {
  770. //
  771. // The caller wants only a portion of this compressed chunk
  772. // so we need to read it into our work buffer and then copy
  773. // the parts from the work buffer into the caller's buffer
  774. //
  775. if (!NT_SUCCESS(Status = LZNT1DecompressChunk( (PUCHAR)WorkSpace,
  776. &WorkSpace->Buffer[0] + sizeof(LZNT1_FRAGMENT_WORKSPACE),
  777. CompressedChunk + sizeof(COMPRESSED_CHUNK_HEADER),
  778. CompressedChunk + CompressedChunkSize,
  779. &UncompressedChunkSize ))) {
  780. *FinalUncompressedSize = UncompressedChunkSize;
  781. return Status;
  782. }
  783. //
  784. // If we got less than we were looking for then we are at the
  785. // end of the file. Remember the real uncompressed size and
  786. // break out of the loop.
  787. //
  788. if ((UncompressedChunkSize - FragmentOffset) < CopySize) {
  789. RtlCopyMemory( CurrentUncompressedFragment,
  790. &WorkSpace->Buffer[ FragmentOffset ],
  791. (UncompressedChunkSize - FragmentOffset) );
  792. CurrentUncompressedFragment += (UncompressedChunkSize - FragmentOffset);
  793. break;
  794. }
  795. RtlCopyMemory( CurrentUncompressedFragment,
  796. &WorkSpace->Buffer[ FragmentOffset ],
  797. CopySize );
  798. }
  799. } else {
  800. //
  801. // The chunk is not compressed so we can do a simple copy of the
  802. // data. First verify that the compressed buffer holds this much
  803. // data.
  804. //
  805. if (CompressedChunk + sizeof(COMPRESSED_CHUNK_HEADER) + FragmentOffset + CopySize > EndOfCompressedBuffer) {
  806. ASSERTMSG("CompressedBuffer is too small", !Lznt1Break);
  807. *FinalUncompressedSize = PtrToUlong(CompressedChunk);
  808. return STATUS_BAD_COMPRESSION_BUFFER;
  809. }
  810. RtlCopyMemory( CurrentUncompressedFragment,
  811. CompressedChunk + sizeof(COMPRESSED_CHUNK_HEADER) + FragmentOffset,
  812. CopySize );
  813. }
  814. //
  815. // Now that we've done at least one copy make sure the fragment
  816. // offset is set to zero so the next time through the loop will
  817. // start at the right offset
  818. //
  819. FragmentOffset = 0;
  820. //
  821. // Adjust the uncompressed fragment information by moving the
  822. // pointer up by the copy size and subtracting copy size from
  823. // the amount of data the user wants
  824. //
  825. CurrentUncompressedFragment += CopySize;
  826. UncompressedFragmentSize -= CopySize;
  827. //
  828. // Now if the uncompressed fragment size is zero then we're
  829. // done
  830. //
  831. if (UncompressedFragmentSize == 0) { break; }
  832. //
  833. // Otherwise the user wants more data so we'll move to the
  834. // next chunk, and then check if the chunk is is use. If
  835. // it is not in use then we the user is trying to read beyond
  836. // the end of compressed data so we'll break out of the loop
  837. //
  838. CompressedChunk += CompressedChunkSize;
  839. if (CompressedChunk > EndOfCompressedBuffer - 2) { break; }
  840. RtlRetrieveUshort( &ChunkHeader, CompressedChunk );
  841. if (ChunkHeader.Short == 0) { break; }
  842. ASSERT( (ChunkHeader.Chunk.ChunkSignature == 3) || !Lznt1Break );
  843. //
  844. // Decode the chunk sizes for the new current chunk
  845. //
  846. UncompressedChunkSize = GetUncompressedChunkSize(ChunkHeader);
  847. CompressedChunkSize = GetCompressedChunkSize(ChunkHeader);
  848. }
  849. //
  850. // Now either we finished filling up the caller's buffer (and
  851. // uncompressed fragment size is zero) or we've exhausted the
  852. // compresed buffer (and chunk header is zero). In either case
  853. // we're done and we can now compute the size of the fragment
  854. // that we're returning to the caller it is simply the difference
  855. // between the start of the buffer and the current position
  856. //
  857. *FinalUncompressedSize = (ULONG)(CurrentUncompressedFragment - UncompressedFragment);
  858. return STATUS_SUCCESS;
  859. }
  860. NTSTATUS
  861. RtlDescribeChunkLZNT1 (
  862. IN OUT PUCHAR *CompressedBuffer,
  863. IN PUCHAR EndOfCompressedBufferPlus1,
  864. OUT PUCHAR *ChunkBuffer,
  865. OUT PULONG ChunkSize
  866. )
  867. /*++
  868. Routine Description:
  869. This routine takes as input a compressed buffer, and returns
  870. a description of the current chunk in that buffer, updating
  871. the CompressedBuffer pointer to point to the next chunk (if
  872. there is one).
  873. Arguments:
  874. CompressedBuffer - Supplies a pointer to the current chunk in
  875. the compressed data, and returns pointing to the next chunk
  876. EndOfCompressedBufferPlus1 - Points at first byte beyond
  877. compressed buffer
  878. ChunkBuffer - Receives a pointer to the chunk, if ChunkSize
  879. is nonzero, else undefined
  880. ChunkSize - Receives the size of the current chunk pointed
  881. to by CompressedBuffer. Returns 0 if STATUS_NO_MORE_ENTRIES.
  882. Return Value:
  883. STATUS_SUCCESS - the chunk size is being returned
  884. STATUS_BAD_COMPRESSION_BUFFER - the input compressed buffer is
  885. ill-formed.
  886. STATUS_NO_MORE_ENTRIES - There is no chunk at the current pointer.
  887. --*/
  888. {
  889. COMPRESSED_CHUNK_HEADER ChunkHeader;
  890. NTSTATUS Status = STATUS_NO_MORE_ENTRIES;
  891. //
  892. // First initialize outputs
  893. //
  894. *ChunkBuffer = *CompressedBuffer;
  895. *ChunkSize = 0;
  896. //
  897. // Make sure that the compressed buffer is at least four bytes long to
  898. // start with, otherwise just return a zero chunk.
  899. //
  900. if (*CompressedBuffer <= EndOfCompressedBufferPlus1 - 4) {
  901. RtlRetrieveUshort( &ChunkHeader, *CompressedBuffer );
  902. //
  903. // Check for end of chunks, terminated by USHORT of 0.
  904. // First assume there are no more.
  905. //
  906. if (ChunkHeader.Short != 0) {
  907. Status = STATUS_SUCCESS;
  908. *ChunkSize = GetCompressedChunkSize(ChunkHeader);
  909. *CompressedBuffer += *ChunkSize;
  910. //
  911. // Check that the chunk actually fits in the buffer supplied
  912. // by the caller. If not, restore *CompressedBuffer for debug!
  913. //
  914. if ((*CompressedBuffer > EndOfCompressedBufferPlus1) ||
  915. (ChunkHeader.Chunk.ChunkSignature != 3)) {
  916. ASSERTMSG("CompressedBuffer is bad or too small", !Lznt1Break);
  917. *CompressedBuffer -= *ChunkSize;
  918. Status = STATUS_BAD_COMPRESSION_BUFFER;
  919. //
  920. // First make sure the chunk contains compressed data
  921. //
  922. } else if (!ChunkHeader.Chunk.IsChunkCompressed) {
  923. //
  924. // The uncompressed chunk must be exactly this size!
  925. // If not, restore *CompressedBuffer for debug!
  926. //
  927. if (*ChunkSize != MAX_UNCOMPRESSED_CHUNK_SIZE + 2) {
  928. ASSERTMSG("Uncompressed chunk is wrong size", !Lznt1Break);
  929. *CompressedBuffer -= *ChunkSize;
  930. Status = STATUS_BAD_COMPRESSION_BUFFER;
  931. //
  932. // The chunk does not contain compressed data so we need to
  933. // remove the chunk header from the chunk description.
  934. //
  935. } else {
  936. *ChunkBuffer += 2;
  937. *ChunkSize -= 2;
  938. }
  939. //
  940. // Otherwise we have a compressed chunk, and we only need to
  941. // see if it is all zeros! Since the header is already interpreted,
  942. // we only have to see if there is exactly one literal and if it
  943. // is zero - it doesn't matter what the copy token says - we have
  944. // a chunk of zeros!
  945. //
  946. } else if ((*ChunkSize == 6) && (*(*ChunkBuffer + 2) == 2) && (*(*ChunkBuffer + 3) == 0)) {
  947. *ChunkSize = 0;
  948. }
  949. }
  950. }
  951. return Status;
  952. }
  953. NTSTATUS
  954. RtlReserveChunkLZNT1 (
  955. IN OUT PUCHAR *CompressedBuffer,
  956. IN PUCHAR EndOfCompressedBufferPlus1,
  957. OUT PUCHAR *ChunkBuffer,
  958. IN ULONG ChunkSize
  959. )
  960. /*++
  961. Routine Description:
  962. This routine reserves space for a chunk of the specified
  963. size in the buffer, writing in a chunk header if necessary
  964. (uncompressed or all zeros case). On return the CompressedBuffer
  965. pointer points to the next chunk.
  966. Arguments:
  967. CompressedBuffer - Supplies a pointer to the current chunk in
  968. the compressed data, and returns pointing to the next chunk
  969. EndOfCompressedBufferPlus1 - Points at first byte beyond
  970. compressed buffer
  971. ChunkBuffer - Receives a pointer to the chunk, if ChunkSize
  972. is nonzero, else undefined
  973. ChunkSize - Supplies the compressed size of the chunk to be received.
  974. Two special values are 0 and MAX_UNCOMPRESSED_CHUNK_SIZE (4096).
  975. 0 means the chunk should be filled with a pattern that equates
  976. to 4096 0's. 4096 implies that the compression routine should
  977. prepare to receive all of the data in uncompressed form.
  978. Return Value:
  979. STATUS_SUCCESS - the chunk size is being returned
  980. STATUS_BUFFER_TOO_SMALL - the compressed buffer is too small to hold the
  981. compressed data.
  982. --*/
  983. {
  984. COMPRESSED_CHUNK_HEADER ChunkHeader;
  985. BOOLEAN Compressed;
  986. PUCHAR Tail, NextChunk, DontCare;
  987. ULONG Size;
  988. NTSTATUS Status;
  989. ASSERT(ChunkSize <= MAX_UNCOMPRESSED_CHUNK_SIZE);
  990. //
  991. // Calculate the address of the tail of this buffer and its
  992. // size, so it can be moved before we store anything.
  993. //
  994. Tail = NextChunk = *CompressedBuffer;
  995. while (NT_SUCCESS(Status = RtlDescribeChunkLZNT1( &NextChunk,
  996. EndOfCompressedBufferPlus1,
  997. &DontCare,
  998. &Size))) {
  999. //
  1000. // First time through the loop, capture the address of the next chunk.
  1001. //
  1002. if (Tail == *CompressedBuffer) {
  1003. Tail = NextChunk;
  1004. }
  1005. }
  1006. //
  1007. // The buffer could be invalid.
  1008. //
  1009. if (Status == STATUS_NO_MORE_ENTRIES) {
  1010. //
  1011. // The only way to successfully terminate the loop is by finding a USHORT
  1012. // terminator of 0. Now calculate the size including the final USHORT
  1013. // we stopped on.
  1014. //
  1015. Size = (ULONG) (NextChunk - Tail + sizeof(USHORT));
  1016. //
  1017. // First initialize outputs
  1018. //
  1019. Status = STATUS_BUFFER_TOO_SMALL;
  1020. *ChunkBuffer = *CompressedBuffer;
  1021. //
  1022. // Make sure that the compressed buffer is at least four bytes long to
  1023. // start with, otherwise just return a zero chunk.
  1024. //
  1025. if (*CompressedBuffer <= (EndOfCompressedBufferPlus1 - ChunkSize)) {
  1026. //
  1027. // If the chunk is uncompressed, then we have to adjust the
  1028. // chunk description for the header.
  1029. //
  1030. if (ChunkSize == MAX_UNCOMPRESSED_CHUNK_SIZE) {
  1031. //
  1032. // Increase ChunkSize to include header.
  1033. //
  1034. ChunkSize += 2;
  1035. //
  1036. // Move the tail now that we know where to put it.
  1037. //
  1038. if ((*CompressedBuffer + ChunkSize + Size) <= EndOfCompressedBufferPlus1) {
  1039. RtlMoveMemory( *CompressedBuffer + ChunkSize, Tail, Size );
  1040. //
  1041. // Build the header and store it for an uncompressed chunk.
  1042. //
  1043. SetCompressedChunkHeader( ChunkHeader,
  1044. MAX_UNCOMPRESSED_CHUNK_SIZE + 2,
  1045. FALSE );
  1046. RtlStoreUshort( (*CompressedBuffer), ChunkHeader.Short );
  1047. //
  1048. // Advance to where the uncompressed data goes.
  1049. //
  1050. *ChunkBuffer += 2;
  1051. Status = STATUS_SUCCESS;
  1052. }
  1053. //
  1054. // Otherwise, if this is a zero chunk we have to build it.
  1055. //
  1056. } else if (ChunkSize == 0) {
  1057. //
  1058. // It takes 6 bytes to describe a chunk of zeros.
  1059. //
  1060. ChunkSize = 6;
  1061. if ((*CompressedBuffer + ChunkSize + Size) <= EndOfCompressedBufferPlus1) {
  1062. //
  1063. // Move the tail now that we know where to put it.
  1064. //
  1065. RtlMoveMemory( *CompressedBuffer + ChunkSize, Tail, Size );
  1066. //
  1067. // Build the header and store it
  1068. //
  1069. SetCompressedChunkHeader( ChunkHeader,
  1070. 6,
  1071. TRUE );
  1072. RtlStoreUshort( (*CompressedBuffer), ChunkHeader.Short );
  1073. //
  1074. // Now store the mask byte with one literal and the literal
  1075. // is 0.
  1076. //
  1077. RtlStoreUshort( (*CompressedBuffer + 2), (USHORT)2 );
  1078. //
  1079. // Now store the copy token for copying 4095 bytes from
  1080. // the preceding byte (stored as offset 0).
  1081. //
  1082. RtlStoreUshort( (*CompressedBuffer + 4), (USHORT)(4095-3));
  1083. Status = STATUS_SUCCESS;
  1084. }
  1085. //
  1086. // Otherwise we have a normal compressed chunk.
  1087. //
  1088. } else {
  1089. //
  1090. // Move the tail now that we know where to put it.
  1091. //
  1092. if ((*CompressedBuffer + ChunkSize + Size) <= EndOfCompressedBufferPlus1) {
  1093. RtlMoveMemory( *CompressedBuffer + ChunkSize, Tail, Size );
  1094. Status = STATUS_SUCCESS;
  1095. }
  1096. }
  1097. //
  1098. // Advance the *CompressedBuffer before return
  1099. //
  1100. *CompressedBuffer += ChunkSize;
  1101. }
  1102. }
  1103. return Status;
  1104. }
  1105. #if defined(ALLOC_DATA_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
  1106. #pragma const_seg("PAGELKCONST")
  1107. #endif
  1108. //
  1109. // The Copy token is two bytes in size.
  1110. // Our definition uses a union to make it easier to set and retrieve token values.
  1111. //
  1112. // Copy Token
  1113. //
  1114. // Length Displacement
  1115. //
  1116. // 12 bits 3 to 4098 4 bits 1 to 16
  1117. // 11 bits 3 to 2050 5 bits 1 to 32
  1118. // 10 bits 3 to 1026 6 bits 1 to 64
  1119. // 9 bits 3 to 514 7 bits 1 to 128
  1120. // 8 bits 3 to 258 8 bits 1 to 256
  1121. // 7 bits 3 to 130 9 bits 1 to 512
  1122. // 6 bits 3 to 66 10 bits 1 to 1024
  1123. // 5 bits 3 to 34 11 bits 1 to 2048
  1124. // 4 bits 3 to 18 12 bits 1 to 4096
  1125. //
  1126. typedef struct _LZNT1_FORMAT {
  1127. ULONG LengthMask;
  1128. ULONG DisplacementMask;
  1129. ULONG MaxLength;
  1130. ULONG MaxDisplacement;
  1131. UCHAR DisplacementShift;
  1132. } LZNT1_FORMAT;
  1133. typedef const LZNT1_FORMAT *PLZNT1_FORMAT;
  1134. #define FORMAT_RECORD(n) \
  1135. { \
  1136. (1 << (n)) - 1, \
  1137. (1 << (16-(n))) - 1, \
  1138. (1 << (n)) + 2, \
  1139. (1 << (16-(n))), \
  1140. (n) \
  1141. }
  1142. const LZNT1_FORMAT LZNT1Formats[] = {
  1143. FORMAT_RECORD(12),
  1144. FORMAT_RECORD(11),
  1145. FORMAT_RECORD(10),
  1146. FORMAT_RECORD(9),
  1147. FORMAT_RECORD(8),
  1148. FORMAT_RECORD(7),
  1149. FORMAT_RECORD(6),
  1150. FORMAT_RECORD(5),
  1151. FORMAT_RECORD(4)
  1152. };
  1153. #undef FORMAT_RECORD
  1154. #define FORMAT412 (&LZNT1Formats[0])
  1155. #define FORMAT511 (&LZNT1Formats[1])
  1156. #define FORMAT610 (&LZNT1Formats[2])
  1157. #define FORMAT79 (&LZNT1Formats[3])
  1158. #define FORMAT88 (&LZNT1Formats[4])
  1159. #define FORMAT97 (&LZNT1Formats[5])
  1160. #define FORMAT106 (&LZNT1Formats[6])
  1161. #define FORMAT115 (&LZNT1Formats[7])
  1162. #define FORMAT124 (&LZNT1Formats[8])
  1163. #pragma optimize("t", on)
  1164. //
  1165. // USHORT
  1166. // GetLZNT1Length (
  1167. // IN COPY_TOKEN_FORMAT Format,
  1168. // IN LZNT1_COPY_TOKEN CopyToken
  1169. // );
  1170. //
  1171. // USHORT
  1172. // GetLZNT1Displacement (
  1173. // IN COPY_TOKEN_FORMAT Format,
  1174. // IN LZNT1_COPY_TOKEN CopyToken
  1175. // );
  1176. //
  1177. // VOID
  1178. // SetLZNT1 (
  1179. // IN COPY_TOKEN_FORMAT Format,
  1180. // IN LZNT1_COPY_TOKEN CopyToken,
  1181. // IN USHORT Length,
  1182. // IN USHORT Displacement
  1183. // );
  1184. //
  1185. USHORT
  1186. __forceinline
  1187. GetLZNT1Length (
  1188. IN PLZNT1_FORMAT Format,
  1189. IN USHORT CopyRecord
  1190. )
  1191. {
  1192. ULONG length;
  1193. length = CopyRecord & Format->LengthMask;
  1194. return (USHORT)(length + 3);
  1195. }
  1196. USHORT
  1197. __forceinline
  1198. GetLZNT1Displacement (
  1199. IN PLZNT1_FORMAT Format,
  1200. IN USHORT CopyRecord
  1201. )
  1202. {
  1203. ULONG displacement;
  1204. displacement = CopyRecord >> Format->DisplacementShift;
  1205. displacement &= Format->DisplacementMask;
  1206. return (USHORT)(displacement + 1);
  1207. }
  1208. USHORT
  1209. __forceinline
  1210. SetLZNT1 (
  1211. IN PLZNT1_FORMAT Format,
  1212. IN ULONG Length,
  1213. IN ULONG Displacement
  1214. )
  1215. {
  1216. ULONG result;
  1217. ASSERT(Length <= Format->MaxLength);
  1218. ASSERT(Displacement <= Format->MaxDisplacement);
  1219. result = ((Displacement - 1) << Format->DisplacementShift) | (Length - 3);
  1220. return (USHORT)result;
  1221. }
  1222. //
  1223. // Local support routine
  1224. //
  1225. NTSTATUS
  1226. LZNT1CompressChunkHiber (
  1227. IN PUCHAR UncompressedBuffer,
  1228. IN PUCHAR EndOfUncompressedBufferPlus1,
  1229. OUT PUCHAR CompressedBuffer,
  1230. IN PUCHAR EndOfCompressedBufferPlus1,
  1231. OUT PULONG FinalCompressedChunkSize,
  1232. IN PLZNT1_HIBER_WORKSPACE WorkSpace
  1233. )
  1234. /*++
  1235. Routine Description:
  1236. This routine takes as input an uncompressed chunk and produces
  1237. one compressed chunk provided the compressed data fits within
  1238. the specified destination buffer.
  1239. The LZNT1 format used to store the compressed buffer.
  1240. An output variable indicates the number of bytes used to store
  1241. the compressed chunk.
  1242. Arguments:
  1243. UncompressedBuffer - Supplies a pointer to the uncompressed chunk.
  1244. EndOfUncompressedBufferPlus1 - Supplies a pointer to the next byte
  1245. following the end of the uncompressed buffer. This is supplied
  1246. instead of the size in bytes because our caller and ourselves
  1247. test against the pointer and by passing the pointer we get to
  1248. skip the code to compute it each time.
  1249. CompressedBuffer - Supplies a pointer to where the compressed chunk
  1250. is to be stored.
  1251. EndOfCompressedBufferPlus1 - Supplies a pointer to the next
  1252. byte following the end of the compressed buffer.
  1253. FinalCompressedChunkSize - Receives the number of bytes needed in
  1254. the compressed buffer to store the compressed chunk.
  1255. Return Value:
  1256. STATUS_SUCCESS - the compression worked without a hitch.
  1257. STATUS_BUFFER_ALL_ZEROS - the compression worked without a hitch and in
  1258. addition the input chunk was all zeros.
  1259. STATUS_BUFFER_TOO_SMALL - the compressed buffer is too small to hold the
  1260. compressed data.
  1261. --*/
  1262. {
  1263. ULONG IndexOrigin = WorkSpace->IndexTable[0] + 2*MAX_UNCOMPRESSED_CHUNK_SIZE;
  1264. PUCHAR EndOfCompressedChunkPlus1;
  1265. PUCHAR EndOfCompressedChunkPlus1Minus16;
  1266. PUCHAR InputPointer;
  1267. PUCHAR OutputPointer;
  1268. PUCHAR FlagPointer;
  1269. UCHAR FlagByte;
  1270. ULONG FlagBit;
  1271. ULONG Length;
  1272. UCHAR NullCharacter = 0;
  1273. PLZNT1_FORMAT Format;
  1274. ULONG MaxLength;
  1275. PUCHAR MaxInputPointer;
  1276. //
  1277. // First adjust the end of the uncompressed buffer pointer to the smaller
  1278. // of what we're passed in and the uncompressed chunk size. We use this
  1279. // to make sure we never compress more than a chunk worth at a time
  1280. //
  1281. if ((UncompressedBuffer + MAX_UNCOMPRESSED_CHUNK_SIZE) < EndOfUncompressedBufferPlus1) {
  1282. EndOfUncompressedBufferPlus1 = UncompressedBuffer + MAX_UNCOMPRESSED_CHUNK_SIZE;
  1283. }
  1284. //
  1285. // Now set the end of the compressed chunk pointer to be the smaller of the
  1286. // compressed size necessary to hold the data in an uncompressed form and
  1287. // the compressed buffer size. We use this to decide if we can't compress
  1288. // any more because the buffer is too small or just because the data
  1289. // doesn't compress very well.
  1290. //
  1291. if ((CompressedBuffer + MAX_UNCOMPRESSED_CHUNK_SIZE - 1) < EndOfCompressedBufferPlus1) {
  1292. EndOfCompressedChunkPlus1 = CompressedBuffer + MAX_UNCOMPRESSED_CHUNK_SIZE - 1;
  1293. } else {
  1294. EndOfCompressedChunkPlus1 = EndOfCompressedBufferPlus1;
  1295. }
  1296. EndOfCompressedChunkPlus1Minus16 = EndOfCompressedChunkPlus1 - 16;
  1297. //
  1298. // Now set the input and output pointers to the next byte we are
  1299. // go to process and asser that the user gave use buffers that were
  1300. // large enough to hold the minimum size chunks
  1301. //
  1302. InputPointer = UncompressedBuffer;
  1303. OutputPointer = CompressedBuffer + sizeof(COMPRESSED_CHUNK_HEADER);
  1304. ASSERT(InputPointer < EndOfUncompressedBufferPlus1);
  1305. //**** ASSERT(OutputPointer + 2 <= EndOfCompressedChunkPlus1);
  1306. //
  1307. // The flag byte stores a copy of the flags for the current
  1308. // run and the flag bit denotes the current bit position within
  1309. // the flag that we are processing. The Flag pointer denotes
  1310. // where in the compressed buffer we will store the current
  1311. // flag byte
  1312. //
  1313. FlagPointer = OutputPointer++;
  1314. FlagBit = 0;
  1315. FlagByte = 0;
  1316. //
  1317. // While there is some more data to be compressed we will do the
  1318. // following loop
  1319. //
  1320. //
  1321. // Ensure that there are at least 3 characters in the buffer
  1322. // It takes at least that many to make a match
  1323. //
  1324. Format = FORMAT412;
  1325. MaxLength = Format->MaxLength;
  1326. MaxInputPointer = UncompressedBuffer + Format->MaxDisplacement;
  1327. if (OutputPointer < EndOfCompressedChunkPlus1Minus16) {
  1328. PUCHAR EndOfUncompressedBufferPlus1Minus3 = EndOfUncompressedBufferPlus1 - 3;
  1329. while (InputPointer <= EndOfUncompressedBufferPlus1Minus3) {
  1330. UCHAR InputPointer0;
  1331. ULONG Index;
  1332. ULONG InputOffset;
  1333. ULONG MatchedOffset;
  1334. ULONG MatchedIndex;
  1335. PUCHAR MatchedString;
  1336. Index = InputPointer[0];
  1337. Index = ( (Index << 8) | (Index >> 4) );
  1338. Index = ( Index ^ InputPointer[1] ^ (InputPointer[2]<<4) ) & 0xfff;
  1339. MatchedIndex = (ULONG)(WorkSpace->IndexTable[Index]);
  1340. InputOffset = (ULONG)(InputPointer - UncompressedBuffer);
  1341. WorkSpace->IndexTable[Index] = (IndexOrigin + InputOffset);
  1342. MatchedOffset = (ULONG)(MatchedIndex - IndexOrigin);
  1343. //
  1344. // Check whether purported match lies within current buffer
  1345. // Recall that the hint vector may contain arbitrary garbage
  1346. //
  1347. if ( (MatchedOffset < InputOffset)
  1348. && ( (MatchedString = UncompressedBuffer + MatchedOffset)
  1349. , (MatchedString[0] == InputPointer[0]) ) // do at least 3 characters match?
  1350. && (MatchedString[1] == InputPointer[1])
  1351. && (MatchedString[2] == InputPointer[2]) ) {
  1352. ULONG MaxLength1;
  1353. ULONG MaxLength4;
  1354. while (MaxInputPointer < InputPointer) {
  1355. Format += 1;
  1356. MaxLength = Format->MaxLength;
  1357. MaxInputPointer = UncompressedBuffer + Format->MaxDisplacement;
  1358. }
  1359. MaxLength1 = (ULONG)(EndOfUncompressedBufferPlus1 - InputPointer);
  1360. if (MaxLength < MaxLength1) MaxLength1 = MaxLength;
  1361. MaxLength4 = MaxLength1 - (4 - 1);
  1362. Length = 3;
  1363. for (;;) {
  1364. if ((long)Length < (long)MaxLength4) {
  1365. if (InputPointer[Length] != MatchedString[Length]) break;
  1366. Length++;
  1367. if (InputPointer[Length] != MatchedString[Length]) break;
  1368. Length++;
  1369. if (InputPointer[Length] != MatchedString[Length]) break;
  1370. Length++;
  1371. if (InputPointer[Length] != MatchedString[Length]) break;
  1372. Length++;
  1373. continue;
  1374. } else {
  1375. while (Length < MaxLength1) {
  1376. if (InputPointer[Length] != MatchedString[Length]) break;
  1377. Length++;
  1378. }
  1379. break;
  1380. }
  1381. }
  1382. //
  1383. // We need to output a two byte copy token
  1384. // Ensure that there is room in the output buffer
  1385. //
  1386. ASSERT((OutputPointer+1) < EndOfCompressedChunkPlus1);
  1387. //
  1388. // Compute the displacement from the current pointer
  1389. // to the matched string
  1390. //
  1391. SetFlag(FlagByte, (1 << FlagBit));
  1392. {
  1393. ULONG Displacement = (ULONG)(InputPointer - MatchedString);
  1394. USHORT token;
  1395. token = SetLZNT1(Format,Length,Displacement);
  1396. *(PUSHORT)OutputPointer = token;
  1397. OutputPointer += sizeof(USHORT);
  1398. }
  1399. InputPointer += Length;
  1400. } else {
  1401. //
  1402. // There is more data to output now make sure the output
  1403. // buffer is not already full and can contain at least one
  1404. // more byte
  1405. //
  1406. ASSERT(OutputPointer < EndOfCompressedChunkPlus1);
  1407. ASSERT(!FlagOn(FlagByte, (1 << FlagBit)));
  1408. NullCharacter |= *(OutputPointer++) = *(InputPointer++);
  1409. }
  1410. //
  1411. // Now adjust the flag bit and check if the flag byte
  1412. // should now be output. If so output the flag byte
  1413. // and scarf up a new byte in the output buffer for the
  1414. // next flag byte. Do not advance OutputPointer if we
  1415. // have no more input anyway!
  1416. //
  1417. FlagBit = (FlagBit + 1) % 8;
  1418. if (!FlagBit) {
  1419. *FlagPointer = FlagByte;
  1420. FlagByte = 0;
  1421. FlagPointer = (OutputPointer++);
  1422. //
  1423. // Ensure that we have room for the at most 16 bytes
  1424. // that this flag byte may describe
  1425. //
  1426. if (OutputPointer >= EndOfCompressedChunkPlus1Minus16) { break; }
  1427. }
  1428. }
  1429. }
  1430. //
  1431. // UNDONE: Could pick up another match or two right at the end of the buffer
  1432. //
  1433. //
  1434. // Too few characters left for a match, emit them as literals
  1435. //
  1436. if (OutputPointer < EndOfCompressedChunkPlus1Minus16) {
  1437. while (InputPointer < EndOfUncompressedBufferPlus1) {
  1438. while (MaxInputPointer < InputPointer) {
  1439. Format += 1;
  1440. MaxLength = Format->MaxLength;
  1441. MaxInputPointer = UncompressedBuffer + Format->MaxDisplacement;
  1442. }
  1443. //
  1444. // There is more data to output now make sure the output
  1445. // buffer is not already full and can contain at least one
  1446. // more byte
  1447. //
  1448. ASSERT(OutputPointer < EndOfCompressedChunkPlus1);
  1449. ASSERT(!FlagOn(FlagByte, (1 << FlagBit)));
  1450. NullCharacter |= *(OutputPointer++) = *(InputPointer++);
  1451. //
  1452. // Now adjust the flag bit and check if the flag byte
  1453. // should now be output. If so output the flag byte
  1454. // and scarf up a new byte in the output buffer for the
  1455. // next flag byte. Do not advance OutputPointer if we
  1456. // have no more input anyway!
  1457. //
  1458. FlagBit = (FlagBit + 1) % 8;
  1459. if (!FlagBit) {
  1460. *FlagPointer = FlagByte;
  1461. FlagByte = 0;
  1462. FlagPointer = (OutputPointer++);
  1463. //
  1464. // Ensure that we have room for the at most 16 bytes
  1465. // that this flag byte may describe
  1466. //
  1467. if (OutputPointer >= EndOfCompressedChunkPlus1Minus16) { break; }
  1468. }
  1469. }
  1470. }
  1471. //
  1472. // We've exited the preceeding loop because either the input buffer is
  1473. // all compressed or because we ran out of space in the output buffer.
  1474. // Check here if the input buffer is not exhasted (i.e., we ran out
  1475. // of space)
  1476. //
  1477. if (InputPointer < EndOfUncompressedBufferPlus1) {
  1478. //
  1479. // We ran out of space, but now if the total space available
  1480. // for the compressed chunk is equal to the uncompressed data plus
  1481. // the header then we will make this an uncompressed chunk and copy
  1482. // over the uncompressed data
  1483. //
  1484. if ((CompressedBuffer + MAX_UNCOMPRESSED_CHUNK_SIZE + sizeof(COMPRESSED_CHUNK_HEADER)) <= EndOfCompressedBufferPlus1) {
  1485. COMPRESSED_CHUNK_HEADER ChunkHeader;
  1486. RtlCopyMemory( CompressedBuffer + sizeof(COMPRESSED_CHUNK_HEADER),
  1487. UncompressedBuffer,
  1488. MAX_UNCOMPRESSED_CHUNK_SIZE );
  1489. *FinalCompressedChunkSize = MAX_UNCOMPRESSED_CHUNK_SIZE + sizeof(COMPRESSED_CHUNK_HEADER);
  1490. ChunkHeader.Short = 0;
  1491. SetCompressedChunkHeader( ChunkHeader,
  1492. (USHORT)*FinalCompressedChunkSize,
  1493. FALSE );
  1494. RtlStoreUshort( CompressedBuffer, ChunkHeader.Short );
  1495. WorkSpace->IndexTable[0] = IndexOrigin;
  1496. return STATUS_SUCCESS;
  1497. }
  1498. //
  1499. // Otherwise the input buffer really is too small to store the
  1500. // compressed chuunk
  1501. //
  1502. WorkSpace->IndexTable[0] = IndexOrigin;
  1503. return STATUS_BUFFER_TOO_SMALL;
  1504. }
  1505. //
  1506. // At this point the entire input buffer has been compressed so we need
  1507. // to output the last flag byte, provided it fits in the compressed buffer,
  1508. // set and store the chunk header. Now if the Flag pointer doesn't fit
  1509. // in the output buffer that is because it is one beyond the end and
  1510. // we incremented output pointer too far so now bring output pointer
  1511. // back down.
  1512. //
  1513. if (FlagPointer < EndOfCompressedChunkPlus1) {
  1514. *FlagPointer = FlagByte;
  1515. } else {
  1516. OutputPointer--;
  1517. }
  1518. {
  1519. COMPRESSED_CHUNK_HEADER ChunkHeader;
  1520. *FinalCompressedChunkSize = (ULONG)(OutputPointer - CompressedBuffer);
  1521. ChunkHeader.Short = 0;
  1522. SetCompressedChunkHeader( ChunkHeader,
  1523. (USHORT)*FinalCompressedChunkSize,
  1524. TRUE );
  1525. RtlStoreUshort( CompressedBuffer, ChunkHeader.Short );
  1526. }
  1527. //
  1528. // Now if the only literal we ever output was a null then the
  1529. // input buffer was all zeros.
  1530. //
  1531. if (!NullCharacter) {
  1532. WorkSpace->IndexTable[0] = IndexOrigin;
  1533. return STATUS_BUFFER_ALL_ZEROS;
  1534. }
  1535. //
  1536. // Otherwise return to our caller
  1537. //
  1538. WorkSpace->IndexTable[0] = IndexOrigin;
  1539. return STATUS_SUCCESS;
  1540. }
  1541. #pragma optimize("t", off)
  1542. //
  1543. // Local support routine
  1544. //
  1545. NTSTATUS
  1546. LZNT1CompressChunk (
  1547. IN PLZNT1_MATCH_FUNCTION MatchFunction,
  1548. IN PUCHAR UncompressedBuffer,
  1549. IN PUCHAR EndOfUncompressedBufferPlus1,
  1550. OUT PUCHAR CompressedBuffer,
  1551. IN PUCHAR EndOfCompressedBufferPlus1,
  1552. OUT PULONG FinalCompressedChunkSize,
  1553. IN PVOID WorkSpace
  1554. )
  1555. /*++
  1556. Routine Description:
  1557. This routine takes as input an uncompressed chunk and produces
  1558. one compressed chunk provided the compressed data fits within
  1559. the specified destination buffer.
  1560. The LZNT1 format used to store the compressed buffer.
  1561. An output variable indicates the number of bytes used to store
  1562. the compressed chunk.
  1563. Arguments:
  1564. UncompressedBuffer - Supplies a pointer to the uncompressed chunk.
  1565. EndOfUncompressedBufferPlus1 - Supplies a pointer to the next byte
  1566. following the end of the uncompressed buffer. This is supplied
  1567. instead of the size in bytes because our caller and ourselves
  1568. test against the pointer and by passing the pointer we get to
  1569. skip the code to compute it each time.
  1570. CompressedBuffer - Supplies a pointer to where the compressed chunk
  1571. is to be stored.
  1572. EndOfCompressedBufferPlus1 - Supplies a pointer to the next
  1573. byte following the end of the compressed buffer.
  1574. FinalCompressedChunkSize - Receives the number of bytes needed in
  1575. the compressed buffer to store the compressed chunk.
  1576. Return Value:
  1577. STATUS_SUCCESS - the compression worked without a hitch.
  1578. STATUS_BUFFER_ALL_ZEROS - the compression worked without a hitch and in
  1579. addition the input chunk was all zeros.
  1580. STATUS_BUFFER_TOO_SMALL - the compressed buffer is too small to hold the
  1581. compressed data.
  1582. --*/
  1583. {
  1584. PUCHAR EndOfCompressedChunkPlus1;
  1585. PUCHAR InputPointer;
  1586. PUCHAR OutputPointer;
  1587. PUCHAR FlagPointer;
  1588. UCHAR FlagByte;
  1589. ULONG FlagBit;
  1590. LONG Length;
  1591. LONG Displacement;
  1592. USHORT CopyToken;
  1593. COMPRESSED_CHUNK_HEADER ChunkHeader;
  1594. UCHAR NullCharacter = 0;
  1595. PLZNT1_FORMAT Format = FORMAT412;
  1596. //
  1597. // First adjust the end of the uncompressed buffer pointer to the smaller
  1598. // of what we're passed in and the uncompressed chunk size. We use this
  1599. // to make sure we never compress more than a chunk worth at a time
  1600. //
  1601. if ((UncompressedBuffer + MAX_UNCOMPRESSED_CHUNK_SIZE) < EndOfUncompressedBufferPlus1) {
  1602. EndOfUncompressedBufferPlus1 = UncompressedBuffer + MAX_UNCOMPRESSED_CHUNK_SIZE;
  1603. }
  1604. //
  1605. // Now set the end of the compressed chunk pointer to be the smaller of the
  1606. // compressed size necessary to hold the data in an uncompressed form and
  1607. // the compressed buffer size. We use this to decide if we can't compress
  1608. // any more because the buffer is too small or just because the data
  1609. // doesn't compress very well.
  1610. //
  1611. if ((CompressedBuffer + MAX_UNCOMPRESSED_CHUNK_SIZE - 1) < EndOfCompressedBufferPlus1) {
  1612. EndOfCompressedChunkPlus1 = CompressedBuffer + MAX_UNCOMPRESSED_CHUNK_SIZE - 1;
  1613. } else {
  1614. EndOfCompressedChunkPlus1 = EndOfCompressedBufferPlus1;
  1615. }
  1616. //
  1617. // Now set the input and output pointers to the next byte we are
  1618. // go to process and asser that the user gave use buffers that were
  1619. // large enough to hold the minimum size chunks
  1620. //
  1621. InputPointer = UncompressedBuffer;
  1622. OutputPointer = CompressedBuffer + sizeof(COMPRESSED_CHUNK_HEADER);
  1623. ASSERT(InputPointer < EndOfUncompressedBufferPlus1);
  1624. //**** ASSERT(OutputPointer + 2 <= EndOfCompressedChunkPlus1);
  1625. //
  1626. // The flag byte stores a copy of the flags for the current
  1627. // run and the flag bit denotes the current bit position within
  1628. // the flag that we are processing. The Flag pointer denotes
  1629. // where in the compressed buffer we will store the current
  1630. // flag byte
  1631. //
  1632. FlagPointer = OutputPointer++;
  1633. FlagBit = 0;
  1634. FlagByte = 0;
  1635. ChunkHeader.Short = 0;
  1636. //
  1637. // While there is some more data to be compressed we will do the
  1638. // following loop
  1639. //
  1640. ((PLZNT1_STANDARD_WORKSPACE)WorkSpace)->UncompressedBuffer = UncompressedBuffer;
  1641. ((PLZNT1_STANDARD_WORKSPACE)WorkSpace)->EndOfUncompressedBufferPlus1 = EndOfUncompressedBufferPlus1;
  1642. ((PLZNT1_STANDARD_WORKSPACE)WorkSpace)->MaxLength = FORMAT412->MaxLength;
  1643. while (InputPointer < EndOfUncompressedBufferPlus1) {
  1644. while (UncompressedBuffer + Format->MaxDisplacement < InputPointer) {
  1645. Format += 1;
  1646. ((PLZNT1_STANDARD_WORKSPACE)WorkSpace)->MaxLength = Format->MaxLength;
  1647. }
  1648. //
  1649. // Search for a string in the Lempel
  1650. //
  1651. Length = 0;
  1652. if ((InputPointer + 3) <= EndOfUncompressedBufferPlus1) {
  1653. Length = (MatchFunction)( InputPointer, WorkSpace );
  1654. }
  1655. //
  1656. // If the return length is zero then we need to output
  1657. // a literal. We clear the flag bit to denote the literal
  1658. // output the charcter and build up a character bits
  1659. // composite that if it is still zero when we are done then
  1660. // we know the uncompressed buffer contained only zeros.
  1661. //
  1662. if (!Length) {
  1663. //
  1664. // There is more data to output now make sure the output
  1665. // buffer is not already full and can contain at least one
  1666. // more byte
  1667. //
  1668. if (OutputPointer >= EndOfCompressedChunkPlus1) { break; }
  1669. ClearFlag(FlagByte, (1 << FlagBit));
  1670. NullCharacter |= *(OutputPointer++) = *(InputPointer++);
  1671. } else {
  1672. //
  1673. // We need to output two byte, now make sure that
  1674. // the output buffer can contain at least two more
  1675. // bytes.
  1676. //
  1677. if ((OutputPointer+1) >= EndOfCompressedChunkPlus1) { break; }
  1678. //
  1679. // Compute the displacement from the current pointer
  1680. // to the matched string
  1681. //
  1682. Displacement = (ULONG)(InputPointer - ((PLZNT1_STANDARD_WORKSPACE)WorkSpace)->MatchedString);
  1683. SetFlag(FlagByte, (1 << FlagBit));
  1684. CopyToken = SetLZNT1(Format, Length, Displacement);
  1685. RtlStoreUshort (OutputPointer, CopyToken);
  1686. OutputPointer += sizeof(USHORT);
  1687. InputPointer += Length;
  1688. }
  1689. //
  1690. // Now adjust the flag bit and check if the flag byte
  1691. // should now be output. If so output the flag byte
  1692. // and scarf up a new byte in the output buffer for the
  1693. // next flag byte. Do not advance OutputPointer if we
  1694. // have no more input anyway!
  1695. //
  1696. FlagBit = (FlagBit + 1) % 8;
  1697. if (!FlagBit && (InputPointer < EndOfUncompressedBufferPlus1)) {
  1698. *FlagPointer = FlagByte;
  1699. FlagByte = 0;
  1700. FlagPointer = (OutputPointer++);
  1701. }
  1702. }
  1703. //
  1704. // We've exited the preceeding loop because either the input buffer is
  1705. // all compressed or because we ran out of space in the output buffer.
  1706. // Check here if the input buffer is not exhasted (i.e., we ran out
  1707. // of space)
  1708. //
  1709. if (InputPointer < EndOfUncompressedBufferPlus1) {
  1710. //
  1711. // We ran out of space, but now if the total space available
  1712. // for the compressed chunk is equal to the uncompressed data plus
  1713. // the header then we will make this an uncompressed chunk and copy
  1714. // over the uncompressed data
  1715. //
  1716. if ((CompressedBuffer + MAX_UNCOMPRESSED_CHUNK_SIZE + sizeof(COMPRESSED_CHUNK_HEADER)) <= EndOfCompressedBufferPlus1) {
  1717. RtlCopyMemory( CompressedBuffer + sizeof(COMPRESSED_CHUNK_HEADER),
  1718. UncompressedBuffer,
  1719. MAX_UNCOMPRESSED_CHUNK_SIZE );
  1720. *FinalCompressedChunkSize = MAX_UNCOMPRESSED_CHUNK_SIZE + sizeof(COMPRESSED_CHUNK_HEADER);
  1721. SetCompressedChunkHeader( ChunkHeader,
  1722. (USHORT)*FinalCompressedChunkSize,
  1723. FALSE );
  1724. RtlStoreUshort( CompressedBuffer, ChunkHeader.Short );
  1725. return STATUS_SUCCESS;
  1726. }
  1727. //
  1728. // Otherwise the input buffer really is too small to store the
  1729. // compressed chuunk
  1730. //
  1731. return STATUS_BUFFER_TOO_SMALL;
  1732. }
  1733. //
  1734. // At this point the entire input buffer has been compressed so we need
  1735. // to output the last flag byte, provided it fits in the compressed buffer,
  1736. // set and store the chunk header. Now if the Flag pointer doesn't fit
  1737. // in the output buffer that is because it is one beyond the end and
  1738. // we incremented output pointer too far so now bring output pointer
  1739. // back down.
  1740. //
  1741. if (FlagPointer < EndOfCompressedChunkPlus1) {
  1742. *FlagPointer = FlagByte;
  1743. } else {
  1744. OutputPointer--;
  1745. }
  1746. *FinalCompressedChunkSize = (ULONG)(OutputPointer - CompressedBuffer);
  1747. SetCompressedChunkHeader( ChunkHeader,
  1748. (USHORT)*FinalCompressedChunkSize,
  1749. TRUE );
  1750. RtlStoreUshort( CompressedBuffer, ChunkHeader.Short );
  1751. //
  1752. // Now if the only literal we ever output was a null then the
  1753. // input buffer was all zeros.
  1754. //
  1755. if (!NullCharacter) {
  1756. return STATUS_BUFFER_ALL_ZEROS;
  1757. }
  1758. //
  1759. // Otherwise return to our caller
  1760. //
  1761. return STATUS_SUCCESS;
  1762. }
  1763. #if !defined(_X86_)
  1764. //
  1765. // Local support routine
  1766. //
  1767. NTSTATUS
  1768. LZNT1DecompressChunk (
  1769. OUT PUCHAR UncompressedBuffer,
  1770. IN PUCHAR EndOfUncompressedBufferPlus1,
  1771. IN PUCHAR CompressedBuffer,
  1772. IN PUCHAR EndOfCompressedBufferPlus1,
  1773. OUT PULONG FinalUncompressedChunkSize
  1774. )
  1775. /*++
  1776. Routine Description:
  1777. This routine takes as input a compressed chunk and produces its
  1778. uncompressed equivalent chunk provided the uncompressed data fits
  1779. within the specified destination buffer.
  1780. The compressed buffer must be stored in the LZNT1 format.
  1781. An output variable indicates the number of bytes used to store the
  1782. uncompressed data.
  1783. Arguments:
  1784. UncompressedBuffer - Supplies a pointer to where the uncompressed
  1785. chunk is to be stored.
  1786. EndOfUncompressedBufferPlus1 - Supplies a pointer to the next byte
  1787. following the end of the uncompressed buffer. This is supplied
  1788. instead of the size in bytes because our caller and ourselves
  1789. test against the pointer and by passing the pointer we get to
  1790. skip the code to compute it each time.
  1791. CompressedBuffer - Supplies a pointer to the compressed chunk. (This
  1792. pointer has already been adjusted to point past the chunk header.)
  1793. EndOfCompressedBufferPlus1 - Supplies a pointer to the next
  1794. byte following the end of the compressed buffer.
  1795. FinalUncompressedChunkSize - Receives the number of bytes needed in
  1796. the uncompressed buffer to store the uncompressed chunk.
  1797. Return Value:
  1798. STATUS_SUCCESS - the decompression worked without a hitch.
  1799. STATUS_BAD_COMPRESSION_BUFFER - the input compressed buffer is
  1800. ill-formed.
  1801. --*/
  1802. {
  1803. PUCHAR OutputPointer;
  1804. PUCHAR InputPointer;
  1805. UCHAR FlagByte;
  1806. ULONG FlagBit;
  1807. PLZNT1_FORMAT Format = FORMAT412;
  1808. //
  1809. // The two pointers will slide through our input and input buffer.
  1810. // For the input buffer we skip over the chunk header.
  1811. //
  1812. OutputPointer = UncompressedBuffer;
  1813. InputPointer = CompressedBuffer;
  1814. //
  1815. // The flag byte stores a copy of the flags for the current
  1816. // run and the flag bit denotes the current bit position within
  1817. // the flag that we are processing
  1818. //
  1819. FlagByte = *(InputPointer++);
  1820. FlagBit = 0;
  1821. //
  1822. // While we haven't exhausted either the input or output buffer
  1823. // we will do some more decompression
  1824. //
  1825. while ((OutputPointer < EndOfUncompressedBufferPlus1) && (InputPointer < EndOfCompressedBufferPlus1)) {
  1826. while (UncompressedBuffer + Format->MaxDisplacement < OutputPointer) { Format += 1; }
  1827. //
  1828. // Check the current flag if it is zero then the current
  1829. // input token is a literal byte that we simply copy over
  1830. // to the output buffer
  1831. //
  1832. if (!FlagOn(FlagByte, (1 << FlagBit))) {
  1833. *(OutputPointer++) = *(InputPointer++);
  1834. } else {
  1835. USHORT CopyToken;
  1836. LONG Displacement;
  1837. LONG Length;
  1838. //
  1839. // The current input is a copy token so we'll get the
  1840. // copy token into our variable and extract the
  1841. // length and displacement from the token
  1842. //
  1843. if (InputPointer+1 >= EndOfCompressedBufferPlus1) {
  1844. *FinalUncompressedChunkSize = PtrToUlong(InputPointer);
  1845. return STATUS_BAD_COMPRESSION_BUFFER;
  1846. }
  1847. //
  1848. // Now grab the next input byte and extract the
  1849. // length and displacement from the copy token
  1850. //
  1851. RtlRetrieveUshort( &CopyToken, InputPointer );
  1852. InputPointer += sizeof(USHORT);
  1853. Displacement = GetLZNT1Displacement(Format, CopyToken);
  1854. Length = GetLZNT1Length(Format, CopyToken);
  1855. //
  1856. // At this point we have the length and displacement
  1857. // from the copy token, now we need to make sure that the
  1858. // displacement doesn't send us outside the uncompressed buffer
  1859. //
  1860. if (Displacement > (OutputPointer - UncompressedBuffer)) {
  1861. *FinalUncompressedChunkSize = PtrToUlong(InputPointer);
  1862. return STATUS_BAD_COMPRESSION_BUFFER;
  1863. }
  1864. //
  1865. // We also need to adjust the length to keep the copy from
  1866. // overflowing the output buffer
  1867. //
  1868. if ((OutputPointer + Length) >= EndOfUncompressedBufferPlus1) {
  1869. Length = (ULONG)(EndOfUncompressedBufferPlus1 - OutputPointer);
  1870. }
  1871. //
  1872. // Now we copy bytes. We cannot use Rtl Move Memory here because
  1873. // it does the copy backwards from what the LZ algorithm needs.
  1874. //
  1875. while (Length > 0) {
  1876. *(OutputPointer) = *(OutputPointer-Displacement);
  1877. Length -= 1;
  1878. OutputPointer += 1;
  1879. }
  1880. }
  1881. //
  1882. // Before we go back to the start of the loop we need to adjust the
  1883. // flag bit value (it goes from 0, 1, ... 7) and if the flag bit
  1884. // is back to zero we need to read in the next flag byte. In this
  1885. // case we are at the end of the input buffer we'll just break out
  1886. // of the loop because we're done.
  1887. //
  1888. FlagBit = (FlagBit + 1) % 8;
  1889. if (!FlagBit) {
  1890. if (InputPointer >= EndOfCompressedBufferPlus1) { break; }
  1891. FlagByte = *(InputPointer++);
  1892. }
  1893. }
  1894. //
  1895. // The decompression is done so now set the final uncompressed
  1896. // chunk size and return success to our caller
  1897. //
  1898. *FinalUncompressedChunkSize = (ULONG)(OutputPointer - UncompressedBuffer);
  1899. return STATUS_SUCCESS;
  1900. }
  1901. #endif // _X86_
  1902. //
  1903. // Local support routine
  1904. //
  1905. ULONG
  1906. LZNT1FindMatchStandard (
  1907. IN PUCHAR ZivString,
  1908. IN PLZNT1_STANDARD_WORKSPACE WorkSpace
  1909. )
  1910. /*++
  1911. Routine Description:
  1912. This routine does the compression lookup. It locates
  1913. a match for the ziv within a specified uncompressed buffer.
  1914. Arguments:
  1915. ZivString - Supplies a pointer to the Ziv in the uncompressed buffer.
  1916. The Ziv is the string we want to try and find a match for.
  1917. Return Value:
  1918. Returns the length of the match if the match is greater than three
  1919. characters otherwise return 0.
  1920. --*/
  1921. {
  1922. PUCHAR UncompressedBuffer = WorkSpace->UncompressedBuffer;
  1923. PUCHAR EndOfUncompressedBufferPlus1 = WorkSpace->EndOfUncompressedBufferPlus1;
  1924. ULONG MaxLength = WorkSpace->MaxLength;
  1925. ULONG Index;
  1926. PUCHAR FirstEntry;
  1927. ULONG FirstLength;
  1928. PUCHAR SecondEntry;
  1929. ULONG SecondLength;
  1930. //
  1931. // First check if the Ziv is within two bytes of the end of
  1932. // the uncompressed buffer, if so then we can't match
  1933. // three or more characters
  1934. //
  1935. Index = ((40543*((((ZivString[0]<<4)^ZivString[1])<<4)^ZivString[2]))>>4) & 0xfff;
  1936. FirstEntry = WorkSpace->IndexPTable[Index][0];
  1937. FirstLength = 0;
  1938. SecondEntry = WorkSpace->IndexPTable[Index][1];
  1939. SecondLength = 0;
  1940. //
  1941. // Check if first entry is good, and if so then get its length
  1942. //
  1943. if ((FirstEntry >= UncompressedBuffer) && // is it within the uncompressed buffer?
  1944. (FirstEntry < ZivString) &&
  1945. (FirstEntry[0] == ZivString[0]) && // do at least 3 characters match?
  1946. (FirstEntry[1] == ZivString[1]) &&
  1947. (FirstEntry[2] == ZivString[2])) {
  1948. FirstLength = 3;
  1949. while ((FirstLength < MaxLength)
  1950. &&
  1951. (ZivString + FirstLength < EndOfUncompressedBufferPlus1)
  1952. &&
  1953. (ZivString[FirstLength] == FirstEntry[FirstLength])) {
  1954. FirstLength++;
  1955. }
  1956. }
  1957. //
  1958. // Check if second entry is good, and if so then get its length
  1959. //
  1960. if ((SecondEntry >= UncompressedBuffer) && // is it within the uncompressed buffer?
  1961. (SecondEntry < ZivString) &&
  1962. (SecondEntry[0] == ZivString[0]) && // do at least 3 characters match?
  1963. (SecondEntry[1] == ZivString[1]) &&
  1964. (SecondEntry[2] == ZivString[2])) {
  1965. SecondLength = 3;
  1966. while ((SecondLength < MaxLength)
  1967. &&
  1968. (ZivString + SecondLength< EndOfUncompressedBufferPlus1)
  1969. &&
  1970. (ZivString[SecondLength] == SecondEntry[SecondLength])) {
  1971. SecondLength++;
  1972. }
  1973. }
  1974. if ((FirstLength >= SecondLength)) {
  1975. WorkSpace->IndexPTable[Index][1] = FirstEntry;
  1976. WorkSpace->IndexPTable[Index][0] = ZivString;
  1977. WorkSpace->MatchedString = FirstEntry;
  1978. return FirstLength;
  1979. }
  1980. WorkSpace->IndexPTable[Index][1] = FirstEntry;
  1981. WorkSpace->IndexPTable[Index][0] = ZivString;
  1982. WorkSpace->MatchedString = SecondEntry;
  1983. return SecondLength;
  1984. }
  1985. //
  1986. // Local support routine
  1987. //
  1988. ULONG
  1989. LZNT1FindMatchMaximum (
  1990. IN PUCHAR ZivString,
  1991. IN PLZNT1_MAXIMUM_WORKSPACE WorkSpace
  1992. )
  1993. /*++
  1994. Routine Description:
  1995. This routine does the compression lookup. It locates
  1996. a match for the ziv within a specified uncompressed buffer.
  1997. If the matched string is two or more characters long then this
  1998. routine does not update the lookup state information.
  1999. Arguments:
  2000. ZivString - Supplies a pointer to the Ziv in the uncompressed buffer.
  2001. The Ziv is the string we want to try and find a match for.
  2002. Return Value:
  2003. Returns the length of the match if the match is greater than three
  2004. characters otherwise return 0.
  2005. --*/
  2006. {
  2007. PUCHAR UncompressedBuffer = WorkSpace->UncompressedBuffer;
  2008. PUCHAR EndOfUncompressedBufferPlus1 = WorkSpace->EndOfUncompressedBufferPlus1;
  2009. ULONG MaxLength = WorkSpace->MaxLength;
  2010. ULONG i;
  2011. ULONG BestMatchedLength;
  2012. PUCHAR q;
  2013. //
  2014. // First check if the Ziv is within two bytes of the end of
  2015. // the uncompressed buffer, if so then we can't match
  2016. // three or more characters
  2017. //
  2018. BestMatchedLength = 0;
  2019. for (q = UncompressedBuffer; q < ZivString; q += 1) {
  2020. i = 0;
  2021. while ((i < MaxLength)
  2022. &&
  2023. (ZivString + i < EndOfUncompressedBufferPlus1)
  2024. &&
  2025. (ZivString[i] == q[i])) {
  2026. i++;
  2027. }
  2028. if (i >= BestMatchedLength) {
  2029. BestMatchedLength = i;
  2030. WorkSpace->MatchedString = q;
  2031. }
  2032. }
  2033. if (BestMatchedLength < 3) {
  2034. return 0;
  2035. } else {
  2036. return BestMatchedLength;
  2037. }
  2038. }
  2039. #if defined(ALLOC_DATA_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
  2040. #pragma const_seg()
  2041. #endif