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.

2846 lines
86 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. //
  1106. // The Copy token is two bytes in size.
  1107. // Our definition uses a union to make it easier to set and retrieve token values.
  1108. //
  1109. // Copy Token
  1110. //
  1111. // Length Displacement
  1112. //
  1113. // 12 bits 3 to 4098 4 bits 1 to 16
  1114. // 11 bits 3 to 2050 5 bits 1 to 32
  1115. // 10 bits 3 to 1026 6 bits 1 to 64
  1116. // 9 bits 3 to 514 7 bits 1 to 128
  1117. // 8 bits 3 to 258 8 bits 1 to 256
  1118. // 7 bits 3 to 130 9 bits 1 to 512
  1119. // 6 bits 3 to 66 10 bits 1 to 1024
  1120. // 5 bits 3 to 34 11 bits 1 to 2048
  1121. // 4 bits 3 to 18 12 bits 1 to 4096
  1122. //
  1123. #define FORMAT412 0
  1124. #define FORMAT511 1
  1125. #define FORMAT610 2
  1126. #define FORMAT79 3
  1127. #define FORMAT88 4
  1128. #define FORMAT97 5
  1129. #define FORMAT106 6
  1130. #define FORMAT115 7
  1131. #define FORMAT124 8
  1132. // 4/12 5/11 6/10 7/9 8/8 9/7 10/6 11/5 12/4
  1133. #if defined(ALLOC_DATA_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
  1134. #pragma const_seg("PAGELKCONST")
  1135. #endif
  1136. const ULONG FormatMaxLength[] = { 4098, 2050, 1026, 514, 258, 130, 66, 34, 18 };
  1137. const ULONG FormatMaxDisplacement[] = { 16, 32, 64, 128, 256, 512, 1024, 2048, 4096 };
  1138. typedef union _LZNT1_COPY_TOKEN {
  1139. struct { USHORT Length : 12; USHORT Displacement : 4; } Fields412;
  1140. struct { USHORT Length : 11; USHORT Displacement : 5; } Fields511;
  1141. struct { USHORT Length : 10; USHORT Displacement : 6; } Fields610;
  1142. struct { USHORT Length : 9; USHORT Displacement : 7; } Fields79;
  1143. struct { USHORT Length : 8; USHORT Displacement : 8; } Fields88;
  1144. struct { USHORT Length : 7; USHORT Displacement : 9; } Fields97;
  1145. struct { USHORT Length : 6; USHORT Displacement : 10; } Fields106;
  1146. struct { USHORT Length : 5; USHORT Displacement : 11; } Fields115;
  1147. struct { USHORT Length : 4; USHORT Displacement : 12; } Fields124;
  1148. UCHAR Bytes[2];
  1149. } LZNT1_COPY_TOKEN, *PLZNT1_COPY_TOKEN;
  1150. //
  1151. // USHORT
  1152. // GetLZNT1Length (
  1153. // IN COPY_TOKEN_FORMAT Format,
  1154. // IN LZNT1_COPY_TOKEN CopyToken
  1155. // );
  1156. //
  1157. // USHORT
  1158. // GetLZNT1Displacement (
  1159. // IN COPY_TOKEN_FORMAT Format,
  1160. // IN LZNT1_COPY_TOKEN CopyToken
  1161. // );
  1162. //
  1163. // VOID
  1164. // SetLZNT1 (
  1165. // IN COPY_TOKEN_FORMAT Format,
  1166. // IN LZNT1_COPY_TOKEN CopyToken,
  1167. // IN USHORT Length,
  1168. // IN USHORT Displacement
  1169. // );
  1170. //
  1171. #define GetLZNT1Length(F,CT) ( \
  1172. ( F == FORMAT412 ? (CT).Fields412.Length + 3 \
  1173. : F == FORMAT511 ? (CT).Fields511.Length + 3 \
  1174. : F == FORMAT610 ? (CT).Fields610.Length + 3 \
  1175. : F == FORMAT79 ? (CT).Fields79.Length + 3 \
  1176. : F == FORMAT88 ? (CT).Fields88.Length + 3 \
  1177. : F == FORMAT97 ? (CT).Fields97.Length + 3 \
  1178. : F == FORMAT106 ? (CT).Fields106.Length + 3 \
  1179. : F == FORMAT115 ? (CT).Fields115.Length + 3 \
  1180. : (CT).Fields124.Length + 3 \
  1181. ) \
  1182. )
  1183. #define GetLZNT1Displacement(F,CT) ( \
  1184. ( F == FORMAT412 ? (CT).Fields412.Displacement + 1 \
  1185. : F == FORMAT511 ? (CT).Fields511.Displacement + 1 \
  1186. : F == FORMAT610 ? (CT).Fields610.Displacement + 1 \
  1187. : F == FORMAT79 ? (CT).Fields79.Displacement + 1 \
  1188. : F == FORMAT88 ? (CT).Fields88.Displacement + 1 \
  1189. : F == FORMAT97 ? (CT).Fields97.Displacement + 1 \
  1190. : F == FORMAT106 ? (CT).Fields106.Displacement + 1 \
  1191. : F == FORMAT115 ? (CT).Fields115.Displacement + 1 \
  1192. : (CT).Fields124.Displacement + 1 \
  1193. ) \
  1194. )
  1195. #define SetLZNT1(F,CT,L,D) { \
  1196. if (F == FORMAT412) { (CT).Fields412.Length = (L) - 3; (CT).Fields412.Displacement = (D) - 1; } \
  1197. else if (F == FORMAT511) { (CT).Fields511.Length = (L) - 3; (CT).Fields511.Displacement = (D) - 1; } \
  1198. else if (F == FORMAT610) { (CT).Fields610.Length = (L) - 3; (CT).Fields610.Displacement = (D) - 1; } \
  1199. else if (F == FORMAT79) { (CT).Fields79.Length = (L) - 3; (CT).Fields79.Displacement = (D) - 1; } \
  1200. else if (F == FORMAT88) { (CT).Fields88.Length = (L) - 3; (CT).Fields88.Displacement = (D) - 1; } \
  1201. else if (F == FORMAT97) { (CT).Fields97.Length = (L) - 3; (CT).Fields97.Displacement = (D) - 1; } \
  1202. else if (F == FORMAT106) { (CT).Fields106.Length = (L) - 3; (CT).Fields106.Displacement = (D) - 1; } \
  1203. else if (F == FORMAT115) { (CT).Fields115.Length = (L) - 3; (CT).Fields115.Displacement = (D) - 1; } \
  1204. else { (CT).Fields124.Length = (L) - 3; (CT).Fields124.Displacement = (D) - 1; } \
  1205. }
  1206. #pragma optimize("t", on)
  1207. //
  1208. // Local support routine
  1209. //
  1210. NTSTATUS
  1211. LZNT1CompressChunkHiber (
  1212. IN PUCHAR UncompressedBuffer,
  1213. IN PUCHAR EndOfUncompressedBufferPlus1,
  1214. OUT PUCHAR CompressedBuffer,
  1215. IN PUCHAR EndOfCompressedBufferPlus1,
  1216. OUT PULONG FinalCompressedChunkSize,
  1217. IN PLZNT1_HIBER_WORKSPACE WorkSpace
  1218. )
  1219. /*++
  1220. Routine Description:
  1221. This routine takes as input an uncompressed chunk and produces
  1222. one compressed chunk provided the compressed data fits within
  1223. the specified destination buffer.
  1224. The LZNT1 format used to store the compressed buffer.
  1225. An output variable indicates the number of bytes used to store
  1226. the compressed chunk.
  1227. Arguments:
  1228. UncompressedBuffer - Supplies a pointer to the uncompressed chunk.
  1229. EndOfUncompressedBufferPlus1 - Supplies a pointer to the next byte
  1230. following the end of the uncompressed buffer. This is supplied
  1231. instead of the size in bytes because our caller and ourselves
  1232. test against the pointer and by passing the pointer we get to
  1233. skip the code to compute it each time.
  1234. CompressedBuffer - Supplies a pointer to where the compressed chunk
  1235. is to be stored.
  1236. EndOfCompressedBufferPlus1 - Supplies a pointer to the next
  1237. byte following the end of the compressed buffer.
  1238. FinalCompressedChunkSize - Receives the number of bytes needed in
  1239. the compressed buffer to store the compressed chunk.
  1240. Return Value:
  1241. STATUS_SUCCESS - the compression worked without a hitch.
  1242. STATUS_BUFFER_ALL_ZEROS - the compression worked without a hitch and in
  1243. addition the input chunk was all zeros.
  1244. STATUS_BUFFER_TOO_SMALL - the compressed buffer is too small to hold the
  1245. compressed data.
  1246. --*/
  1247. {
  1248. ULONG IndexOrigin = WorkSpace->IndexTable[0] + 2*MAX_UNCOMPRESSED_CHUNK_SIZE;
  1249. PUCHAR EndOfCompressedChunkPlus1;
  1250. PUCHAR EndOfCompressedChunkPlus1Minus16;
  1251. PUCHAR InputPointer;
  1252. PUCHAR OutputPointer;
  1253. PUCHAR FlagPointer;
  1254. UCHAR FlagByte;
  1255. ULONG FlagBit;
  1256. ULONG Length;
  1257. UCHAR NullCharacter = 0;
  1258. ULONG Format;
  1259. ULONG MaxLength;
  1260. PUCHAR MaxInputPointer;
  1261. //
  1262. // First adjust the end of the uncompressed buffer pointer to the smaller
  1263. // of what we're passed in and the uncompressed chunk size. We use this
  1264. // to make sure we never compress more than a chunk worth at a time
  1265. //
  1266. if ((UncompressedBuffer + MAX_UNCOMPRESSED_CHUNK_SIZE) < EndOfUncompressedBufferPlus1) {
  1267. EndOfUncompressedBufferPlus1 = UncompressedBuffer + MAX_UNCOMPRESSED_CHUNK_SIZE;
  1268. }
  1269. //
  1270. // Now set the end of the compressed chunk pointer to be the smaller of the
  1271. // compressed size necessary to hold the data in an uncompressed form and
  1272. // the compressed buffer size. We use this to decide if we can't compress
  1273. // any more because the buffer is too small or just because the data
  1274. // doesn't compress very well.
  1275. //
  1276. if ((CompressedBuffer + MAX_UNCOMPRESSED_CHUNK_SIZE - 1) < EndOfCompressedBufferPlus1) {
  1277. EndOfCompressedChunkPlus1 = CompressedBuffer + MAX_UNCOMPRESSED_CHUNK_SIZE - 1;
  1278. } else {
  1279. EndOfCompressedChunkPlus1 = EndOfCompressedBufferPlus1;
  1280. }
  1281. EndOfCompressedChunkPlus1Minus16 = EndOfCompressedChunkPlus1 - 16;
  1282. //
  1283. // Now set the input and output pointers to the next byte we are
  1284. // go to process and asser that the user gave use buffers that were
  1285. // large enough to hold the minimum size chunks
  1286. //
  1287. InputPointer = UncompressedBuffer;
  1288. OutputPointer = CompressedBuffer + sizeof(COMPRESSED_CHUNK_HEADER);
  1289. ASSERT(InputPointer < EndOfUncompressedBufferPlus1);
  1290. //**** ASSERT(OutputPointer + 2 <= EndOfCompressedChunkPlus1);
  1291. //
  1292. // The flag byte stores a copy of the flags for the current
  1293. // run and the flag bit denotes the current bit position within
  1294. // the flag that we are processing. The Flag pointer denotes
  1295. // where in the compressed buffer we will store the current
  1296. // flag byte
  1297. //
  1298. FlagPointer = OutputPointer++;
  1299. FlagBit = 0;
  1300. FlagByte = 0;
  1301. //
  1302. // While there is some more data to be compressed we will do the
  1303. // following loop
  1304. //
  1305. //
  1306. // Ensure that there are at least 3 characters in the buffer
  1307. // It takes at least that many to make a match
  1308. //
  1309. Format = FORMAT412;
  1310. MaxLength = FormatMaxLength[Format];
  1311. MaxInputPointer = UncompressedBuffer + FormatMaxDisplacement[Format];
  1312. if (OutputPointer < EndOfCompressedChunkPlus1Minus16) {
  1313. PUCHAR EndOfUncompressedBufferPlus1Minus3 = EndOfUncompressedBufferPlus1 - 3;
  1314. while (InputPointer <= EndOfUncompressedBufferPlus1Minus3) {
  1315. UCHAR InputPointer0;
  1316. ULONG Index;
  1317. ULONG InputOffset;
  1318. ULONG MatchedOffset;
  1319. ULONG MatchedIndex;
  1320. PUCHAR MatchedString;
  1321. Index = InputPointer[0];
  1322. Index = ( (Index << 8) | (Index >> 4) );
  1323. Index = ( Index ^ InputPointer[1] ^ (InputPointer[2]<<4) ) & 0xfff;
  1324. MatchedIndex = (ULONG)(WorkSpace->IndexTable[Index]);
  1325. InputOffset = (ULONG)(InputPointer - UncompressedBuffer);
  1326. WorkSpace->IndexTable[Index] = (IndexOrigin + InputOffset);
  1327. MatchedOffset = (ULONG)(MatchedIndex - IndexOrigin);
  1328. //
  1329. // Check whether purported match lies within current buffer
  1330. // Recall that the hint vector may contain arbitrary garbage
  1331. //
  1332. if ( (MatchedOffset < InputOffset)
  1333. && ( (MatchedString = UncompressedBuffer + MatchedOffset)
  1334. , (MatchedString[0] == InputPointer[0]) ) // do at least 3 characters match?
  1335. && (MatchedString[1] == InputPointer[1])
  1336. && (MatchedString[2] == InputPointer[2]) ) {
  1337. ULONG MaxLength1;
  1338. ULONG MaxLength4;
  1339. while (MaxInputPointer < InputPointer) {
  1340. Format += 1;
  1341. MaxLength = FormatMaxLength[Format];
  1342. MaxInputPointer = UncompressedBuffer + FormatMaxDisplacement[Format];
  1343. }
  1344. MaxLength1 = (ULONG)(EndOfUncompressedBufferPlus1 - InputPointer);
  1345. if (MaxLength < MaxLength1) MaxLength1 = MaxLength;
  1346. MaxLength4 = MaxLength1 - (4 - 1);
  1347. Length = 3;
  1348. for (;;) {
  1349. if ((long)Length < (long)MaxLength4) {
  1350. if (InputPointer[Length] != MatchedString[Length]) break;
  1351. Length++;
  1352. if (InputPointer[Length] != MatchedString[Length]) break;
  1353. Length++;
  1354. if (InputPointer[Length] != MatchedString[Length]) break;
  1355. Length++;
  1356. if (InputPointer[Length] != MatchedString[Length]) break;
  1357. Length++;
  1358. continue;
  1359. } else {
  1360. while (Length < MaxLength1) {
  1361. if (InputPointer[Length] != MatchedString[Length]) break;
  1362. Length++;
  1363. }
  1364. break;
  1365. }
  1366. }
  1367. //
  1368. // We need to output a two byte copy token
  1369. // Ensure that there is room in the output buffer
  1370. //
  1371. ASSERT((OutputPointer+1) < EndOfCompressedChunkPlus1);
  1372. //
  1373. // Compute the displacement from the current pointer
  1374. // to the matched string
  1375. //
  1376. SetFlag(FlagByte, (1 << FlagBit));
  1377. {
  1378. ULONG Displacement = (ULONG)(InputPointer - MatchedString);
  1379. ULONG token = ( ( (Displacement-1) << (12-Format) ) | (Length-3) );
  1380. ASSERT( 0 == ( (Displacement-1) & ~( (1 << (4+Format) ) - 1) ) );
  1381. ASSERT( 0 == ( (Length-3) & ~( (1 << (12-Format) ) - 1) ) );
  1382. *(OutputPointer++) = (UCHAR)(token);
  1383. *(OutputPointer++) = (UCHAR)(token>>8);
  1384. }
  1385. InputPointer += Length;
  1386. } else {
  1387. //
  1388. // There is more data to output now make sure the output
  1389. // buffer is not already full and can contain at least one
  1390. // more byte
  1391. //
  1392. ASSERT(OutputPointer < EndOfCompressedChunkPlus1);
  1393. ASSERT(!FlagOn(FlagByte, (1 << FlagBit)));
  1394. NullCharacter |= *(OutputPointer++) = *(InputPointer++);
  1395. }
  1396. //
  1397. // Now adjust the flag bit and check if the flag byte
  1398. // should now be output. If so output the flag byte
  1399. // and scarf up a new byte in the output buffer for the
  1400. // next flag byte. Do not advance OutputPointer if we
  1401. // have no more input anyway!
  1402. //
  1403. FlagBit = (FlagBit + 1) % 8;
  1404. if (!FlagBit) {
  1405. *FlagPointer = FlagByte;
  1406. FlagByte = 0;
  1407. FlagPointer = (OutputPointer++);
  1408. //
  1409. // Ensure that we have room for the at most 16 bytes
  1410. // that this flag byte may describe
  1411. //
  1412. if (OutputPointer >= EndOfCompressedChunkPlus1Minus16) { break; }
  1413. }
  1414. }
  1415. }
  1416. //
  1417. // UNDONE: Could pick up another match or two right at the end of the buffer
  1418. //
  1419. //
  1420. // Too few characters left for a match, emit them as literals
  1421. //
  1422. if (OutputPointer < EndOfCompressedChunkPlus1Minus16) {
  1423. while (InputPointer < EndOfUncompressedBufferPlus1) {
  1424. while (MaxInputPointer < InputPointer) {
  1425. Format += 1;
  1426. MaxLength = FormatMaxLength[Format];
  1427. MaxInputPointer = UncompressedBuffer + FormatMaxDisplacement[Format];
  1428. }
  1429. //
  1430. // There is more data to output now make sure the output
  1431. // buffer is not already full and can contain at least one
  1432. // more byte
  1433. //
  1434. ASSERT(OutputPointer < EndOfCompressedChunkPlus1);
  1435. ASSERT(!FlagOn(FlagByte, (1 << FlagBit)));
  1436. NullCharacter |= *(OutputPointer++) = *(InputPointer++);
  1437. //
  1438. // Now adjust the flag bit and check if the flag byte
  1439. // should now be output. If so output the flag byte
  1440. // and scarf up a new byte in the output buffer for the
  1441. // next flag byte. Do not advance OutputPointer if we
  1442. // have no more input anyway!
  1443. //
  1444. FlagBit = (FlagBit + 1) % 8;
  1445. if (!FlagBit) {
  1446. *FlagPointer = FlagByte;
  1447. FlagByte = 0;
  1448. FlagPointer = (OutputPointer++);
  1449. //
  1450. // Ensure that we have room for the at most 16 bytes
  1451. // that this flag byte may describe
  1452. //
  1453. if (OutputPointer >= EndOfCompressedChunkPlus1Minus16) { break; }
  1454. }
  1455. }
  1456. }
  1457. //
  1458. // We've exited the preceeding loop because either the input buffer is
  1459. // all compressed or because we ran out of space in the output buffer.
  1460. // Check here if the input buffer is not exhasted (i.e., we ran out
  1461. // of space)
  1462. //
  1463. if (InputPointer < EndOfUncompressedBufferPlus1) {
  1464. //
  1465. // We ran out of space, but now if the total space available
  1466. // for the compressed chunk is equal to the uncompressed data plus
  1467. // the header then we will make this an uncompressed chunk and copy
  1468. // over the uncompressed data
  1469. //
  1470. if ((CompressedBuffer + MAX_UNCOMPRESSED_CHUNK_SIZE + sizeof(COMPRESSED_CHUNK_HEADER)) <= EndOfCompressedBufferPlus1) {
  1471. COMPRESSED_CHUNK_HEADER ChunkHeader;
  1472. RtlCopyMemory( CompressedBuffer + sizeof(COMPRESSED_CHUNK_HEADER),
  1473. UncompressedBuffer,
  1474. MAX_UNCOMPRESSED_CHUNK_SIZE );
  1475. *FinalCompressedChunkSize = MAX_UNCOMPRESSED_CHUNK_SIZE + sizeof(COMPRESSED_CHUNK_HEADER);
  1476. ChunkHeader.Short = 0;
  1477. SetCompressedChunkHeader( ChunkHeader,
  1478. (USHORT)*FinalCompressedChunkSize,
  1479. FALSE );
  1480. RtlStoreUshort( CompressedBuffer, ChunkHeader.Short );
  1481. WorkSpace->IndexTable[0] = IndexOrigin;
  1482. return STATUS_SUCCESS;
  1483. }
  1484. //
  1485. // Otherwise the input buffer really is too small to store the
  1486. // compressed chuunk
  1487. //
  1488. WorkSpace->IndexTable[0] = IndexOrigin;
  1489. return STATUS_BUFFER_TOO_SMALL;
  1490. }
  1491. //
  1492. // At this point the entire input buffer has been compressed so we need
  1493. // to output the last flag byte, provided it fits in the compressed buffer,
  1494. // set and store the chunk header. Now if the Flag pointer doesn't fit
  1495. // in the output buffer that is because it is one beyond the end and
  1496. // we incremented output pointer too far so now bring output pointer
  1497. // back down.
  1498. //
  1499. if (FlagPointer < EndOfCompressedChunkPlus1) {
  1500. *FlagPointer = FlagByte;
  1501. } else {
  1502. OutputPointer--;
  1503. }
  1504. {
  1505. COMPRESSED_CHUNK_HEADER ChunkHeader;
  1506. *FinalCompressedChunkSize = (ULONG)(OutputPointer - CompressedBuffer);
  1507. ChunkHeader.Short = 0;
  1508. SetCompressedChunkHeader( ChunkHeader,
  1509. (USHORT)*FinalCompressedChunkSize,
  1510. TRUE );
  1511. RtlStoreUshort( CompressedBuffer, ChunkHeader.Short );
  1512. }
  1513. //
  1514. // Now if the only literal we ever output was a null then the
  1515. // input buffer was all zeros.
  1516. //
  1517. if (!NullCharacter) {
  1518. WorkSpace->IndexTable[0] = IndexOrigin;
  1519. return STATUS_BUFFER_ALL_ZEROS;
  1520. }
  1521. //
  1522. // Otherwise return to our caller
  1523. //
  1524. WorkSpace->IndexTable[0] = IndexOrigin;
  1525. return STATUS_SUCCESS;
  1526. }
  1527. #pragma optimize("t", off)
  1528. //
  1529. // Local support routine
  1530. //
  1531. NTSTATUS
  1532. LZNT1CompressChunk (
  1533. IN PLZNT1_MATCH_FUNCTION MatchFunction,
  1534. IN PUCHAR UncompressedBuffer,
  1535. IN PUCHAR EndOfUncompressedBufferPlus1,
  1536. OUT PUCHAR CompressedBuffer,
  1537. IN PUCHAR EndOfCompressedBufferPlus1,
  1538. OUT PULONG FinalCompressedChunkSize,
  1539. IN PVOID WorkSpace
  1540. )
  1541. /*++
  1542. Routine Description:
  1543. This routine takes as input an uncompressed chunk and produces
  1544. one compressed chunk provided the compressed data fits within
  1545. the specified destination buffer.
  1546. The LZNT1 format used to store the compressed buffer.
  1547. An output variable indicates the number of bytes used to store
  1548. the compressed chunk.
  1549. Arguments:
  1550. UncompressedBuffer - Supplies a pointer to the uncompressed chunk.
  1551. EndOfUncompressedBufferPlus1 - Supplies a pointer to the next byte
  1552. following the end of the uncompressed buffer. This is supplied
  1553. instead of the size in bytes because our caller and ourselves
  1554. test against the pointer and by passing the pointer we get to
  1555. skip the code to compute it each time.
  1556. CompressedBuffer - Supplies a pointer to where the compressed chunk
  1557. is to be stored.
  1558. EndOfCompressedBufferPlus1 - Supplies a pointer to the next
  1559. byte following the end of the compressed buffer.
  1560. FinalCompressedChunkSize - Receives the number of bytes needed in
  1561. the compressed buffer to store the compressed chunk.
  1562. Return Value:
  1563. STATUS_SUCCESS - the compression worked without a hitch.
  1564. STATUS_BUFFER_ALL_ZEROS - the compression worked without a hitch and in
  1565. addition the input chunk was all zeros.
  1566. STATUS_BUFFER_TOO_SMALL - the compressed buffer is too small to hold the
  1567. compressed data.
  1568. --*/
  1569. {
  1570. PUCHAR EndOfCompressedChunkPlus1;
  1571. PUCHAR InputPointer;
  1572. PUCHAR OutputPointer;
  1573. PUCHAR FlagPointer;
  1574. UCHAR FlagByte;
  1575. ULONG FlagBit;
  1576. LONG Length;
  1577. LONG Displacement;
  1578. LZNT1_COPY_TOKEN CopyToken;
  1579. COMPRESSED_CHUNK_HEADER ChunkHeader;
  1580. UCHAR NullCharacter = 0;
  1581. ULONG Format = FORMAT412;
  1582. //
  1583. // First adjust the end of the uncompressed buffer pointer to the smaller
  1584. // of what we're passed in and the uncompressed chunk size. We use this
  1585. // to make sure we never compress more than a chunk worth at a time
  1586. //
  1587. if ((UncompressedBuffer + MAX_UNCOMPRESSED_CHUNK_SIZE) < EndOfUncompressedBufferPlus1) {
  1588. EndOfUncompressedBufferPlus1 = UncompressedBuffer + MAX_UNCOMPRESSED_CHUNK_SIZE;
  1589. }
  1590. //
  1591. // Now set the end of the compressed chunk pointer to be the smaller of the
  1592. // compressed size necessary to hold the data in an uncompressed form and
  1593. // the compressed buffer size. We use this to decide if we can't compress
  1594. // any more because the buffer is too small or just because the data
  1595. // doesn't compress very well.
  1596. //
  1597. if ((CompressedBuffer + MAX_UNCOMPRESSED_CHUNK_SIZE - 1) < EndOfCompressedBufferPlus1) {
  1598. EndOfCompressedChunkPlus1 = CompressedBuffer + MAX_UNCOMPRESSED_CHUNK_SIZE - 1;
  1599. } else {
  1600. EndOfCompressedChunkPlus1 = EndOfCompressedBufferPlus1;
  1601. }
  1602. //
  1603. // Now set the input and output pointers to the next byte we are
  1604. // go to process and asser that the user gave use buffers that were
  1605. // large enough to hold the minimum size chunks
  1606. //
  1607. InputPointer = UncompressedBuffer;
  1608. OutputPointer = CompressedBuffer + sizeof(COMPRESSED_CHUNK_HEADER);
  1609. ASSERT(InputPointer < EndOfUncompressedBufferPlus1);
  1610. //**** ASSERT(OutputPointer + 2 <= EndOfCompressedChunkPlus1);
  1611. //
  1612. // The flag byte stores a copy of the flags for the current
  1613. // run and the flag bit denotes the current bit position within
  1614. // the flag that we are processing. The Flag pointer denotes
  1615. // where in the compressed buffer we will store the current
  1616. // flag byte
  1617. //
  1618. FlagPointer = OutputPointer++;
  1619. FlagBit = 0;
  1620. FlagByte = 0;
  1621. ChunkHeader.Short = 0;
  1622. //
  1623. // While there is some more data to be compressed we will do the
  1624. // following loop
  1625. //
  1626. ((PLZNT1_STANDARD_WORKSPACE)WorkSpace)->UncompressedBuffer = UncompressedBuffer;
  1627. ((PLZNT1_STANDARD_WORKSPACE)WorkSpace)->EndOfUncompressedBufferPlus1 = EndOfUncompressedBufferPlus1;
  1628. ((PLZNT1_STANDARD_WORKSPACE)WorkSpace)->MaxLength = FormatMaxLength[FORMAT412];
  1629. while (InputPointer < EndOfUncompressedBufferPlus1) {
  1630. while (UncompressedBuffer + FormatMaxDisplacement[Format] < InputPointer) {
  1631. Format += 1;
  1632. ((PLZNT1_STANDARD_WORKSPACE)WorkSpace)->MaxLength = FormatMaxLength[Format];
  1633. }
  1634. //
  1635. // Search for a string in the Lempel
  1636. //
  1637. Length = 0;
  1638. if ((InputPointer + 3) <= EndOfUncompressedBufferPlus1) {
  1639. Length = (MatchFunction)( InputPointer, WorkSpace );
  1640. }
  1641. //
  1642. // If the return length is zero then we need to output
  1643. // a literal. We clear the flag bit to denote the literal
  1644. // output the charcter and build up a character bits
  1645. // composite that if it is still zero when we are done then
  1646. // we know the uncompressed buffer contained only zeros.
  1647. //
  1648. if (!Length) {
  1649. //
  1650. // There is more data to output now make sure the output
  1651. // buffer is not already full and can contain at least one
  1652. // more byte
  1653. //
  1654. if (OutputPointer >= EndOfCompressedChunkPlus1) { break; }
  1655. ClearFlag(FlagByte, (1 << FlagBit));
  1656. NullCharacter |= *(OutputPointer++) = *(InputPointer++);
  1657. } else {
  1658. //
  1659. // We need to output two byte, now make sure that
  1660. // the output buffer can contain at least two more
  1661. // bytes.
  1662. //
  1663. if ((OutputPointer+1) >= EndOfCompressedChunkPlus1) { break; }
  1664. //
  1665. // Compute the displacement from the current pointer
  1666. // to the matched string
  1667. //
  1668. Displacement = (ULONG)(InputPointer - ((PLZNT1_STANDARD_WORKSPACE)WorkSpace)->MatchedString);
  1669. SetFlag(FlagByte, (1 << FlagBit));
  1670. SetLZNT1(Format, CopyToken, (USHORT)Length, (USHORT)Displacement);
  1671. *(OutputPointer++) = CopyToken.Bytes[0];
  1672. *(OutputPointer++) = CopyToken.Bytes[1];
  1673. InputPointer += Length;
  1674. }
  1675. //
  1676. // Now adjust the flag bit and check if the flag byte
  1677. // should now be output. If so output the flag byte
  1678. // and scarf up a new byte in the output buffer for the
  1679. // next flag byte. Do not advance OutputPointer if we
  1680. // have no more input anyway!
  1681. //
  1682. FlagBit = (FlagBit + 1) % 8;
  1683. if (!FlagBit && (InputPointer < EndOfUncompressedBufferPlus1)) {
  1684. *FlagPointer = FlagByte;
  1685. FlagByte = 0;
  1686. FlagPointer = (OutputPointer++);
  1687. }
  1688. }
  1689. //
  1690. // We've exited the preceeding loop because either the input buffer is
  1691. // all compressed or because we ran out of space in the output buffer.
  1692. // Check here if the input buffer is not exhasted (i.e., we ran out
  1693. // of space)
  1694. //
  1695. if (InputPointer < EndOfUncompressedBufferPlus1) {
  1696. //
  1697. // We ran out of space, but now if the total space available
  1698. // for the compressed chunk is equal to the uncompressed data plus
  1699. // the header then we will make this an uncompressed chunk and copy
  1700. // over the uncompressed data
  1701. //
  1702. if ((CompressedBuffer + MAX_UNCOMPRESSED_CHUNK_SIZE + sizeof(COMPRESSED_CHUNK_HEADER)) <= EndOfCompressedBufferPlus1) {
  1703. RtlCopyMemory( CompressedBuffer + sizeof(COMPRESSED_CHUNK_HEADER),
  1704. UncompressedBuffer,
  1705. MAX_UNCOMPRESSED_CHUNK_SIZE );
  1706. *FinalCompressedChunkSize = MAX_UNCOMPRESSED_CHUNK_SIZE + sizeof(COMPRESSED_CHUNK_HEADER);
  1707. SetCompressedChunkHeader( ChunkHeader,
  1708. (USHORT)*FinalCompressedChunkSize,
  1709. FALSE );
  1710. RtlStoreUshort( CompressedBuffer, ChunkHeader.Short );
  1711. return STATUS_SUCCESS;
  1712. }
  1713. //
  1714. // Otherwise the input buffer really is too small to store the
  1715. // compressed chuunk
  1716. //
  1717. return STATUS_BUFFER_TOO_SMALL;
  1718. }
  1719. //
  1720. // At this point the entire input buffer has been compressed so we need
  1721. // to output the last flag byte, provided it fits in the compressed buffer,
  1722. // set and store the chunk header. Now if the Flag pointer doesn't fit
  1723. // in the output buffer that is because it is one beyond the end and
  1724. // we incremented output pointer too far so now bring output pointer
  1725. // back down.
  1726. //
  1727. if (FlagPointer < EndOfCompressedChunkPlus1) {
  1728. *FlagPointer = FlagByte;
  1729. } else {
  1730. OutputPointer--;
  1731. }
  1732. *FinalCompressedChunkSize = (ULONG)(OutputPointer - CompressedBuffer);
  1733. SetCompressedChunkHeader( ChunkHeader,
  1734. (USHORT)*FinalCompressedChunkSize,
  1735. TRUE );
  1736. RtlStoreUshort( CompressedBuffer, ChunkHeader.Short );
  1737. //
  1738. // Now if the only literal we ever output was a null then the
  1739. // input buffer was all zeros.
  1740. //
  1741. if (!NullCharacter) {
  1742. return STATUS_BUFFER_ALL_ZEROS;
  1743. }
  1744. //
  1745. // Otherwise return to our caller
  1746. //
  1747. return STATUS_SUCCESS;
  1748. }
  1749. #if !defined(_X86_)
  1750. //
  1751. // Local support routine
  1752. //
  1753. NTSTATUS
  1754. LZNT1DecompressChunk (
  1755. OUT PUCHAR UncompressedBuffer,
  1756. IN PUCHAR EndOfUncompressedBufferPlus1,
  1757. IN PUCHAR CompressedBuffer,
  1758. IN PUCHAR EndOfCompressedBufferPlus1,
  1759. OUT PULONG FinalUncompressedChunkSize
  1760. )
  1761. /*++
  1762. Routine Description:
  1763. This routine takes as input a compressed chunk and produces its
  1764. uncompressed equivalent chunk provided the uncompressed data fits
  1765. within the specified destination buffer.
  1766. The compressed buffer must be stored in the LZNT1 format.
  1767. An output variable indicates the number of bytes used to store the
  1768. uncompressed data.
  1769. Arguments:
  1770. UncompressedBuffer - Supplies a pointer to where the uncompressed
  1771. chunk is to be stored.
  1772. EndOfUncompressedBufferPlus1 - Supplies a pointer to the next byte
  1773. following the end of the uncompressed buffer. This is supplied
  1774. instead of the size in bytes because our caller and ourselves
  1775. test against the pointer and by passing the pointer we get to
  1776. skip the code to compute it each time.
  1777. CompressedBuffer - Supplies a pointer to the compressed chunk. (This
  1778. pointer has already been adjusted to point past the chunk header.)
  1779. EndOfCompressedBufferPlus1 - Supplies a pointer to the next
  1780. byte following the end of the compressed buffer.
  1781. FinalUncompressedChunkSize - Receives the number of bytes needed in
  1782. the uncompressed buffer to store the uncompressed chunk.
  1783. Return Value:
  1784. STATUS_SUCCESS - the decompression worked without a hitch.
  1785. STATUS_BAD_COMPRESSION_BUFFER - the input compressed buffer is
  1786. ill-formed.
  1787. --*/
  1788. {
  1789. PUCHAR OutputPointer;
  1790. PUCHAR InputPointer;
  1791. UCHAR FlagByte;
  1792. ULONG FlagBit;
  1793. ULONG Format = FORMAT412;
  1794. //
  1795. // The two pointers will slide through our input and input buffer.
  1796. // For the input buffer we skip over the chunk header.
  1797. //
  1798. OutputPointer = UncompressedBuffer;
  1799. InputPointer = CompressedBuffer;
  1800. //
  1801. // The flag byte stores a copy of the flags for the current
  1802. // run and the flag bit denotes the current bit position within
  1803. // the flag that we are processing
  1804. //
  1805. FlagByte = *(InputPointer++);
  1806. FlagBit = 0;
  1807. //
  1808. // While we haven't exhausted either the input or output buffer
  1809. // we will do some more decompression
  1810. //
  1811. while ((OutputPointer < EndOfUncompressedBufferPlus1) && (InputPointer < EndOfCompressedBufferPlus1)) {
  1812. while (UncompressedBuffer + FormatMaxDisplacement[Format] < OutputPointer) { Format += 1; }
  1813. //
  1814. // Check the current flag if it is zero then the current
  1815. // input token is a literal byte that we simply copy over
  1816. // to the output buffer
  1817. //
  1818. if (!FlagOn(FlagByte, (1 << FlagBit))) {
  1819. *(OutputPointer++) = *(InputPointer++);
  1820. } else {
  1821. LZNT1_COPY_TOKEN CopyToken;
  1822. LONG Displacement;
  1823. LONG Length;
  1824. //
  1825. // The current input is a copy token so we'll get the
  1826. // copy token into our variable and extract the
  1827. // length and displacement from the token
  1828. //
  1829. if (InputPointer+1 >= EndOfCompressedBufferPlus1) {
  1830. *FinalUncompressedChunkSize = PtrToUlong(InputPointer);
  1831. return STATUS_BAD_COMPRESSION_BUFFER;
  1832. }
  1833. //
  1834. // Now grab the next input byte and extract the
  1835. // length and displacement from the copy token
  1836. //
  1837. CopyToken.Bytes[0] = *(InputPointer++);
  1838. CopyToken.Bytes[1] = *(InputPointer++);
  1839. Displacement = GetLZNT1Displacement(Format, CopyToken);
  1840. Length = GetLZNT1Length(Format, CopyToken);
  1841. //
  1842. // At this point we have the length and displacement
  1843. // from the copy token, now we need to make sure that the
  1844. // displacement doesn't send us outside the uncompressed buffer
  1845. //
  1846. if (Displacement > (OutputPointer - UncompressedBuffer)) {
  1847. *FinalUncompressedChunkSize = PtrToUlong(InputPointer);
  1848. return STATUS_BAD_COMPRESSION_BUFFER;
  1849. }
  1850. //
  1851. // We also need to adjust the length to keep the copy from
  1852. // overflowing the output buffer
  1853. //
  1854. if ((OutputPointer + Length) >= EndOfUncompressedBufferPlus1) {
  1855. Length = (ULONG)(EndOfUncompressedBufferPlus1 - OutputPointer);
  1856. }
  1857. //
  1858. // Now we copy bytes. We cannot use Rtl Move Memory here because
  1859. // it does the copy backwards from what the LZ algorithm needs.
  1860. //
  1861. while (Length > 0) {
  1862. *(OutputPointer) = *(OutputPointer-Displacement);
  1863. Length -= 1;
  1864. OutputPointer += 1;
  1865. }
  1866. }
  1867. //
  1868. // Before we go back to the start of the loop we need to adjust the
  1869. // flag bit value (it goes from 0, 1, ... 7) and if the flag bit
  1870. // is back to zero we need to read in the next flag byte. In this
  1871. // case we are at the end of the input buffer we'll just break out
  1872. // of the loop because we're done.
  1873. //
  1874. FlagBit = (FlagBit + 1) % 8;
  1875. if (!FlagBit) {
  1876. if (InputPointer >= EndOfCompressedBufferPlus1) { break; }
  1877. FlagByte = *(InputPointer++);
  1878. }
  1879. }
  1880. //
  1881. // The decompression is done so now set the final uncompressed
  1882. // chunk size and return success to our caller
  1883. //
  1884. *FinalUncompressedChunkSize = (ULONG)(OutputPointer - UncompressedBuffer);
  1885. return STATUS_SUCCESS;
  1886. }
  1887. #endif // _X86_
  1888. //
  1889. // Local support routine
  1890. //
  1891. ULONG
  1892. LZNT1FindMatchStandard (
  1893. IN PUCHAR ZivString,
  1894. IN PLZNT1_STANDARD_WORKSPACE WorkSpace
  1895. )
  1896. /*++
  1897. Routine Description:
  1898. This routine does the compression lookup. It locates
  1899. a match for the ziv within a specified uncompressed buffer.
  1900. Arguments:
  1901. ZivString - Supplies a pointer to the Ziv in the uncompressed buffer.
  1902. The Ziv is the string we want to try and find a match for.
  1903. Return Value:
  1904. Returns the length of the match if the match is greater than three
  1905. characters otherwise return 0.
  1906. --*/
  1907. {
  1908. PUCHAR UncompressedBuffer = WorkSpace->UncompressedBuffer;
  1909. PUCHAR EndOfUncompressedBufferPlus1 = WorkSpace->EndOfUncompressedBufferPlus1;
  1910. ULONG MaxLength = WorkSpace->MaxLength;
  1911. ULONG Index;
  1912. PUCHAR FirstEntry;
  1913. ULONG FirstLength;
  1914. PUCHAR SecondEntry;
  1915. ULONG SecondLength;
  1916. //
  1917. // First check if the Ziv is within two bytes of the end of
  1918. // the uncompressed buffer, if so then we can't match
  1919. // three or more characters
  1920. //
  1921. Index = ((40543*((((ZivString[0]<<4)^ZivString[1])<<4)^ZivString[2]))>>4) & 0xfff;
  1922. FirstEntry = WorkSpace->IndexPTable[Index][0];
  1923. FirstLength = 0;
  1924. SecondEntry = WorkSpace->IndexPTable[Index][1];
  1925. SecondLength = 0;
  1926. //
  1927. // Check if first entry is good, and if so then get its length
  1928. //
  1929. if ((FirstEntry >= UncompressedBuffer) && // is it within the uncompressed buffer?
  1930. (FirstEntry < ZivString) &&
  1931. (FirstEntry[0] == ZivString[0]) && // do at least 3 characters match?
  1932. (FirstEntry[1] == ZivString[1]) &&
  1933. (FirstEntry[2] == ZivString[2])) {
  1934. FirstLength = 3;
  1935. while ((FirstLength < MaxLength)
  1936. &&
  1937. (ZivString + FirstLength < EndOfUncompressedBufferPlus1)
  1938. &&
  1939. (ZivString[FirstLength] == FirstEntry[FirstLength])) {
  1940. FirstLength++;
  1941. }
  1942. }
  1943. //
  1944. // Check if second entry is good, and if so then get its length
  1945. //
  1946. if ((SecondEntry >= UncompressedBuffer) && // is it within the uncompressed buffer?
  1947. (SecondEntry < ZivString) &&
  1948. (SecondEntry[0] == ZivString[0]) && // do at least 3 characters match?
  1949. (SecondEntry[1] == ZivString[1]) &&
  1950. (SecondEntry[2] == ZivString[2])) {
  1951. SecondLength = 3;
  1952. while ((SecondLength < MaxLength)
  1953. &&
  1954. (ZivString + SecondLength< EndOfUncompressedBufferPlus1)
  1955. &&
  1956. (ZivString[SecondLength] == SecondEntry[SecondLength])) {
  1957. SecondLength++;
  1958. }
  1959. }
  1960. if ((FirstLength >= SecondLength)) {
  1961. WorkSpace->IndexPTable[Index][1] = FirstEntry;
  1962. WorkSpace->IndexPTable[Index][0] = ZivString;
  1963. WorkSpace->MatchedString = FirstEntry;
  1964. return FirstLength;
  1965. }
  1966. WorkSpace->IndexPTable[Index][1] = FirstEntry;
  1967. WorkSpace->IndexPTable[Index][0] = ZivString;
  1968. WorkSpace->MatchedString = SecondEntry;
  1969. return SecondLength;
  1970. }
  1971. //
  1972. // Local support routine
  1973. //
  1974. ULONG
  1975. LZNT1FindMatchMaximum (
  1976. IN PUCHAR ZivString,
  1977. IN PLZNT1_MAXIMUM_WORKSPACE WorkSpace
  1978. )
  1979. /*++
  1980. Routine Description:
  1981. This routine does the compression lookup. It locates
  1982. a match for the ziv within a specified uncompressed buffer.
  1983. If the matched string is two or more characters long then this
  1984. routine does not update the lookup state information.
  1985. Arguments:
  1986. ZivString - Supplies a pointer to the Ziv in the uncompressed buffer.
  1987. The Ziv is the string we want to try and find a match for.
  1988. Return Value:
  1989. Returns the length of the match if the match is greater than three
  1990. characters otherwise return 0.
  1991. --*/
  1992. {
  1993. PUCHAR UncompressedBuffer = WorkSpace->UncompressedBuffer;
  1994. PUCHAR EndOfUncompressedBufferPlus1 = WorkSpace->EndOfUncompressedBufferPlus1;
  1995. ULONG MaxLength = WorkSpace->MaxLength;
  1996. ULONG i;
  1997. ULONG BestMatchedLength;
  1998. PUCHAR q;
  1999. //
  2000. // First check if the Ziv is within two bytes of the end of
  2001. // the uncompressed buffer, if so then we can't match
  2002. // three or more characters
  2003. //
  2004. BestMatchedLength = 0;
  2005. for (q = UncompressedBuffer; q < ZivString; q += 1) {
  2006. i = 0;
  2007. while ((i < MaxLength)
  2008. &&
  2009. (ZivString + i < EndOfUncompressedBufferPlus1)
  2010. &&
  2011. (ZivString[i] == q[i])) {
  2012. i++;
  2013. }
  2014. if (i >= BestMatchedLength) {
  2015. BestMatchedLength = i;
  2016. WorkSpace->MatchedString = q;
  2017. }
  2018. }
  2019. if (BestMatchedLength < 3) {
  2020. return 0;
  2021. } else {
  2022. return BestMatchedLength;
  2023. }
  2024. }
  2025. #if defined(ALLOC_DATA_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
  2026. #pragma const_seg()
  2027. #endif