ObjFW
 
Loading...
Searching...
No Matches
OFInflateStream.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#ifndef OF_INFLATE64_STREAM_M
26# import "OFInflateStream.h"
27#else
28# import "OFInflate64Stream.h"
29# define OFInflateStream OFInflate64Stream
30#endif
31#import "OFHuffmanTree.h"
32
33#import "OFInitializationFailedException.h"
34#import "OFInvalidFormatException.h"
35#import "OFNotOpenException.h"
36#import "OFOutOfMemoryException.h"
37
38#ifndef OF_INFLATE64_STREAM_M
39# define bufferSize OFInflateStreamBufferSize
40#else
41# define bufferSize OFInflate64StreamBufferSize
42#endif
43
44enum State {
45 stateBlockHeader,
46 stateUncompressedBlockHeader,
47 stateUncompressedBlock,
48 stateHuffmanTree,
49 stateHuffmanBlock
50};
51
52enum HuffmanState {
53 huffmanStateWriteValue,
54 huffmanStateAwaitCode,
55 huffmanStateAwaitLengthExtraBits,
56 huffmanStateAwaitDistance,
57 huffmanStateAwaitDistanceExtraBits,
58 huffmanStateProcessPair
59};
60
61#ifndef OF_INFLATE64_STREAM_M
62static const uint8_t numDistanceCodes = 30;
63static const uint8_t lengthCodes[29] = {
64 /* indices are -257, values -3 */
65 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56,
66 64, 80, 96, 112, 128, 160, 192, 224, 255
67};
68static const uint8_t lengthExtraBits[29] = {
69 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4,
70 5, 5, 5, 5, 0
71};
72static const uint16_t distanceCodes[30] = {
73 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385,
74 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577
75};
76static const uint8_t distanceExtraBits[30] = {
77 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10,
78 10, 11, 11, 12, 12, 13, 13
79};
80#else
81static const uint8_t numDistanceCodes = 32;
82static const uint8_t lengthCodes[29] = {
83 /* indices are -257, values -3 */
84 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56,
85 64, 80, 96, 112, 128, 160, 192, 224, 0
86};
87static const uint8_t lengthExtraBits[29] = {
88 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4,
89 5, 5, 5, 5, 16
90};
91static const uint16_t distanceCodes[32] = {
92 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385,
93 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577,
94 32769, 49153
95};
96static const uint8_t distanceExtraBits[32] = {
97 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10,
98 10, 11, 11, 12, 12, 13, 13, 14, 14
99};
100#endif
101static const uint8_t codeLengthsOrder[19] = {
102 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
103};
104static OFHuffmanTree fixedLitLenTree, fixedDistTree;
105
106@implementation OFInflateStream
107@synthesize underlyingStream = _stream;
108
109static OF_INLINE bool
110tryReadBits(OFInflateStream *stream, uint16_t *bits, uint8_t count)
111{
112 uint16_t ret = stream->_savedBits;
113
114 OFAssert(stream->_savedBitsLength < count);
115
116 for (uint_fast8_t i = stream->_savedBitsLength; i < count; i++) {
117 if OF_UNLIKELY (stream->_bitIndex == 8) {
118 if OF_LIKELY (stream->_bufferIndex <
119 stream->_bufferLength)
120 stream->_byte =
121 stream->_buffer[stream->_bufferIndex++];
122 else {
123 size_t length = [stream->_stream
124 readIntoBuffer: stream->_buffer
125 length: bufferSize];
126
127 if OF_UNLIKELY (length < 1) {
128 stream->_savedBits = ret;
129 stream->_savedBitsLength = i;
130 return false;
131 }
132
133 stream->_byte = stream->_buffer[0];
134 stream->_bufferIndex = 1;
135 stream->_bufferLength = (uint16_t)length;
136 }
137
138 stream->_bitIndex = 0;
139 }
140
141 ret |= ((stream->_byte >> stream->_bitIndex++) & 1) << i;
142 }
143
144 stream->_savedBits = 0;
145 stream->_savedBitsLength = 0;
146 *bits = ret;
147
148 return true;
149}
150
151+ (void)initialize
152{
153 uint8_t lengths[288];
154
155 if (self != [OFInflateStream class])
156 return;
157
158 for (uint16_t i = 0; i <= 143; i++)
159 lengths[i] = 8;
160 for (uint16_t i = 144; i <= 255; i++)
161 lengths[i] = 9;
162 for (uint16_t i = 256; i <= 279; i++)
163 lengths[i] = 7;
164 for (uint16_t i = 280; i <= 287; i++)
165 lengths[i] = 8;
166
167 fixedLitLenTree = _OFHuffmanTreeNew(lengths, 288);
168
169 for (uint16_t i = 0; i <= 31; i++)
170 lengths[i] = 5;
171
172 fixedDistTree = _OFHuffmanTreeNew(lengths, 32);
173}
174
175+ (instancetype)streamWithStream: (OFStream *)stream
176{
177 return objc_autoreleaseReturnValue(
178 [[self alloc] initWithStream: stream]);
179}
180
181- (instancetype)init
182{
183 OF_INVALID_INIT_METHOD
184}
185
186- (instancetype)initWithStream: (OFStream *)stream
187{
188 self = [super init];
189
190 @try {
191 _stream = objc_retain(stream);
192
193 /* 0-7 address the bit, 8 means fetch next byte */
194 _bitIndex = 8;
195
196#ifdef OF_INFLATE64_STREAM_M
197 _slidingWindowMask = 0xFFFF;
198#else
199 _slidingWindowMask = 0x7FFF;
200#endif
201 _slidingWindow = OFAllocZeroedMemory(_slidingWindowMask + 1, 1);
202 } @catch (id e) {
203 objc_release(self);
204 @throw e;
205 }
206
207 return self;
208}
209
210- (void)dealloc
211{
212 if (_stream != nil)
213 [self close];
214
215 OFFreeMemory(_slidingWindow);
216
217 if (_state == stateHuffmanTree) {
218 OFFreeMemory(_context.huffmanTree.lengths);
219
220 if (_context.huffmanTree.codeLenTree != NULL)
221 _OFHuffmanTreeFree(_context.huffmanTree.codeLenTree);
222 }
223
224 if (_state == stateHuffmanTree || _state == stateHuffmanBlock) {
225 if (_context.huffman.litLenTree != fixedLitLenTree)
226 _OFHuffmanTreeFree(_context.huffman.litLenTree);
227 if (_context.huffman.distTree != fixedDistTree)
228 _OFHuffmanTreeFree(_context.huffman.distTree);
229 }
230
231 [super dealloc];
232}
233
234- (size_t)lowlevelReadIntoBuffer: (void *)buffer_
235 length: (size_t)length
236{
237 unsigned char *buffer = buffer_;
238 uint16_t bits = 0, tmp, value = 0;
239 size_t bytesWritten = 0;
240 unsigned char *slidingWindow;
241 uint16_t slidingWindowIndex;
242
243 if (_stream == nil)
244 @throw [OFNotOpenException exceptionWithObject: self];
245
246 if (_atEndOfStream)
247 return 0;
248
249start:
250 switch ((enum State)_state) {
251 case stateBlockHeader:
252 if OF_UNLIKELY (_inLastBlock) {
253 [_stream unreadFromBuffer: _buffer + _bufferIndex
254 length: _bufferLength -
255 _bufferIndex];
256 _bufferIndex = _bufferLength = 0;
257
258 _atEndOfStream = true;
259 return bytesWritten;
260 }
261
262 if OF_UNLIKELY (!tryReadBits(self, &bits, 3))
263 return bytesWritten;
264
265 _inLastBlock = (bits & 1);
266
267 switch (bits >> 1) {
268 case 0: /* No compression */
269 _state = stateUncompressedBlockHeader;
270 _bitIndex = 8;
271 _context.uncompressedHeader.position = 0;
272 memset(_context.uncompressedHeader.length, 0, 4);
273 break;
274 case 1: /* Fixed Huffman */
275 _state = stateHuffmanBlock;
276 _context.huffman.state = huffmanStateAwaitCode;
277 _context.huffman.litLenTree = fixedLitLenTree;
278 _context.huffman.distTree = fixedDistTree;
279 _context.huffman.treeIter = fixedLitLenTree;
280 break;
281 case 2: /* Dynamic Huffman */
282 _state = stateHuffmanTree;
283 _context.huffmanTree.lengths = NULL;
284 _context.huffmanTree.receivedCount = 0;
285 _context.huffmanTree.value = 0xFE;
286 _context.huffmanTree.litLenCodesCount = 0xFF;
287 _context.huffmanTree.distCodesCount = 0xFF;
288 _context.huffmanTree.codeLenCodesCount = 0xFF;
289 break;
290 default:
292 }
293
294 goto start;
295 case stateUncompressedBlockHeader:
296#define CTX _context.uncompressedHeader
297 /* FIXME: This can be done more efficiently than unreading */
298 [_stream unreadFromBuffer: _buffer + _bufferIndex
299 length: _bufferLength - _bufferIndex];
300 _bufferIndex = _bufferLength = 0;
301
302 CTX.position += [_stream
303 readIntoBuffer: CTX.length + CTX.position
304 length: 4 - CTX.position];
305
306 if OF_UNLIKELY (CTX.position < 4)
307 return bytesWritten;
308
309 if OF_UNLIKELY ((CTX.length[0] | (CTX.length[1] << 8)) !=
310 (uint16_t)~(CTX.length[2] | (CTX.length[3] << 8)))
311 @throw [OFInvalidFormatException exception];
312
313 _state = stateUncompressedBlock;
314
315 /*
316 * Do not reorder! _context.uncompressed.position and
317 * _context.uncompressedHeader.length overlap!
318 */
319 _context.uncompressed.length =
320 CTX.length[0] | (CTX.length[1] << 8);
321 _context.uncompressed.position = 0;
322
323 goto start;
324#undef CTX
325 case stateUncompressedBlock:
326#define CTX _context.uncompressed
327 if OF_UNLIKELY (length == 0)
328 return bytesWritten;
329
330 tmp = (length < (size_t)CTX.length - CTX.position
331 ? (uint16_t)length : CTX.length - CTX.position);
332
333 tmp = (uint16_t)[_stream readIntoBuffer: buffer + bytesWritten
334 length: tmp];
335
336 if OF_UNLIKELY (tmp == 0)
337 return bytesWritten;
338
339 slidingWindow = _slidingWindow;
340 slidingWindowIndex = _slidingWindowIndex;
341 for (uint_fast16_t i = 0; i < tmp; i++) {
342 slidingWindow[slidingWindowIndex] =
343 buffer[bytesWritten + i];
344 slidingWindowIndex = (slidingWindowIndex + 1) &
345 _slidingWindowMask;
346 }
347 _slidingWindowIndex = slidingWindowIndex;
348
349 length -= tmp;
350 bytesWritten += tmp;
351
352 CTX.position += tmp;
353 if OF_UNLIKELY (CTX.position == CTX.length)
354 _state = stateBlockHeader;
355
356 goto start;
357#undef CTX
358 case stateHuffmanTree:
359#define CTX _context.huffmanTree
360 if OF_LIKELY (CTX.value == 0xFE) {
361 if OF_LIKELY (CTX.litLenCodesCount == 0xFF) {
362 if OF_UNLIKELY (!tryReadBits(self, &bits, 5))
363 return bytesWritten;
364
365 if OF_UNLIKELY (bits > 29)
366 @throw [OFInvalidFormatException
367 exception];
368
369 CTX.litLenCodesCount = bits;
370 }
371
372 if OF_LIKELY (CTX.distCodesCount == 0xFF) {
373 if OF_UNLIKELY (!tryReadBits(self, &bits, 5))
374 return bytesWritten;
375
376 CTX.distCodesCount = bits;
377 }
378
379 if OF_LIKELY (CTX.codeLenCodesCount == 0xFF) {
380 if OF_UNLIKELY (!tryReadBits(self, &bits, 4))
381 return bytesWritten;
382
383 CTX.codeLenCodesCount = bits;
384 }
385
386 if OF_LIKELY (CTX.lengths == NULL)
387 CTX.lengths = OFAllocZeroedMemory(19, 1);
388
389 for (uint16_t i = CTX.receivedCount;
390 i < CTX.codeLenCodesCount + 4; i++) {
391 if OF_UNLIKELY (!tryReadBits(self, &bits, 3)) {
392 CTX.receivedCount = i;
393 return bytesWritten;
394 }
395
396 CTX.lengths[codeLengthsOrder[i]] = bits;
397 }
398
399 CTX.codeLenTree = _OFHuffmanTreeNew(CTX.lengths, 19);
400 CTX.treeIter = CTX.codeLenTree;
401
402 OFFreeMemory(CTX.lengths);
403 CTX.lengths = NULL;
404 CTX.receivedCount = 0;
405 CTX.value = 0xFF;
406 }
407
408 if OF_LIKELY (CTX.lengths == NULL)
409 CTX.lengths = OFAllocMemory(
410 CTX.litLenCodesCount + CTX.distCodesCount + 258, 1);
411
412 for (uint16_t i = CTX.receivedCount;
413 i < CTX.litLenCodesCount + CTX.distCodesCount + 258;) {
414 uint8_t j, count;
415
416 if OF_LIKELY (CTX.value == 0xFF) {
417 if OF_UNLIKELY (!_OFHuffmanTreeWalk(self,
418 tryReadBits, &CTX.treeIter, &value)) {
419 CTX.receivedCount = i;
420 return bytesWritten;
421 }
422
423 CTX.treeIter = CTX.codeLenTree;
424
425 if (value < 16) {
426 CTX.lengths[i++] = value;
427 continue;
428 }
429 } else
430 value = CTX.value;
431
432 switch (value) {
433 case 16:
434 if OF_UNLIKELY (i < 1)
436 exception];
437
438 if OF_UNLIKELY (!tryReadBits(self, &bits, 2)) {
439 CTX.receivedCount = i;
440 CTX.value = value;
441 return bytesWritten;
442 }
443
444 value = CTX.lengths[i - 1];
445 count = bits + 3;
446
447 break;
448 case 17:
449 if OF_UNLIKELY (!tryReadBits(self, &bits, 3)) {
450 CTX.receivedCount = i;
451 CTX.value = value;
452 return bytesWritten;
453 }
454
455 value = 0;
456 count = bits + 3;
457
458 break;
459 case 18:
460 if OF_UNLIKELY (!tryReadBits(self, &bits, 7)) {
461 CTX.receivedCount = i;
462 CTX.value = value;
463 return bytesWritten;
464 }
465
466 value = 0;
467 count = bits + 11;
468
469 break;
470 default:
472 }
473
474 if OF_UNLIKELY (i + count >
475 CTX.litLenCodesCount + CTX.distCodesCount + 258)
477
478 for (j = 0; j < count; j++)
479 CTX.lengths[i++] = value;
480
481 CTX.value = 0xFF;
482 }
483
484 _OFHuffmanTreeFree(CTX.codeLenTree);
485 CTX.codeLenTree = NULL;
486
487 CTX.litLenTree = _OFHuffmanTreeNew(CTX.lengths,
488 CTX.litLenCodesCount + 257);
489 CTX.distTree = _OFHuffmanTreeNew(
490 CTX.lengths + CTX.litLenCodesCount + 257,
491 CTX.distCodesCount + 1);
492
493 OFFreeMemory(CTX.lengths);
494
495 /*
496 * litLenTree and distTree are at the same location in
497 * _context.huffman and _context.huffmanTree, thus no need to
498 * set them.
499 */
500 _state = stateHuffmanBlock;
501 _context.huffman.state = huffmanStateAwaitCode;
502 _context.huffman.treeIter = CTX.litLenTree;
503
504 goto start;
505#undef CTX
506 case stateHuffmanBlock:
507#define CTX _context.huffman
508 for (;;) {
509 uint8_t extraBits, lengthCodeIndex;
510
511 if OF_UNLIKELY (CTX.state == huffmanStateWriteValue) {
512 if OF_UNLIKELY (length == 0)
513 return bytesWritten;
514
515 buffer[bytesWritten++] = CTX.value;
516 length--;
517
518 _slidingWindow[_slidingWindowIndex] = CTX.value;
519 _slidingWindowIndex =
520 (_slidingWindowIndex + 1) &
521 _slidingWindowMask;
522
523 CTX.state = huffmanStateAwaitCode;
524 CTX.treeIter = CTX.litLenTree;
525 }
526
527 if OF_UNLIKELY (CTX.state ==
528 huffmanStateAwaitLengthExtraBits) {
529 if OF_UNLIKELY (!tryReadBits(self, &bits,
530 CTX.extraBits))
531 return bytesWritten;
532
533 CTX.length += bits;
534
535 CTX.state = huffmanStateAwaitDistance;
536 CTX.treeIter = CTX.distTree;
537 }
538
539 /* Distance of length distance pair */
540 if (CTX.state == huffmanStateAwaitDistance) {
541 if OF_UNLIKELY (!_OFHuffmanTreeWalk(self,
542 tryReadBits, &CTX.treeIter, &value))
543 return bytesWritten;
544
545 if OF_UNLIKELY (value >= numDistanceCodes)
547 exception];
548
549 CTX.distance = distanceCodes[value];
550 extraBits = distanceExtraBits[value];
551
552 if (extraBits > 0) {
553 if OF_UNLIKELY (!tryReadBits(self,
554 &bits, extraBits)) {
555#define HSADEB huffmanStateAwaitDistanceExtraBits
556 CTX.state = HSADEB;
557#undef HSADEB
558 CTX.extraBits = extraBits;
559 return bytesWritten;
560 }
561
562 CTX.distance += bits;
563 }
564
565 CTX.state = huffmanStateProcessPair;
566 } else if (CTX.state ==
567 huffmanStateAwaitDistanceExtraBits) {
568 if OF_UNLIKELY (!tryReadBits(self, &bits,
569 CTX.extraBits))
570 return bytesWritten;
571
572 CTX.distance += bits;
573
574 CTX.state = huffmanStateProcessPair;
575 }
576
577 /* Length distance pair */
578 if (CTX.state == huffmanStateProcessPair) {
579 for (uint_fast16_t j = 0; j < CTX.length; j++) {
580 uint16_t idx;
581
582 if OF_UNLIKELY (length == 0) {
583 CTX.length -= j;
584 return bytesWritten;
585 }
586
587 idx = (_slidingWindowIndex -
588 CTX.distance) & _slidingWindowMask;
589 value = _slidingWindow[idx];
590
591 buffer[bytesWritten++] = value;
592 length--;
593
594 _slidingWindow[_slidingWindowIndex] =
595 value;
596 _slidingWindowIndex =
597 (_slidingWindowIndex + 1) &
598 _slidingWindowMask;
599 }
600
601 CTX.state = huffmanStateAwaitCode;
602 CTX.treeIter = CTX.litLenTree;
603 }
604
605 if OF_UNLIKELY (!_OFHuffmanTreeWalk(self, tryReadBits,
606 &CTX.treeIter, &value))
607 return bytesWritten;
608
609 /* End of block */
610 if OF_UNLIKELY (value == 256) {
611 if (CTX.litLenTree != fixedLitLenTree)
612 _OFHuffmanTreeFree(CTX.litLenTree);
613 if (CTX.distTree != fixedDistTree)
614 _OFHuffmanTreeFree(CTX.distTree);
615
616 _state = stateBlockHeader;
617 goto start;
618 }
619
620 /* Literal byte */
621 if OF_LIKELY (value < 256) {
622 if OF_UNLIKELY (length == 0) {
623 CTX.state = huffmanStateWriteValue;
624 CTX.value = value;
625 return bytesWritten;
626 }
627
628 buffer[bytesWritten++] = value;
629 length--;
630
631 _slidingWindow[_slidingWindowIndex] = value;
632 _slidingWindowIndex =
633 (_slidingWindowIndex + 1) &
634 _slidingWindowMask;
635
636 CTX.treeIter = CTX.litLenTree;
637 continue;
638 }
639
640 if OF_UNLIKELY (value > 285)
642
643 /* Length of length distance pair */
644 lengthCodeIndex = value - 257;
645 CTX.length = lengthCodes[lengthCodeIndex] + 3;
646 extraBits = lengthExtraBits[lengthCodeIndex];
647
648 if (extraBits > 0) {
649 if OF_UNLIKELY (!tryReadBits(self, &bits,
650 extraBits)) {
651 CTX.extraBits = extraBits;
652 CTX.state =
653 huffmanStateAwaitLengthExtraBits;
654 return bytesWritten;
655 }
656
657 CTX.length += bits;
658 }
659
660 CTX.treeIter = CTX.distTree;
661 CTX.state = huffmanStateAwaitDistance;
662 }
663
664 break;
665#undef CTX
666 }
667
668 OF_UNREACHABLE
669}
670
672{
673 if (_stream == nil)
675
676 return _atEndOfStream;
677}
678
680{
681 return ((id <OFReadyForReadingObserving>)_stream)
682 .fileDescriptorForReading;
683}
684
686{
687 return (_stream.hasDataInReadBuffer ||
688 _bufferLength - _bufferIndex > 0);
689}
690
691- (void)close
692{
693 if (_stream == nil)
695
696 /* Give back our buffer to the stream, in case it's shared */
697 [_stream unreadFromBuffer: _buffer + _bufferIndex
698 length: _bufferLength - _bufferIndex];
699 _bufferIndex = _bufferLength = 0;
700
701 objc_release(_stream);
702 _stream = nil;
703
704 [super close];
705}
706@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
instancetype exception()
Creates a new, autoreleased exception.
Definition OFException.m:283
A class that handles Deflate decompression transparently for an underlying stream.
Definition OFInflateStream.h:39
OFStream * underlyingStream
The underlying stream of the inflate stream.
Definition OFInflateStream.h:84
An exception indicating that the format is invalid.
Definition OFInvalidFormatException.h:30
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
void dealloc()
Deallocates the object.
Definition OFObject.m:1321
instancetype alloc()
Allocates memory for an instance of the class and sets up the memory pool for the object.
Definition OFObject.m:515
void initialize()
A method which is called the moment before the first call to the class is being made.
Definition OFObject.m:511
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
bool lowlevelIsAtEndOfStream()
Returns whether the lowlevel is at the end of the stream.
Definition OFStream.m:112
void close()
Closes the stream.
Definition OFStream.m:1772
bool lowlevelHasDataInReadBuffer()
Returns whether the lowlevel has data in the read buffer.
Definition OFStream.m:117
int fileDescriptorForReading
The file descriptor for reading that should be checked by the OFKernelEventObserver.
Definition OFKernelEventObserver.h:89