25#import "OFDeflateStream.h"
26#ifdef OF_DEFLATE64_STREAM_M
27# import "OFDeflate64Stream.h"
28# define OFDeflateStream OFDeflate64Stream
30#import "OFHuffmanTree.h"
32#import "OFInitializationFailedException.h"
33#import "OFInvalidArgumentException.h"
34#import "OFInvalidFormatException.h"
35#import "OFNotImplementedException.h"
36#import "OFNotOpenException.h"
37#import "OFOutOfMemoryException.h"
41 stateUncompressedBlockHeader,
42 stateUncompressedBlock,
48 huffmanStateWriteValue,
49 huffmanStateAwaitCode,
50 huffmanStateAwaitLengthExtraBits,
51 huffmanStateAwaitDistance,
52 huffmanStateAwaitDistanceExtraBits,
53 huffmanStateProcessPair
56#ifndef OF_DEFLATE64_STREAM_M
57static const uint16_t slidingWindowMask = 0x7FFF;
58static const uint8_t numDistanceCodes = 30;
59static const uint8_t lengthCodes[29] = {
61 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56,
62 64, 80, 96, 112, 128, 160, 192, 224, 255
64static const uint8_t lengthExtraBits[29] = {
65 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4,
68static const uint16_t distanceCodes[30] = {
69 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385,
70 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577
72static const uint8_t distanceExtraBits[30] = {
73 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10,
74 10, 11, 11, 12, 12, 13, 13
77static const uint16_t slidingWindowMask = 0xFFFF;
78static const uint8_t numDistanceCodes = 32;
79static const uint8_t lengthCodes[29] = {
81 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56,
82 64, 80, 96, 112, 128, 160, 192, 224, 0
84static const uint8_t lengthExtraBits[29] = {
85 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4,
88static const uint16_t distanceCodes[32] = {
89 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385,
90 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577,
93static const uint8_t distanceExtraBits[32] = {
94 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10,
95 10, 11, 11, 12, 12, 13, 13, 14, 14
98static const uint8_t codeLengthsOrder[19] = {
99 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
101static OFHuffmanTree fixedLitLenTree, fixedDistTree;
109 struct OFInflateContext *ctx = stream->_inflateCtx;
110 uint16_t ret = ctx->savedBits;
112 OFAssert(ctx->savedBitsLength < count);
114 for (uint_fast8_t i = ctx->savedBitsLength; i < count; i++) {
115 if OF_UNLIKELY (ctx->bitIndex == 8) {
116 if OF_LIKELY (ctx->bufferIndex < ctx->bufferLength)
117 ctx->
byte = ctx->buffer[ctx->bufferIndex++];
119 size_t length = [stream->_stream
121 length: OFInflateStreamBufferSize];
123 if OF_UNLIKELY (length < 1) {
124 ctx->savedBits = ret;
125 ctx->savedBitsLength = i;
129 ctx->byte = ctx->buffer[0];
130 ctx->bufferIndex = 1;
131 ctx->bufferLength = (uint16_t)length;
137 ret |= ((ctx->byte >> ctx->bitIndex++) & 1) << i;
141 ctx->savedBitsLength = 0;
149 uint8_t lengths[288];
151 if (
self != [OFDeflateStream
class])
154 for (uint16_t i = 0; i <= 143; i++)
156 for (uint16_t i = 144; i <= 255; i++)
158 for (uint16_t i = 256; i <= 279; i++)
160 for (uint16_t i = 280; i <= 287; i++)
163 fixedLitLenTree = _OFHuffmanTreeNew(lengths, 288);
165 for (uint16_t i = 0; i <= 31; i++)
168 fixedDistTree = _OFHuffmanTreeNew(lengths, 32);
171+ (instancetype)streamWithStream: (
OFStream *)stream
173 return objc_autoreleaseReturnValue(
174 [[
self alloc] initWithStream: stream]);
179 return objc_autoreleaseReturnValue(
180 [[
self alloc] initWithStream: stream
186 OF_INVALID_INIT_METHOD
199 _stream = objc_retain(stream);
202 if ([mode isEqual:
@"r"]) {
204 sizeof(*_inflateCtx));
207 _inflateCtx->bitIndex = 8;
208 }
else if ([mode isEqual:
@"w"])
229 if (_inflateCtx != NULL) {
230 if (_inflateCtx->state == stateHuffmanTree) {
233 if (_inflateCtx->ctx.huffmanTree.codeLenTree != NULL)
235 _inflateCtx->ctx.huffmanTree.codeLenTree);
238 if (_inflateCtx->state == stateHuffmanTree ||
239 _inflateCtx->state == stateHuffmanBlock) {
240 if (_inflateCtx->ctx.huffman.litLenTree !=
243 _inflateCtx->ctx.huffman.litLenTree);
244 if (_inflateCtx->ctx.huffman.distTree != fixedDistTree)
246 _inflateCtx->ctx.huffman.distTree);
255- (size_t)lowlevelReadIntoBuffer: (
void *)buffer_
256 length: (
size_t)length
258 unsigned char *buffer = buffer_;
259 uint16_t bits = 0, tmp, value = 0;
260 size_t bytesWritten = 0;
261 unsigned char *slidingWindow;
262 uint16_t slidingWindowIndex;
263 struct OFInflateContext *ctx;
265 if (_stream ==
nil || _inflateCtx == NULL)
274 switch ((
enum State)ctx->state) {
275 case stateBlockHeader:
276 if OF_UNLIKELY (ctx->inLastBlock) {
277 [_stream unreadFromBuffer: ctx->buffer +
279 length: ctx->bufferLength -
281 ctx->bufferIndex = ctx->bufferLength = 0;
283 _atEndOfStream =
true;
287 if OF_UNLIKELY (!tryReadBits(
self, &bits, 3))
290 ctx->inLastBlock = (bits & 1);
294 ctx->state = stateUncompressedBlockHeader;
296 ctx->ctx.uncompressedHeader.position = 0;
297 memset(ctx->ctx.uncompressedHeader.length, 0, 4);
300 ctx->state = stateHuffmanBlock;
301 ctx->ctx.huffman.state = huffmanStateAwaitCode;
302 ctx->ctx.huffman.litLenTree = fixedLitLenTree;
303 ctx->ctx.huffman.distTree = fixedDistTree;
304 ctx->ctx.huffman.treeIter = fixedLitLenTree;
307 ctx->state = stateHuffmanTree;
308 ctx->ctx.huffmanTree.lengths = NULL;
309 ctx->ctx.huffmanTree.receivedCount = 0;
310 ctx->ctx.huffmanTree.value = 0xFE;
311 ctx->ctx.huffmanTree.litLenCodesCount = 0xFF;
312 ctx->ctx.huffmanTree.distCodesCount = 0xFF;
313 ctx->ctx.huffmanTree.codeLenCodesCount = 0xFF;
320 case stateUncompressedBlockHeader:
321#define CTX ctx->ctx.uncompressedHeader
323 [_stream unreadFromBuffer: ctx->buffer + ctx->bufferIndex
324 length: ctx->bufferLength -
326 ctx->bufferIndex = ctx->bufferLength = 0;
328 CTX.position += [_stream
329 readIntoBuffer: CTX.length + CTX.position
330 length: 4 - CTX.position];
332 if OF_UNLIKELY (CTX.position < 4)
335 if OF_UNLIKELY ((CTX.length[0] | (CTX.length[1] << 8)) !=
336 (uint16_t)~(CTX.length[2] | (CTX.length[3] << 8)))
337 @throw [OFInvalidFormatException exception];
339 ctx->state = stateUncompressedBlock;
345 ctx->ctx.uncompressed.length =
346 CTX.length[0] | (CTX.length[1] << 8);
347 ctx->ctx.uncompressed.position = 0;
351 case stateUncompressedBlock:
352#define CTX ctx->ctx.uncompressed
353 if OF_UNLIKELY (length == 0)
356 tmp = (length < (
size_t)CTX.length - CTX.position
357 ? (uint16_t)length : CTX.length - CTX.position);
359 tmp = (uint16_t)[_stream readIntoBuffer: buffer + bytesWritten
362 if OF_UNLIKELY (tmp == 0)
365 slidingWindow = _slidingWindow;
366 slidingWindowIndex = _slidingWindowIndex;
367 for (uint_fast16_t i = 0; i < tmp; i++) {
368 slidingWindow[slidingWindowIndex] =
369 buffer[bytesWritten + i];
370 slidingWindowIndex = (slidingWindowIndex + 1) &
373 _slidingWindowIndex = slidingWindowIndex;
379 if OF_UNLIKELY (CTX.position == CTX.length)
380 ctx->state = stateBlockHeader;
384 case stateHuffmanTree:
385#define CTX ctx->ctx.huffmanTree
386 if OF_LIKELY (CTX.value == 0xFE) {
387 if OF_LIKELY (CTX.litLenCodesCount == 0xFF) {
388 if OF_UNLIKELY (!tryReadBits(
self, &bits, 5))
391 if OF_UNLIKELY (bits > 29)
392 @throw [OFInvalidFormatException
395 CTX.litLenCodesCount = bits;
398 if OF_LIKELY (CTX.distCodesCount == 0xFF) {
399 if OF_UNLIKELY (!tryReadBits(
self, &bits, 5))
402 CTX.distCodesCount = bits;
405 if OF_LIKELY (CTX.codeLenCodesCount == 0xFF) {
406 if OF_UNLIKELY (!tryReadBits(
self, &bits, 4))
409 CTX.codeLenCodesCount = bits;
412 if OF_LIKELY (CTX.lengths == NULL)
415 for (uint16_t i = CTX.receivedCount;
416 i < CTX.codeLenCodesCount + 4; i++) {
417 if OF_UNLIKELY (!tryReadBits(
self, &bits, 3)) {
418 CTX.receivedCount = i;
422 CTX.lengths[codeLengthsOrder[i]] = bits;
425 CTX.codeLenTree = _OFHuffmanTreeNew(CTX.lengths, 19);
426 CTX.treeIter = CTX.codeLenTree;
430 CTX.receivedCount = 0;
434 if OF_LIKELY (CTX.lengths == NULL)
436 CTX.litLenCodesCount + CTX.distCodesCount + 258, 1);
438 for (uint16_t i = CTX.receivedCount;
439 i < CTX.litLenCodesCount + CTX.distCodesCount + 258;) {
442 if OF_LIKELY (CTX.value == 0xFF) {
443 if OF_UNLIKELY (!_OFHuffmanTreeWalk(
self,
444 tryReadBits, &CTX.treeIter, &value)) {
445 CTX.receivedCount = i;
449 CTX.treeIter = CTX.codeLenTree;
452 CTX.lengths[i++] = value;
460 if OF_UNLIKELY (i < 1)
464 if OF_UNLIKELY (!tryReadBits(
self, &bits, 2)) {
465 CTX.receivedCount = i;
470 value = CTX.lengths[i - 1];
475 if OF_UNLIKELY (!tryReadBits(
self, &bits, 3)) {
476 CTX.receivedCount = i;
486 if OF_UNLIKELY (!tryReadBits(
self, &bits, 7)) {
487 CTX.receivedCount = i;
500 if OF_UNLIKELY (i + count >
501 CTX.litLenCodesCount + CTX.distCodesCount + 258)
504 for (j = 0; j < count; j++)
505 CTX.lengths[i++] = value;
510 _OFHuffmanTreeFree(CTX.codeLenTree);
511 CTX.codeLenTree = NULL;
513 CTX.litLenTree = _OFHuffmanTreeNew(CTX.lengths,
514 CTX.litLenCodesCount + 257);
515 CTX.distTree = _OFHuffmanTreeNew(
516 CTX.lengths + CTX.litLenCodesCount + 257,
517 CTX.distCodesCount + 1);
526 ctx->state = stateHuffmanBlock;
527 ctx->ctx.huffman.state = huffmanStateAwaitCode;
528 ctx->ctx.huffman.treeIter = CTX.litLenTree;
532 case stateHuffmanBlock:
533#define CTX ctx->ctx.huffman
535 uint8_t extraBits, lengthCodeIndex;
537 if OF_UNLIKELY (CTX.state == huffmanStateWriteValue) {
538 if OF_UNLIKELY (length == 0)
541 buffer[bytesWritten++] = CTX.value;
544 _slidingWindow[_slidingWindowIndex] = CTX.value;
545 _slidingWindowIndex =
546 (_slidingWindowIndex + 1) &
549 CTX.state = huffmanStateAwaitCode;
550 CTX.treeIter = CTX.litLenTree;
553 if OF_UNLIKELY (CTX.state ==
554 huffmanStateAwaitLengthExtraBits) {
555 if OF_UNLIKELY (!tryReadBits(
self, &bits,
561 CTX.state = huffmanStateAwaitDistance;
562 CTX.treeIter = CTX.distTree;
566 if (CTX.state == huffmanStateAwaitDistance) {
567 if OF_UNLIKELY (!_OFHuffmanTreeWalk(
self,
568 tryReadBits, &CTX.treeIter, &value))
571 if OF_UNLIKELY (value >= numDistanceCodes)
575 CTX.distance = distanceCodes[value];
576 extraBits = distanceExtraBits[value];
579 if OF_UNLIKELY (!tryReadBits(
self,
581#define HSADEB huffmanStateAwaitDistanceExtraBits
584 CTX.extraBits = extraBits;
588 CTX.distance += bits;
591 CTX.state = huffmanStateProcessPair;
592 }
else if (CTX.state ==
593 huffmanStateAwaitDistanceExtraBits) {
594 if OF_UNLIKELY (!tryReadBits(
self, &bits,
598 CTX.distance += bits;
600 CTX.state = huffmanStateProcessPair;
604 if (CTX.state == huffmanStateProcessPair) {
605 for (uint_fast16_t j = 0; j < CTX.length; j++) {
608 if OF_UNLIKELY (length == 0) {
613 idx = (_slidingWindowIndex -
614 CTX.distance) & slidingWindowMask;
615 value = _slidingWindow[idx];
617 buffer[bytesWritten++] = value;
620 _slidingWindow[_slidingWindowIndex] =
622 _slidingWindowIndex =
623 (_slidingWindowIndex + 1) &
627 CTX.state = huffmanStateAwaitCode;
628 CTX.treeIter = CTX.litLenTree;
631 if OF_UNLIKELY (!_OFHuffmanTreeWalk(
self, tryReadBits,
632 &CTX.treeIter, &value))
636 if OF_UNLIKELY (value == 256) {
637 if (CTX.litLenTree != fixedLitLenTree)
638 _OFHuffmanTreeFree(CTX.litLenTree);
639 if (CTX.distTree != fixedDistTree)
640 _OFHuffmanTreeFree(CTX.distTree);
642 ctx->state = stateBlockHeader;
647 if OF_LIKELY (value < 256) {
648 if OF_UNLIKELY (length == 0) {
649 CTX.state = huffmanStateWriteValue;
654 buffer[bytesWritten++] = value;
657 _slidingWindow[_slidingWindowIndex] = value;
658 _slidingWindowIndex =
659 (_slidingWindowIndex + 1) &
662 CTX.treeIter = CTX.litLenTree;
666 if OF_UNLIKELY (value > 285)
670 lengthCodeIndex = value - 257;
671 CTX.length = lengthCodes[lengthCodeIndex] + 3;
672 extraBits = lengthExtraBits[lengthCodeIndex];
675 if OF_UNLIKELY (!tryReadBits(
self, &bits,
677 CTX.extraBits = extraBits;
679 huffmanStateAwaitLengthExtraBits;
686 CTX.treeIter = CTX.distTree;
687 CTX.state = huffmanStateAwaitDistance;
697- (bool)lowlevelIsAtEndOfStream
702 return _atEndOfStream;
705- (int)fileDescriptorForReading
707 return ((id <OFReadyForReadingObserving>)_stream)
708 .fileDescriptorForReading;
711- (bool)lowlevelHasDataInReadBuffer
713 return (_stream.hasDataInReadBuffer || (_inflateCtx != NULL &&
714 _inflateCtx->bufferLength - _inflateCtx->bufferIndex > 0));
722 if (_inflateCtx != NULL) {
724 [_stream unreadFromBuffer: _inflateCtx->buffer +
725 _inflateCtx->bufferIndex
726 length: _inflateCtx->bufferLength -
727 _inflateCtx->bufferIndex];
728 _inflateCtx->bufferIndex = _inflateCtx->bufferLength = 0;
731 objc_release(_stream);
void * OFAllocZeroedMemory(size_t count, size_t size) OF_MALLOC_FUNC
Allocates memory for the specified number of items of the specified size and initializes it with zero...
Definition OFObject.m:119
void OFFreeMemory(void *pointer)
Frees memory allocated by OFAllocMemory, OFAllocZeroedMemory or OFResizeMemory.
Definition OFObject.m:156
void * OFAllocMemory(size_t count, size_t size) OF_MALLOC_FUNC
Allocates memory for the specified number of items of the specified size.
Definition OFObject.m:101
#define nil
A value representing no object.
Definition ObjFWRT.h:68
A class that handles Deflate decompression transparently for an underlying stream.
Definition OFDeflateStream.h:38
OFStream * underlyingStream
The underlying stream of the deflate stream.
Definition OFDeflateStream.h:87
instancetype initWithStream:mode:(OFStream *stream,[mode] OFString *mode)
Initializes an already allocated OFDeflateStream with the specified underlying stream.
Definition OFDeflateStream.m:194
instancetype exception()
Creates a new, autoreleased exception.
Definition OFException.m:283
An exception indicating that the argument is invalid for this method.
Definition OFInvalidArgumentException.h:30
An exception indicating that a method or part of it is not implemented.
Definition OFNotImplementedException.h:31
instancetype exceptionWithSelector:object:(SEL selector,[object] nullable id object)
Creates a new, autoreleased not implemented exception.
Definition OFNotImplementedException.m:33
An exception indicating an object is not open, connected or bound.
Definition OFNotOpenException.h:30
instancetype exceptionWithObject:(id object)
Creates a new, autoreleased not open exception.
Definition OFNotOpenException.m:33
instancetype init()
Initializes an already allocated object.
Definition OFObject.m:671
instancetype alloc()
Allocates memory for an instance of the class and sets up the memory pool for the object.
Definition OFObject.m:515
A base class for different types of streams.
Definition OFStream.h:280
size_t readIntoBuffer:length:(void *buffer,[length] size_t length)
Reads at most length bytes from the stream into a buffer.
Definition OFStream.m:135
A class for handling strings.
Definition OFString.h:143