2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * PURPOSE: Compression and decompression functions
5 * FILE: lib/rtl/compress.c
11 /* INCLUDES *****************************************************************/
18 /* MACROS *******************************************************************/
20 #define COMPRESSION_FORMAT_MASK 0x00FF
21 #define COMPRESSION_ENGINE_MASK 0xFF00
26 /* FUNCTIONS ****************************************************************/
28 /* Based on Wine Staging */
30 /* decompress a single LZNT1 chunk */
31 static PUCHAR
lznt1_decompress_chunk(UCHAR
*dst
, ULONG dst_size
, UCHAR
*src
, ULONG src_size
)
33 UCHAR
*src_cur
, *src_end
, *dst_cur
, *dst_end
;
34 ULONG displacement_bits
, length_bits
;
35 ULONG code_displacement
, code_length
;
39 src_end
= src
+ src_size
;
41 dst_end
= dst
+ dst_size
;
43 /* Partial decompression is no error on Windows. */
44 while (src_cur
< src_end
&& dst_cur
< dst_end
)
46 /* read flags header */
47 flags
= 0x8000 | *src_cur
++;
49 /* parse following 8 entities, either uncompressed data or backwards reference */
50 while ((flags
& 0xFF00) && src_cur
< src_end
)
54 /* backwards reference */
55 if (src_cur
+ sizeof(WORD
) > src_end
)
57 code
= *(WORD
*)src_cur
;
58 src_cur
+= sizeof(WORD
);
60 /* find length / displacement bits */
61 for (displacement_bits
= 12; displacement_bits
> 4; displacement_bits
--)
62 if ((1 << (displacement_bits
- 1)) < dst_cur
- dst
) break;
63 length_bits
= 16 - displacement_bits
;
64 code_length
= (code
& ((1 << length_bits
) - 1)) + 3;
65 code_displacement
= (code
>> length_bits
) + 1;
67 /* ensure reference is valid */
68 if (dst_cur
< dst
+ code_displacement
)
71 /* copy bytes of chunk - we can't use memcpy()
72 * since source and dest can be overlapping */
75 if (dst_cur
>= dst_end
) return dst_cur
;
76 *dst_cur
= *(dst_cur
- code_displacement
);
82 /* uncompressed data */
83 if (dst_cur
>= dst_end
) return dst_cur
;
84 *dst_cur
++ = *src_cur
++;
94 /* decompress data encoded with LZNT1 */
95 static NTSTATUS
lznt1_decompress(UCHAR
*dst
, ULONG dst_size
, UCHAR
*src
, ULONG src_size
,
96 ULONG offset
, ULONG
*final_size
, UCHAR
*workspace
)
98 UCHAR
*src_cur
= src
, *src_end
= src
+ src_size
;
99 UCHAR
*dst_cur
= dst
, *dst_end
= dst
+ dst_size
;
100 ULONG chunk_size
, block_size
;
104 if (src_cur
+ sizeof(WORD
) > src_end
)
105 return STATUS_BAD_COMPRESSION_BUFFER
;
107 /* skip over chunks which have a big distance (>= 0x1000) to the destination offset */
108 while (offset
>= 0x1000 && src_cur
+ sizeof(WORD
) <= src_end
)
110 /* read chunk header and extract size */
111 chunk_header
= *(WORD
*)src_cur
;
112 src_cur
+= sizeof(WORD
);
113 if (!chunk_header
) goto out
;
114 chunk_size
= (chunk_header
& 0xFFF) + 1;
116 /* ensure we have enough buffer to process chunk */
117 if (src_cur
+ chunk_size
> src_end
)
118 return STATUS_BAD_COMPRESSION_BUFFER
;
120 src_cur
+= chunk_size
;
124 /* this chunk is can be included partially */
125 if (offset
&& src_cur
+ sizeof(WORD
) <= src_end
)
127 /* read chunk header and extract size */
128 chunk_header
= *(WORD
*)src_cur
;
129 src_cur
+= sizeof(WORD
);
130 if (!chunk_header
) goto out
;
131 chunk_size
= (chunk_header
& 0xFFF) + 1;
133 /* ensure we have enough buffer to process chunk */
134 if (src_cur
+ chunk_size
> src_end
)
135 return STATUS_BAD_COMPRESSION_BUFFER
;
137 if (dst_cur
>= dst_end
)
140 if (chunk_header
& 0x8000)
142 /* compressed chunk */
143 if (!workspace
) return STATUS_ACCESS_VIOLATION
;
144 ptr
= lznt1_decompress_chunk(workspace
, 0x1000, src_cur
, chunk_size
);
145 if (!ptr
) return STATUS_BAD_COMPRESSION_BUFFER
;
146 if (ptr
- workspace
> offset
)
148 block_size
= min((ptr
- workspace
) - offset
, dst_end
- dst_cur
);
149 memcpy(dst_cur
, workspace
+ offset
, block_size
);
150 dst_cur
+= block_size
;
155 /* uncompressed chunk */
156 if (chunk_size
> offset
)
158 block_size
= min(chunk_size
- offset
, dst_end
- dst_cur
);
159 memcpy(dst_cur
, src_cur
+ offset
, block_size
);
160 dst_cur
+= block_size
;
164 src_cur
+= chunk_size
;
167 /* handle remaining chunks */
168 while (src_cur
+ sizeof(WORD
) <= src_end
)
170 /* read chunk header and extract size */
171 chunk_header
= *(WORD
*)src_cur
;
172 src_cur
+= sizeof(WORD
);
173 if (!chunk_header
) goto out
;
174 chunk_size
= (chunk_header
& 0xFFF) + 1;
176 if (src_cur
+ chunk_size
> src_end
)
177 return STATUS_BAD_COMPRESSION_BUFFER
;
179 /* add padding if required */
180 block_size
= ((dst_cur
- dst
) + offset
) & 0xFFF;
183 block_size
= 0x1000 - block_size
;
184 if (dst_cur
+ block_size
>= dst_end
)
186 memset(dst_cur
, 0, block_size
);
187 dst_cur
+= block_size
;
190 if (dst_cur
>= dst_end
)
193 if (chunk_header
& 0x8000)
195 /* compressed chunk */
196 dst_cur
= lznt1_decompress_chunk(dst_cur
, dst_end
- dst_cur
, src_cur
, chunk_size
);
197 if (!dst_cur
) return STATUS_BAD_COMPRESSION_BUFFER
;
201 /* uncompressed chunk */
202 block_size
= min(chunk_size
, dst_end
- dst_cur
);
203 memcpy(dst_cur
, src_cur
, block_size
);
204 dst_cur
+= block_size
;
207 src_cur
+= chunk_size
;
212 *final_size
= dst_cur
- dst
;
214 return STATUS_SUCCESS
;
220 RtlpCompressBufferLZNT1(UCHAR
*src
, ULONG src_size
, UCHAR
*dst
, ULONG dst_size
,
221 ULONG chunk_size
, ULONG
*final_size
, UCHAR
*workspace
)
223 UCHAR
*src_cur
= src
, *src_end
= src
+ src_size
;
224 UCHAR
*dst_cur
= dst
, *dst_end
= dst
+ dst_size
;
227 while (src_cur
< src_end
)
229 /* determine size of current chunk */
230 block_size
= min(0x1000, src_end
- src_cur
);
231 if (dst_cur
+ sizeof(WORD
) + block_size
> dst_end
)
232 return STATUS_BUFFER_TOO_SMALL
;
234 /* write (uncompressed) chunk header */
235 *(WORD
*)dst_cur
= 0x3000 | (block_size
- 1);
236 dst_cur
+= sizeof(WORD
);
238 /* write chunk content */
239 memcpy(dst_cur
, src_cur
, block_size
);
240 dst_cur
+= block_size
;
241 src_cur
+= block_size
;
245 *final_size
= dst_cur
- dst
;
247 return STATUS_SUCCESS
;
252 RtlpWorkSpaceSizeLZNT1(USHORT Engine
,
253 PULONG BufferAndWorkSpaceSize
,
254 PULONG FragmentWorkSpaceSize
)
256 if (Engine
== COMPRESSION_ENGINE_STANDARD
)
258 *BufferAndWorkSpaceSize
= 0x8010;
259 *FragmentWorkSpaceSize
= 0x1000;
260 return(STATUS_SUCCESS
);
262 else if (Engine
== COMPRESSION_ENGINE_MAXIMUM
)
264 *BufferAndWorkSpaceSize
= 0x10;
265 *FragmentWorkSpaceSize
= 0x1000;
266 return(STATUS_SUCCESS
);
269 return(STATUS_NOT_SUPPORTED
);
277 RtlCompressBuffer(IN USHORT CompressionFormatAndEngine
,
278 IN PUCHAR UncompressedBuffer
,
279 IN ULONG UncompressedBufferSize
,
280 OUT PUCHAR CompressedBuffer
,
281 IN ULONG CompressedBufferSize
,
282 IN ULONG UncompressedChunkSize
,
283 OUT PULONG FinalCompressedSize
,
286 USHORT Format
= CompressionFormatAndEngine
& COMPRESSION_FORMAT_MASK
;
287 /* USHORT Engine = CompressionFormatAndEngine & COMPRESSION_ENGINE_MASK; */
289 if ((Format
== COMPRESSION_FORMAT_NONE
) ||
290 (Format
== COMPRESSION_FORMAT_DEFAULT
))
291 return(STATUS_INVALID_PARAMETER
);
293 if (Format
== COMPRESSION_FORMAT_LZNT1
)
294 return(RtlpCompressBufferLZNT1(UncompressedBuffer
,
295 UncompressedBufferSize
,
297 CompressedBufferSize
,
298 UncompressedChunkSize
,
302 return(STATUS_UNSUPPORTED_COMPRESSION
);
310 RtlCompressChunks(IN PUCHAR UncompressedBuffer
,
311 IN ULONG UncompressedBufferSize
,
312 OUT PUCHAR CompressedBuffer
,
313 IN ULONG CompressedBufferSize
,
314 IN OUT PCOMPRESSED_DATA_INFO CompressedDataInfo
,
315 IN ULONG CompressedDataInfoLength
,
319 return STATUS_NOT_IMPLEMENTED
;
326 RtlDecompressChunks(OUT PUCHAR UncompressedBuffer
,
327 IN ULONG UncompressedBufferSize
,
328 IN PUCHAR CompressedBuffer
,
329 IN ULONG CompressedBufferSize
,
330 IN PUCHAR CompressedTail
,
331 IN ULONG CompressedTailSize
,
332 IN PCOMPRESSED_DATA_INFO CompressedDataInfo
)
335 return STATUS_NOT_IMPLEMENTED
;
342 RtlDecompressFragment(IN USHORT format
,
343 OUT PUCHAR uncompressed
,
344 IN ULONG uncompressed_size
,
345 IN PUCHAR compressed
,
346 IN ULONG compressed_size
,
348 OUT PULONG final_size
,
351 DPRINT("0x%04x, %p, %u, %p, %u, %u, %p, %p :stub\n", format
, uncompressed
,
352 uncompressed_size
, compressed
, compressed_size
, offset
, final_size
, workspace
);
354 switch (format
& ~COMPRESSION_ENGINE_MAXIMUM
)
356 case COMPRESSION_FORMAT_LZNT1
:
357 return lznt1_decompress(uncompressed
, uncompressed_size
, compressed
,
358 compressed_size
, offset
, final_size
, workspace
);
360 case COMPRESSION_FORMAT_NONE
:
361 case COMPRESSION_FORMAT_DEFAULT
:
362 return STATUS_INVALID_PARAMETER
;
365 DPRINT1("format %d not implemented\n", format
);
366 return STATUS_UNSUPPORTED_COMPRESSION
;
374 RtlDecompressBuffer(IN USHORT CompressionFormat
,
375 OUT PUCHAR UncompressedBuffer
,
376 IN ULONG UncompressedBufferSize
,
377 IN PUCHAR CompressedBuffer
,
378 IN ULONG CompressedBufferSize
,
379 OUT PULONG FinalUncompressedSize
)
381 return RtlDecompressFragment(CompressionFormat
, UncompressedBuffer
, UncompressedBufferSize
,
382 CompressedBuffer
, CompressedBufferSize
, 0, FinalUncompressedSize
, NULL
);
389 RtlDescribeChunk(IN USHORT CompressionFormat
,
390 IN OUT PUCHAR
*CompressedBuffer
,
391 IN PUCHAR EndOfCompressedBufferPlus1
,
392 OUT PUCHAR
*ChunkBuffer
,
393 OUT PULONG ChunkSize
)
396 return STATUS_NOT_IMPLEMENTED
;
404 RtlGetCompressionWorkSpaceSize(IN USHORT CompressionFormatAndEngine
,
405 OUT PULONG CompressBufferAndWorkSpaceSize
,
406 OUT PULONG CompressFragmentWorkSpaceSize
)
408 USHORT Format
= CompressionFormatAndEngine
& COMPRESSION_FORMAT_MASK
;
409 USHORT Engine
= CompressionFormatAndEngine
& COMPRESSION_ENGINE_MASK
;
411 if ((Format
== COMPRESSION_FORMAT_NONE
) ||
412 (Format
== COMPRESSION_FORMAT_DEFAULT
))
413 return(STATUS_INVALID_PARAMETER
);
415 if (Format
== COMPRESSION_FORMAT_LZNT1
)
416 return(RtlpWorkSpaceSizeLZNT1(Engine
,
417 CompressBufferAndWorkSpaceSize
,
418 CompressFragmentWorkSpaceSize
));
420 return(STATUS_UNSUPPORTED_COMPRESSION
);
429 RtlReserveChunk(IN USHORT CompressionFormat
,
430 IN OUT PUCHAR
*CompressedBuffer
,
431 IN PUCHAR EndOfCompressedBufferPlus1
,
432 OUT PUCHAR
*ChunkBuffer
,
436 return STATUS_NOT_IMPLEMENTED
;