ObjFW
Loading...
Searching...
No Matches
OFDeflateStream.m
1/*
2 * Copyright (c) 2008-2026 Jonathan Schleifer <js@nil.im>
3 *
4 * All rights reserved.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License version 3.0 only,
8 * as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
13 * version 3.0 for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * version 3.0 along with this program. If not, see
17 * <https://www.gnu.org/licenses/>.
18 */
19
20#include "config.h"
21
22#include <stdlib.h>
23#include <string.h>
24
25#import "OFDeflateStream.h"
26#ifdef OF_DEFLATE64_STREAM_M
27# import "OFDeflate64Stream.h"
28# define OFDeflateStream OFDeflate64Stream
29#endif
30#import "OFHuffmanTree.h"
31
32#import "OFInitializationFailedException.h"
33#import "OFInvalidArgumentException.h"
34#import "OFInvalidFormatException.h"
35#import "OFNotImplementedException.h"
36#import "OFNotOpenException.h"
37#import "OFOutOfMemoryException.h"
38
39enum State {
40 stateBlockHeader,
41 stateUncompressedBlockHeader,
42 stateUncompressedBlock,
43 stateHuffmanTree,
44 stateHuffmanBlock
45};
46
47enum HuffmanState {
48 huffmanStateWriteValue,
49 huffmanStateAwaitCode,
50 huffmanStateAwaitLengthExtraBits,
51 huffmanStateAwaitDistance,
52 huffmanStateAwaitDistanceExtraBits,
53 huffmanStateProcessPair
54};
55
56#ifndef OF_DEFLATE64_STREAM_M
57static const uint16_t slidingWindowMask = 0x7FFF;
58static const uint8_t numDistanceCodes = 30;
59static const uint8_t lengthCodes[29] = {
60 /* indices are -257, values -3 */
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
63};
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,
66 5, 5, 5, 5, 0
67};
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
71};
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
75};
76#else
77static const uint16_t slidingWindowMask = 0xFFFF;
78static const uint8_t numDistanceCodes = 32;
79static const uint8_t lengthCodes[29] = {
80 /* indices are -257, values -3 */
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
83};
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,
86 5, 5, 5, 5, 16
87};
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,
91 32769, 49153
92};
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
96};
97#endif
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
100};
101static OFHuffmanTree fixedLitLenTree, fixedDistTree;
102
103@implementation OFDeflateStream
104@synthesize underlyingStream = _stream;
105
106static OF_INLINE bool
107tryReadBits(OFDeflateStream *stream, uint16_t *bits, uint8_t count)
108{
109 struct OFInflateContext *ctx = stream->_inflateCtx;
110 uint16_t ret = ctx->savedBits;
111
112 OFAssert(ctx->savedBitsLength < count);
113
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++];
118 else {
119 size_t length = [stream->_stream
120 readIntoBuffer: ctx->buffer
121 length: OFInflateStreamBufferSize];
122
123 if OF_UNLIKELY (length < 1) {
124 ctx->savedBits = ret;
125 ctx->savedBitsLength = i;
126 return false;
127 }
128
129 ctx->byte = ctx->buffer[0];
130 ctx->bufferIndex = 1;
131 ctx->bufferLength = (uint16_t)length;
132 }
133
134 ctx->bitIndex = 0;
135 }
136
137 ret |= ((ctx->byte >> ctx->bitIndex++) & 1) << i;
138 }
139
140 ctx->savedBits = 0;
141 ctx->savedBitsLength = 0;
142 *bits = ret;
143
144 return true;
145}
146
147+ (void)initialize
148{
149 uint8_t lengths[288];
150
151 if (self != [OFDeflateStream class])
152 return;
153
154 for (uint16_t i = 0; i <= 143; i++)
155 lengths[i] = 8;
156 for (uint16_t i = 144; i <= 255; i++)
157 lengths[i] = 9;
158 for (uint16_t i = 256; i <= 279; i++)
159 lengths[i] = 7;
160 for (uint16_t i = 280; i <= 287; i++)
161 lengths[i] = 8;
162
163 fixedLitLenTree = _OFHuffmanTreeNew(lengths, 288);
164
165 for (uint16_t i = 0; i <= 31; i++)
166 lengths[i] = 5;
167
168 fixedDistTree = _OFHuffmanTreeNew(lengths, 32);
169}
170
171+ (instancetype)streamWithStream: (OFStream *)stream
172{
173 return objc_autoreleaseReturnValue(
174 [[self alloc] initWithStream: stream]);
175}
176
177+ (instancetype)streamWithStream: (OFStream *)stream mode: (OFString *)mode
178{
179 return objc_autoreleaseReturnValue(
180 [[self alloc] initWithStream: stream
181 mode: mode]);
182}
183
184- (instancetype)init
185{
186 OF_INVALID_INIT_METHOD
187}
188
189- (instancetype)initWithStream: (OFStream *)stream
190{
191 return [self initWithStream: stream mode: @"r"];
192}
193
194- (instancetype)initWithStream: (OFStream *)stream mode: (OFString *)mode
195{
196 self = [super init];
197
198 @try {
199 _stream = objc_retain(stream);
200 _slidingWindow = OFAllocZeroedMemory(slidingWindowMask + 1, 1);
201
202 if ([mode isEqual: @"r"]) {
203 _inflateCtx = OFAllocZeroedMemory(1,
204 sizeof(*_inflateCtx));
205
206 /* 0-7 address the bit, 8 means fetch next byte */
207 _inflateCtx->bitIndex = 8;
208 } else if ([mode isEqual: @"w"])
211 object: nil];
212 else
214 } @catch (id e) {
215 objc_release(self);
216 @throw e;
217 }
218
219 return self;
220}
221
222- (void)dealloc
223{
224 if (_stream != nil)
225 [self close];
226
227 OFFreeMemory(_slidingWindow);
228
229 if (_inflateCtx != NULL) {
230 if (_inflateCtx->state == stateHuffmanTree) {
231 OFFreeMemory(_inflateCtx->ctx.huffmanTree.lengths);
232
233 _OFHuffmanTreeFree(
234 _inflateCtx->ctx.huffmanTree.codeLenTree);
235 }
236
237 if (_inflateCtx->state == stateHuffmanTree ||
238 _inflateCtx->state == stateHuffmanBlock) {
239 if (_inflateCtx->ctx.huffman.litLenTree !=
240 fixedLitLenTree)
241 _OFHuffmanTreeFree(
242 _inflateCtx->ctx.huffman.litLenTree);
243 if (_inflateCtx->ctx.huffman.distTree != fixedDistTree)
244 _OFHuffmanTreeFree(
245 _inflateCtx->ctx.huffman.distTree);
246 }
247
248 OFFreeMemory(_inflateCtx);
249 }
250
251 [super dealloc];
252}
253
254- (size_t)lowlevelReadIntoBuffer: (void *)buffer_
255 length: (size_t)length
256{
257 unsigned char *buffer = buffer_;
258 uint16_t bits = 0, tmp, value = 0;
259 size_t bytesWritten = 0;
260 unsigned char *slidingWindow;
261 uint16_t slidingWindowIndex;
262 struct OFInflateContext *ctx;
263
264 if (_stream == nil || _inflateCtx == NULL)
266
267 if (_atEndOfStream)
268 return 0;
269
270 ctx = _inflateCtx;
271
272start:
273 switch ((enum State)ctx->state) {
274 case stateBlockHeader:
275 if OF_UNLIKELY (ctx->inLastBlock) {
276 [_stream unreadFromBuffer: ctx->buffer +
277 ctx->bufferIndex
278 length: ctx->bufferLength -
279 ctx->bufferIndex];
280 ctx->bufferIndex = ctx->bufferLength = 0;
281
282 _atEndOfStream = true;
283 return bytesWritten;
284 }
285
286 if OF_UNLIKELY (!tryReadBits(self, &bits, 3))
287 return bytesWritten;
288
289 ctx->inLastBlock = (bits & 1);
290
291 switch (bits >> 1) {
292 case 0: /* No compression */
293 ctx->state = stateUncompressedBlockHeader;
294 ctx->bitIndex = 8;
295 ctx->ctx.uncompressedHeader.position = 0;
296 memset(ctx->ctx.uncompressedHeader.length, 0, 4);
297 break;
298 case 1: /* Fixed Huffman */
299 ctx->state = stateHuffmanBlock;
300 ctx->ctx.huffman.state = huffmanStateAwaitCode;
301 ctx->ctx.huffman.litLenTree = fixedLitLenTree;
302 ctx->ctx.huffman.distTree = fixedDistTree;
303 ctx->ctx.huffman.treeIter = fixedLitLenTree;
304 break;
305 case 2: /* Dynamic Huffman */
306 ctx->state = stateHuffmanTree;
307 ctx->ctx.huffmanTree.lengths = NULL;
308 ctx->ctx.huffmanTree.receivedCount = 0;
309 ctx->ctx.huffmanTree.value = 0xFE;
310 ctx->ctx.huffmanTree.litLenCodesCount = 0xFF;
311 ctx->ctx.huffmanTree.distCodesCount = 0xFF;
312 ctx->ctx.huffmanTree.codeLenCodesCount = 0xFF;
313 break;
314 default:
316 }
317
318 goto start;
319 case stateUncompressedBlockHeader:
320#define CTX ctx->ctx.uncompressedHeader
321 /* FIXME: This can be done more efficiently than unreading */
322 [_stream unreadFromBuffer: ctx->buffer + ctx->bufferIndex
323 length: ctx->bufferLength -
324 ctx->bufferIndex];
325 ctx->bufferIndex = ctx->bufferLength = 0;
326
327 CTX.position += [_stream
328 readIntoBuffer: CTX.length + CTX.position
329 length: 4 - CTX.position];
330
331 if OF_UNLIKELY (CTX.position < 4)
332 return bytesWritten;
333
334 if OF_UNLIKELY ((CTX.length[0] | (CTX.length[1] << 8)) !=
335 (uint16_t)~(CTX.length[2] | (CTX.length[3] << 8)))
336 @throw [OFInvalidFormatException exception];
337
338 ctx->state = stateUncompressedBlock;
339
340 /*
341 * Do not reorder! _context.uncompressed.position and
342 * _context.uncompressedHeader.length overlap!
343 */
344 ctx->ctx.uncompressed.length =
345 CTX.length[0] | (CTX.length[1] << 8);
346 ctx->ctx.uncompressed.position = 0;
347
348 goto start;
349#undef CTX
350 case stateUncompressedBlock:
351#define CTX ctx->ctx.uncompressed
352 if OF_UNLIKELY (length == 0)
353 return bytesWritten;
354
355 tmp = (length < (size_t)CTX.length - CTX.position
356 ? (uint16_t)length : CTX.length - CTX.position);
357
358 tmp = (uint16_t)[_stream readIntoBuffer: buffer + bytesWritten
359 length: tmp];
360
361 if OF_UNLIKELY (tmp == 0)
362 return bytesWritten;
363
364 slidingWindow = _slidingWindow;
365 slidingWindowIndex = _slidingWindowIndex;
366 for (uint_fast16_t i = 0; i < tmp; i++) {
367 slidingWindow[slidingWindowIndex] =
368 buffer[bytesWritten + i];
369 slidingWindowIndex = (slidingWindowIndex + 1) &
370 slidingWindowMask;
371 }
372 _slidingWindowIndex = slidingWindowIndex;
373
374 length -= tmp;
375 bytesWritten += tmp;
376
377 CTX.position += tmp;
378 if OF_UNLIKELY (CTX.position == CTX.length)
379 ctx->state = stateBlockHeader;
380
381 goto start;
382#undef CTX
383 case stateHuffmanTree:
384#define CTX ctx->ctx.huffmanTree
385 if OF_LIKELY (CTX.value == 0xFE) {
386 if OF_LIKELY (CTX.litLenCodesCount == 0xFF) {
387 if OF_UNLIKELY (!tryReadBits(self, &bits, 5))
388 return bytesWritten;
389
390 if OF_UNLIKELY (bits > 29)
391 @throw [OFInvalidFormatException
392 exception];
393
394 CTX.litLenCodesCount = bits;
395 }
396
397 if OF_LIKELY (CTX.distCodesCount == 0xFF) {
398 if OF_UNLIKELY (!tryReadBits(self, &bits, 5))
399 return bytesWritten;
400
401 CTX.distCodesCount = bits;
402 }
403
404 if OF_LIKELY (CTX.codeLenCodesCount == 0xFF) {
405 if OF_UNLIKELY (!tryReadBits(self, &bits, 4))
406 return bytesWritten;
407
408 CTX.codeLenCodesCount = bits;
409 }
410
411 if OF_LIKELY (CTX.lengths == NULL)
412 CTX.lengths = OFAllocZeroedMemory(19, 1);
413
414 for (uint16_t i = CTX.receivedCount;
415 i < CTX.codeLenCodesCount + 4; i++) {
416 if OF_UNLIKELY (!tryReadBits(self, &bits, 3)) {
417 CTX.receivedCount = i;
418 return bytesWritten;
419 }
420
421 CTX.lengths[codeLengthsOrder[i]] = bits;
422 }
423
424 CTX.codeLenTree = _OFHuffmanTreeNew(CTX.lengths, 19);
425 CTX.treeIter = CTX.codeLenTree;
426
427 OFFreeMemory(CTX.lengths);
428 CTX.lengths = NULL;
429 CTX.receivedCount = 0;
430 CTX.value = 0xFF;
431 }
432
433 if OF_LIKELY (CTX.lengths == NULL)
434 CTX.lengths = OFAllocMemory(
435 CTX.litLenCodesCount + CTX.distCodesCount + 258, 1);
436
437 for (uint16_t i = CTX.receivedCount;
438 i < CTX.litLenCodesCount + CTX.distCodesCount + 258;) {
439 uint8_t j, count;
440
441 if OF_LIKELY (CTX.value == 0xFF) {
442 if OF_UNLIKELY (!_OFHuffmanTreeWalk(self,
443 tryReadBits, &CTX.treeIter, &value)) {
444 CTX.receivedCount = i;
445 return bytesWritten;
446 }
447
448 CTX.treeIter = CTX.codeLenTree;
449
450 if (value < 16) {
451 CTX.lengths[i++] = value;
452 continue;
453 }
454 } else
455 value = CTX.value;
456
457 switch (value) {
458 case 16:
459 if OF_UNLIKELY (i < 1)
461 exception];
462
463 if OF_UNLIKELY (!tryReadBits(self, &bits, 2)) {
464 CTX.receivedCount = i;
465 CTX.value = value;
466 return bytesWritten;
467 }
468
469 value = CTX.lengths[i - 1];
470 count = bits + 3;
471
472 break;
473 case 17:
474 if OF_UNLIKELY (!tryReadBits(self, &bits, 3)) {
475 CTX.receivedCount = i;
476 CTX.value = value;
477 return bytesWritten;
478 }
479
480 value = 0;
481 count = bits + 3;
482
483 break;
484 case 18:
485 if OF_UNLIKELY (!tryReadBits(self, &bits, 7)) {
486 CTX.receivedCount = i;
487 CTX.value = value;
488 return bytesWritten;
489 }
490
491 value = 0;
492 count = bits + 11;
493
494 break;
495 default:
497 }
498
499 if OF_UNLIKELY (i + count >
500 CTX.litLenCodesCount + CTX.distCodesCount + 258)
502
503 for (j = 0; j < count; j++)
504 CTX.lengths[i++] = value;
505
506 CTX.value = 0xFF;
507 }
508
509 _OFHuffmanTreeFree(CTX.codeLenTree);
510 CTX.codeLenTree = NULL;
511
512 CTX.litLenTree = _OFHuffmanTreeNew(CTX.lengths,
513 CTX.litLenCodesCount + 257);
514 CTX.distTree = _OFHuffmanTreeNew(
515 CTX.lengths + CTX.litLenCodesCount + 257,
516 CTX.distCodesCount + 1);
517
518 OFFreeMemory(CTX.lengths);
519
520 /*
521 * litLenTree and distTree are at the same location in
522 * _context.huffman and _context.huffmanTree, thus no need to
523 * set them.
524 */
525 ctx->state = stateHuffmanBlock;
526 ctx->ctx.huffman.state = huffmanStateAwaitCode;
527 ctx->ctx.huffman.treeIter = CTX.litLenTree;
528
529 goto start;
530#undef CTX
531 case stateHuffmanBlock:
532#define CTX ctx->ctx.huffman
533 for (;;) {
534 uint8_t extraBits, lengthCodeIndex;
535
536 if OF_UNLIKELY (CTX.state == huffmanStateWriteValue) {
537 if OF_UNLIKELY (length == 0)
538 return bytesWritten;
539
540 buffer[bytesWritten++] = CTX.value;
541 length--;
542
543 _slidingWindow[_slidingWindowIndex] = CTX.value;
544 _slidingWindowIndex =
545 (_slidingWindowIndex + 1) &
546 slidingWindowMask;
547
548 CTX.state = huffmanStateAwaitCode;
549 CTX.treeIter = CTX.litLenTree;
550 }
551
552 if OF_UNLIKELY (CTX.state ==
553 huffmanStateAwaitLengthExtraBits) {
554 if OF_UNLIKELY (!tryReadBits(self, &bits,
555 CTX.extraBits))
556 return bytesWritten;
557
558 CTX.length += bits;
559
560 CTX.state = huffmanStateAwaitDistance;
561 CTX.treeIter = CTX.distTree;
562 }
563
564 /* Distance of length distance pair */
565 if (CTX.state == huffmanStateAwaitDistance) {
566 if OF_UNLIKELY (!_OFHuffmanTreeWalk(self,
567 tryReadBits, &CTX.treeIter, &value))
568 return bytesWritten;
569
570 if OF_UNLIKELY (value >= numDistanceCodes)
572 exception];
573
574 CTX.distance = distanceCodes[value];
575 extraBits = distanceExtraBits[value];
576
577 if (extraBits > 0) {
578 if OF_UNLIKELY (!tryReadBits(self,
579 &bits, extraBits)) {
580#define HSADEB huffmanStateAwaitDistanceExtraBits
581 CTX.state = HSADEB;
582#undef HSADEB
583 CTX.extraBits = extraBits;
584 return bytesWritten;
585 }
586
587 CTX.distance += bits;
588 }
589
590 CTX.state = huffmanStateProcessPair;
591 } else if (CTX.state ==
592 huffmanStateAwaitDistanceExtraBits) {
593 if OF_UNLIKELY (!tryReadBits(self, &bits,
594 CTX.extraBits))
595 return bytesWritten;
596
597 CTX.distance += bits;
598
599 CTX.state = huffmanStateProcessPair;
600 }
601
602 /* Length distance pair */
603 if (CTX.state == huffmanStateProcessPair) {
604 for (uint_fast16_t j = 0; j < CTX.length; j++) {
605 uint16_t idx;
606
607 if OF_UNLIKELY (length == 0) {
608 CTX.length -= j;
609 return bytesWritten;
610 }
611
612 idx = (_slidingWindowIndex -
613 CTX.distance) & slidingWindowMask;
614 value = _slidingWindow[idx];
615
616 buffer[bytesWritten++] = value;
617 length--;
618
619 _slidingWindow[_slidingWindowIndex] =
620 value;
621 _slidingWindowIndex =
622 (_slidingWindowIndex + 1) &
623 slidingWindowMask;
624 }
625
626 CTX.state = huffmanStateAwaitCode;
627 CTX.treeIter = CTX.litLenTree;
628 }
629
630 if OF_UNLIKELY (!_OFHuffmanTreeWalk(self, tryReadBits,
631 &CTX.treeIter, &value))
632 return bytesWritten;
633
634 /* End of block */
635 if OF_UNLIKELY (value == 256) {
636 if (CTX.litLenTree != fixedLitLenTree) {
637 _OFHuffmanTreeFree(CTX.litLenTree);
638 CTX.litLenTree = NULL;
639 }
640 if (CTX.distTree != fixedDistTree) {
641 _OFHuffmanTreeFree(CTX.distTree);
642 CTX.distTree = NULL;
643 }
644
645 ctx->state = stateBlockHeader;
646 goto start;
647 }
648
649 /* Literal byte */
650 if OF_LIKELY (value < 256) {
651 if OF_UNLIKELY (length == 0) {
652 CTX.state = huffmanStateWriteValue;
653 CTX.value = value;
654 return bytesWritten;
655 }
656
657 buffer[bytesWritten++] = value;
658 length--;
659
660 _slidingWindow[_slidingWindowIndex] = value;
661 _slidingWindowIndex =
662 (_slidingWindowIndex + 1) &
663 slidingWindowMask;
664
665 CTX.treeIter = CTX.litLenTree;
666 continue;
667 }
668
669 if OF_UNLIKELY (value > 285)
671
672 /* Length of length distance pair */
673 lengthCodeIndex = value - 257;
674 CTX.length = lengthCodes[lengthCodeIndex] + 3;
675 extraBits = lengthExtraBits[lengthCodeIndex];
676
677 if (extraBits > 0) {
678 if OF_UNLIKELY (!tryReadBits(self, &bits,
679 extraBits)) {
680 CTX.extraBits = extraBits;
681 CTX.state =
682 huffmanStateAwaitLengthExtraBits;
683 return bytesWritten;
684 }
685
686 CTX.length += bits;
687 }
688
689 CTX.treeIter = CTX.distTree;
690 CTX.state = huffmanStateAwaitDistance;
691 }
692
693 break;
694#undef CTX
695 }
696
697 OF_UNREACHABLE
698}
699
700- (bool)lowlevelIsAtEndOfStream
701{
702 if (_stream == nil)
704
705 return _atEndOfStream;
706}
707
708- (int)fileDescriptorForReading
709{
710 return ((id <OFReadyForReadingObserving>)_stream)
711 .fileDescriptorForReading;
712}
713
714- (bool)lowlevelHasDataInReadBuffer
715{
716 return (_stream.hasDataInReadBuffer || (_inflateCtx != NULL &&
717 _inflateCtx->bufferLength - _inflateCtx->bufferIndex > 0));
718}
719
720- (void)close
721{
722 if (_stream == nil)
724
725 if (_inflateCtx != NULL) {
726 /* Give back our buffer to the stream, in case it's shared */
727 [_stream unreadFromBuffer: _inflateCtx->buffer +
728 _inflateCtx->bufferIndex
729 length: _inflateCtx->bufferLength -
730 _inflateCtx->bufferIndex];
731 _inflateCtx->bufferIndex = _inflateCtx->bufferLength = 0;
732 }
733
734 objc_release(_stream);
735 _stream = nil;
736
737 [super close];
738}
739@end
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:89
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 the format is invalid.
Definition OFInvalidFormatException.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:674
instancetype alloc()
Allocates memory for an instance of the class and sets up the memory pool for the object.
Definition OFObject.m:518
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:144