Index: src/OFStream.h ================================================================== --- src/OFStream.h +++ src/OFStream.h @@ -1050,10 +1050,34 @@ * @brief Cancels all pending asyncronous requests on the stream. */ - (void)cancelAsyncRequests; #endif +/*! + * @brief "Reverses" a read operation, meaning the bytes from the specified + * buffer will be returned on the next read operation. + * + * This is useful for reading into a buffer, parsing the data and then giving + * back the data which does not need to be processed. This can be used to + * optimize situations in which the length of the data that needs to be + * processed is not known before all data has been processed - for example in + * decompression - in which it would otherwise be necessary to read byte by + * byte to avoid consuming bytes that need to be parsed by something else - + * for example the data descriptor in a ZIP archive which immediately follows + * the compressed data. + * + * If the stream is a file, this method does not change any data in the file. + * + * If the stream is seekable, a seek operation will discard any data that was + * unread. + * + * @param buffer The buffer to unread + * @param length The length of the buffer to unread + */ +- (void)unreadFromBuffer: (const void*)buffer + length: (size_t)length; + /*! * @brief Closes the stream. */ - (void)close; Index: src/OFStream.m ================================================================== --- src/OFStream.m +++ src/OFStream.m @@ -40,10 +40,11 @@ #import "OFRunLoop+Private.h" #import "OFInvalidArgumentException.h" #import "OFInvalidFormatException.h" #import "OFNotImplementedException.h" +#import "OFOutOfRangeException.h" #import "OFSetOptionFailedException.h" #import "macros.h" #import "of_asprintf.h" @@ -1553,10 +1554,25 @@ - (void)cancelAsyncRequests { [OFRunLoop OF_cancelAsyncRequestsForStream: self]; } #endif + +- (void)unreadFromBuffer: (const void*)buffer + length: (size_t)length +{ + if (length > SIZE_MAX - _readBufferLength) + @throw [OFOutOfRangeException exception]; + + _readBuffer = [self resizeMemory: _readBuffer + size: _readBufferLength + length]; + + memmove(_readBuffer + length, _readBuffer, _readBufferLength); + memcpy(_readBuffer, buffer, length); + + _readBufferLength += length; +} - (void)close { [self doesNotRecognizeSelector: _cmd]; abort();