Index: src/OFInflateStream.h ================================================================== --- src/OFInflateStream.h +++ src/OFInflateStream.h @@ -79,11 +79,11 @@ * * @param stream The underlying stream to which compressed data is written or * from which compressed data is read * @return A new, autoreleased OFInflateStream */ -+ (instancetype)streamWithStream: (OFStream *)stream; ++ (instancetype)streamWithStream: (OF_KINDOF(OFStream *))stream; - (instancetype)init OF_UNAVAILABLE; /*! * @brief Initializes an already allocated OFInflateStream with the specified @@ -91,9 +91,10 @@ * * @param stream The underlying stream to which compressed data is written or * from which compressed data is read * @return A initialized OFInflateStream */ -- (instancetype)initWithStream: (OFStream *)stream OF_DESIGNATED_INITIALIZER; +- (instancetype)initWithStream: (OF_KINDOF(OFStream *))stream + OF_DESIGNATED_INITIALIZER; @end OF_ASSUME_NONNULL_END Index: src/OFInflateStream.m ================================================================== --- src/OFInflateStream.m +++ src/OFInflateStream.m @@ -170,21 +170,21 @@ lengths[i] = 5; fixedDistTree = of_huffman_tree_construct(lengths, 32); } -+ (instancetype)streamWithStream: (OFStream *)stream ++ (instancetype)streamWithStream: (OF_KINDOF(OFStream *))stream { return [[[self alloc] initWithStream: stream] autorelease]; } - (instancetype)init { OF_INVALID_INIT_METHOD } -- (instancetype)initWithStream: (OFStream *)stream +- (instancetype)initWithStream: (OF_KINDOF(OFStream *))stream { self = [super init]; @try { _stream = [stream retain]; Index: src/OFLHAArchive.m ================================================================== --- src/OFLHAArchive.m +++ src/OFLHAArchive.m @@ -42,13 +42,14 @@ #define LHSTREAM_BUFFER_SIZE 4096 @interface OFLHAArchive_LHStream: OFStream { @public - OFStream *_stream; + OF_KINDOF(OFStream *) _stream; uint8_t _distanceBits, _dictionaryBits; unsigned char _buffer[LHSTREAM_BUFFER_SIZE]; + uint32_t _bytesConsumed; uint16_t _bufferIndex, _bufferLength; uint8_t _byte; uint8_t _bitIndex, _savedBitsLength; uint16_t _savedBits; unsigned char *_slidingWindow; @@ -62,24 +63,26 @@ uint8_t *_codesLengths; uint16_t _length; uint32_t _distance; } -- (instancetype)of_initWithStream: (OFStream *)stream +- (instancetype)of_initWithStream: (OF_KINDOF(OFStream *))stream distanceBits: (uint8_t)distanceBits dictionaryBits: (uint8_t)dictionaryBits; @end @interface OFLHAArchive_FileReadStream: OFStream { OF_KINDOF(OFStream *) _stream; - uint32_t _toRead; - uint16_t _expectedCRC16, _CRC16; + OF_KINDOF(OFStream *) _decompressedStream; + OFLHAArchiveEntry *_entry; + uint32_t _toRead, _bytesConsumed; + uint16_t _CRC16; bool _atEndOfStream; } -- (instancetype)of_initWithStream: (OFStream *)stream +- (instancetype)of_initWithStream: (OF_KINDOF(OFStream *))stream entry: (OFLHAArchiveEntry *)entry; - (void)of_skip; @end enum state { @@ -113,10 +116,12 @@ stream->_buffer[stream->_bufferIndex++]; else { size_t length = [stream->_stream readIntoBuffer: stream->_buffer length: LHSTREAM_BUFFER_SIZE]; + + stream->_bytesConsumed += (uint32_t)length; if OF_UNLIKELY (length < 1) { stream->_savedBits = ret; stream->_savedBitsLength = i; return false; @@ -281,11 +286,11 @@ _stream = nil; } @end @implementation OFLHAArchive_LHStream -- (instancetype)of_initWithStream: (OFStream *)stream +- (instancetype)of_initWithStream: (OF_KINDOF(OFStream *))stream distanceBits: (uint8_t)distanceBits dictionaryBits: (uint8_t)dictionaryBits { self = [super init]; @@ -600,10 +605,11 @@ * LHA header. */ [_stream unreadFromBuffer: _buffer + _bufferIndex length: _bufferLength - _bufferIndex]; + _bytesConsumed -= _bufferLength - _bufferIndex; _bufferIndex = _bufferLength = 0; return bytesWritten; } @@ -687,16 +693,27 @@ @throw [OFNotOpenException exceptionWithObject: self]; return ([_stream isAtEndOfStream] && _bufferLength - _bufferIndex == 0 && _state == STATE_BLOCK_HEADER); } + +- (int)fileDescriptorForReading +{ + return [_stream fileDescriptorForReading]; +} + +- (bool)hasDataInReadBuffer +{ + return ([super hasDataInReadBuffer] || [_stream hasDataInReadBuffer]); +} - (void)close { /* Give back our buffer to the stream, in case it's shared */ [_stream unreadFromBuffer: _buffer + _bufferIndex length: _bufferLength - _bufferIndex]; + _bytesConsumed -= _bufferLength - _bufferIndex; _bufferIndex = _bufferLength = 0; [_stream release]; _stream = nil; @@ -703,44 +720,42 @@ [super close]; } @end @implementation OFLHAArchive_FileReadStream -- (instancetype)of_initWithStream: (OFStream *)stream +- (instancetype)of_initWithStream: (OF_KINDOF(OFStream *))stream entry: (OFLHAArchiveEntry *)entry { self = [super init]; @try { - OFString *method = [entry method]; + OFString *method; + + _stream = [stream retain]; + + method = [entry method]; if ([method isEqual: @"-lh4-"] || [method isEqual: @"-lh5-"]) - _stream = [[OFLHAArchive_LHStream alloc] + _decompressedStream = [[OFLHAArchive_LHStream alloc] of_initWithStream: stream distanceBits: 4 dictionaryBits: 14]; else if ([method isEqual: @"-lh6-"]) - _stream = [[OFLHAArchive_LHStream alloc] + _decompressedStream = [[OFLHAArchive_LHStream alloc] of_initWithStream: stream distanceBits: 5 dictionaryBits: 16]; else if ([method isEqual: @"-lh7-"]) - _stream = [[OFLHAArchive_LHStream alloc] + _decompressedStream = [[OFLHAArchive_LHStream alloc] of_initWithStream: stream distanceBits: 5 dictionaryBits: 17]; else - _stream = [stream retain]; - - if ([method isEqual: @"-lh0-"] || [method isEqual: @"-lhd-"] || - [method isEqual: @"-lz4-"] || [method isEqual: @"-lh5-"] || - [method isEqual: @"-lh6-"] || [method isEqual: @"-lh6-"]) - _toRead = [entry uncompressedSize]; - else - _toRead = [entry compressedSize]; - - _expectedCRC16 = [entry CRC16]; + _decompressedStream = [stream retain]; + + _entry = [entry copy]; + _toRead = [entry uncompressedSize]; } @catch (id e) { [self release]; @throw e; } @@ -749,12 +764,24 @@ - (void)dealloc { [self close]; + [_stream release]; + [_decompressedStream release]; + [_entry release]; + [super dealloc]; } + +- (bool)lowlevelIsAtEndOfStream +{ + if (_stream == nil) + @throw [OFNotOpenException exceptionWithObject: self]; + + return _atEndOfStream; +} - (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)length { size_t ret; @@ -766,74 +793,90 @@ return 0; if (length > _toRead) length = _toRead; - ret = [_stream readIntoBuffer: buffer - length: length]; + ret = [_decompressedStream readIntoBuffer: buffer + length: length]; _toRead -= ret; _CRC16 = of_crc16(_CRC16, buffer, ret); if (_toRead == 0) { _atEndOfStream = true; - if (_CRC16 != _expectedCRC16) + if (_CRC16 != [_entry CRC16]) @throw [OFChecksumFailedException exception]; } return ret; } -- (bool)lowlevelIsAtEndOfStream -{ - if (_stream == nil) - @throw [OFNotOpenException exceptionWithObject: self]; - - return _atEndOfStream; -} - - (bool)hasDataInReadBuffer { - return ([super hasDataInReadBuffer] || [_stream hasDataInReadBuffer]); + return ([super hasDataInReadBuffer] || + [_decompressedStream hasDataInReadBuffer]); } - (int)fileDescriptorForReading { - return [_stream fileDescriptorForReading]; + return [_decompressedStream fileDescriptorForReading]; +} + +- (void)of_skip +{ + OF_KINDOF(OFStream *) stream; + uint32_t toRead; + + if (_stream == nil || _toRead == 0) + return; + + stream = _stream; + toRead = _toRead; + + /* + * Get the number of consumed bytes and directly read from the + * compressed stream, to make skipping much faster. + */ + if ([_decompressedStream isKindOfClass: + [OFLHAArchive_LHStream class]]) { + OFLHAArchive_LHStream *LHStream = _decompressedStream; + + [LHStream close]; + toRead = [_entry compressedSize] - LHStream->_bytesConsumed; + + stream = _stream; + } + + if ([stream isKindOfClass: [OFSeekableStream class]] && + (sizeof(of_offset_t) > 4 || toRead < INT32_MAX)) + [stream seekToOffset: (of_offset_t)toRead + whence: SEEK_CUR]; + else { + while (toRead > 0) { + char buffer[512]; + size_t min = toRead; + + if (min > 512) + min = 512; + + toRead -= [stream readIntoBuffer: buffer + length: min]; + } + } + + _toRead = 0; } - (void)close { [self of_skip]; [_stream release]; _stream = nil; - [super close]; -} - -- (void)of_skip -{ - if (_stream == nil || _toRead == 0) - return; - - if ([_stream isKindOfClass: [OFSeekableStream class]] && - (sizeof(of_offset_t) > 4 || _toRead < INT32_MAX)) { - [_stream seekToOffset: (of_offset_t)_toRead - whence: SEEK_CUR]; - - _toRead = 0; - } else { - while (_toRead > 0) { - char buffer[512]; - size_t min = _toRead; - - if (min > 512) - min = 512; - - _toRead -= [_stream readIntoBuffer: buffer - length: min]; - } - } + [_decompressedStream release]; + _decompressedStream = nil; + + [super close]; } @end Index: src/OFZIPArchive.m ================================================================== --- src/OFZIPArchive.m +++ src/OFZIPArchive.m @@ -41,10 +41,11 @@ #import "OFNotImplementedException.h" #import "OFNotOpenException.h" #import "OFOpenItemFailedException.h" #import "OFOutOfRangeException.h" #import "OFSeekFailedException.h" +#import "OFTruncatedDataException.h" #import "OFUnsupportedVersionException.h" /* * FIXME: Current limitations: * - Split archives are not supported. @@ -73,11 +74,12 @@ - (bool)matchesEntry: (OFZIPArchiveEntry *)entry; @end @interface OFZIPArchive_FileReadStream: OFStream { - OFStream *_stream, *_decompressedStream; + OF_KINDOF(OFStream *) _stream; + OF_KINDOF(OFStream *) _decompressedStream; OFZIPArchiveEntry *_entry; uint64_t _toRead; uint32_t _CRC32; bool _atEndOfStream; } @@ -776,10 +778,13 @@ [super dealloc]; } - (bool)lowlevelIsAtEndOfStream { + if (_stream == nil) + @throw [OFNotOpenException exceptionWithObject: self]; + return _atEndOfStream; } - (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)length @@ -801,27 +806,41 @@ length = (size_t)_toRead; ret = [_decompressedStream readIntoBuffer: buffer length: length]; - if (ret == 0) - _atEndOfStream = true; - _toRead -= ret; _CRC32 = of_crc32(_CRC32, buffer, ret); - if (_toRead == 0) + if (_toRead == 0) { + _atEndOfStream = true; + if (~_CRC32 != [_entry CRC32]) @throw [OFChecksumFailedException exception]; + } return ret; } + +- (bool)hasDataInReadBuffer +{ + return ([super hasDataInReadBuffer] || + [_decompressedStream hasDataInReadBuffer]); +} + +- (int)fileDescriptorForReading +{ + return [_decompressedStream fileDescriptorForReading]; +} - (void)close { [_stream release]; _stream = nil; + + [_decompressedStream release]; + _decompressedStream = nil; [super close]; } @end