Index: src/OFRunLoop+Private.h ================================================================== --- src/OFRunLoop+Private.h +++ src/OFRunLoop+Private.h @@ -58,10 +58,20 @@ mode: (OFRunLoopMode)mode # ifdef OF_HAVE_BLOCKS block: (nullable OFStreamAsyncReadBlock)block # endif delegate: (nullable id )delegate; ++ (void)of_addAsyncReadStringForStream: (OFStream *)stream + encoding: (OFStringEncoding)encoding + mode: (OFRunLoopMode)mode +# ifdef OF_HAVE_BLOCKS + block: (nullable OFStreamAsyncReadStringBlock) + block +# endif + delegate: (nullable id ) + delegate; + (void)of_addAsyncReadLineForStream: (OFStream *) stream encoding: (OFStringEncoding)encoding mode: (OFRunLoopMode)mode # ifdef OF_HAVE_BLOCKS Index: src/OFRunLoop.m ================================================================== --- src/OFRunLoop.m +++ src/OFRunLoop.m @@ -111,10 +111,20 @@ # endif void *_buffer; size_t _exactLength, _readLength; } @end + +@interface OFRunLoopReadStringQueueItem: OFRunLoopQueueItem +{ +@public +# ifdef OF_HAVE_BLOCKS + OFStreamAsyncReadStringBlock _block; +# endif + OFStringEncoding _encoding; +} +@end @interface OFRunLoopReadLineQueueItem: OFRunLoopQueueItem { @public # ifdef OF_HAVE_BLOCKS @@ -531,10 +541,53 @@ return true; # ifdef OF_HAVE_BLOCKS } # endif } + +# ifdef OF_HAVE_BLOCKS +- (void)dealloc +{ + [_block release]; + + [super dealloc]; +} +# endif +@end + +@implementation OFRunLoopReadStringQueueItem +- (bool)handleObject: (id)object +{ + OFString *string; + id exception = nil; + + @try { + string = [object tryReadStringWithEncoding: _encoding]; + } @catch (id e) { + string = nil; + exception = e; + } + + if (string == nil && ![object isAtEndOfStream] && exception == nil) + return true; + +# ifdef OF_HAVE_BLOCKS + if (_block != NULL) + return _block(string, exception); + else { +# endif + if (![_delegate respondsToSelector: + @selector(stream:didReadString:exception:)]) + return false; + + return [_delegate stream: object + didReadString: string + exception: exception]; +# ifdef OF_HAVE_BLOCKS + } +# endif +} # ifdef OF_HAVE_BLOCKS - (void)dealloc { [_block release]; @@ -1281,10 +1334,30 @@ queueItem->_block = [block copy]; # endif queueItem->_buffer = buffer; queueItem->_exactLength = exactLength; + QUEUE_ITEM +} + ++ (void)of_addAsyncReadStringForStream: (OFStream *)stream + encoding: (OFStringEncoding)encoding + mode: (OFRunLoopMode)mode +# ifdef OF_HAVE_BLOCKS + block: (OFStreamAsyncReadStringBlock)block +# endif + delegate: (id )delegate +{ + NEW_READ(OFRunLoopReadStringQueueItem, stream, mode) + + queueItem->_delegate = [delegate retain]; +# ifdef OF_HAVE_BLOCKS + queueItem->_block = [block copy]; +# endif + queueItem->_encoding = encoding; + QUEUE_ITEM } + (void)of_addAsyncReadLineForStream: (OFStream *) stream Index: src/OFStream.h ================================================================== --- src/OFStream.h +++ src/OFStream.h @@ -50,10 +50,23 @@ * success * @return A bool whether the same block should be used for the next read */ typedef bool (^OFStreamAsyncReadBlock)(size_t length, id _Nullable exception); +/** + * @brief A block which is called when a string was read asynchronously from a + * stream. + * + * @param string The string which has been read or `nil` when the end of stream + * occurred + * @param exception An exception which occurred while reading or `nil` on + * success + * @return A bool whether the same block should be used for the next read + */ +typedef bool (^OFStreamAsyncReadStringBlock)(OFString *_Nullable string, + id _Nullable exception); + /** * @brief A block which is called when a line was read asynchronously from a * stream. * * @param line The line which has been read or `nil` when the end of stream @@ -114,10 +127,24 @@ - (bool)stream: (OFStream *)stream didReadIntoBuffer: (void *)buffer length: (size_t)length exception: (nullable id)exception; +/** + * @brief This method is called when a string was read asynchronously from a + * stream. + * + * @param stream The stream on which a line was read + * @param string The string which has been read or `nil` when the end of stream + * occurred + * @param exception An exception that occurred while reading, or nil on success + * @return A bool whether the read should be repeated + */ +- (bool)stream: (OFStream *)stream + didReadString: (nullable OFString *)string + exception: (nullable id)exception; + /** * @brief This method is called when a line was read asynchronously from a * stream. * * @param stream The stream on which a line was read @@ -770,10 +797,43 @@ * @throw OFNotOpenException The stream is not open */ - (nullable OFString *)readLineWithEncoding: (OFStringEncoding)encoding; #ifdef OF_HAVE_SOCKETS +/** + * @brief Asynchronously reads until a `\0`, end of stream or an exception + * occurs. + * + * @note The stream must conform to @ref OFReadyForReadingObserving in order + * for this to work! + */ +- (void)asyncReadString; + +/** + * @brief Asynchronously reads with the specified encoding until a `\0`, end of + * stream or an exception occurs. + * + * @note The stream must conform to @ref OFReadyForReadingObserving in order + * for this to work! + * + * @param encoding The encoding used by the stream + */ +- (void)asyncReadStringWithEncoding: (OFStringEncoding)encoding; + +/** + * @brief Asynchronously reads with the specified encoding until a `\0`, end of + * stream or an exception occurs. + * + * @note The stream must conform to @ref OFReadyForReadingObserving in order + * for this to work! + * + * @param encoding The encoding used by the stream + * @param runLoopMode The run loop mode in which to perform the async read + */ +- (void)asyncReadStringWithEncoding: (OFStringEncoding)encoding + runLoopMode: (OFRunLoopMode)runLoopMode; + /** * @brief Asynchronously reads until a newline, `\0`, end of stream or an * exception occurs. * * @note The stream must conform to @ref OFReadyForReadingObserving in order @@ -804,10 +864,61 @@ */ - (void)asyncReadLineWithEncoding: (OFStringEncoding)encoding runLoopMode: (OFRunLoopMode)runLoopMode; # ifdef OF_HAVE_BLOCKS +/** + * @brief Asynchronously reads until a `\0`, end of stream or an exception + * occurs. + * + * @note The stream must conform to @ref OFReadyForReadingObserving in order + * for this to work! + * + * @param block The block to call when the data has been received. + * If the block returns true, it will be called again when the + * next string has been received. If you want the next block in + * the queue to handle the next string, you need to return false + * from the block. + */ +- (void)asyncReadStringWithBlock: (OFStreamAsyncReadStringBlock)block; + +/** + * @brief Asynchronously reads with the specified encoding until a `\0`, end of + * stream or an exception occurs. + * + * @note The stream must conform to @ref OFReadyForReadingObserving in order + * for this to work! + * + * @param encoding The encoding used by the stream + * @param block The block to call when the data has been received. + * If the block returns true, it will be called again when the + * next string has been received. If you want the next block in + * the queue to handle the next string, you need to return false + * from the block. + */ +- (void)asyncReadStringWithEncoding: (OFStringEncoding)encoding + block: (OFStreamAsyncReadStringBlock)block; + +/** + * @brief Asynchronously reads with the specified encoding until a `\0`, end of + * stream or an exception occurs. + * + * @note The stream must conform to @ref OFReadyForReadingObserving in order + * for this to work! + * + * @param encoding The encoding used by the stream + * @param runLoopMode The run loop mode in which to perform the async read + * @param block The block to call when the data has been received. + * If the block returns true, it will be called again when the + * next string has been received. If you want the next block in + * the queue to handle the next string, you need to return false + * from the block. + */ +- (void)asyncReadStringWithEncoding: (OFStringEncoding)encoding + runLoopMode: (OFRunLoopMode)runLoopMode + block: (OFStreamAsyncReadStringBlock)block; + /** * @brief Asynchronously reads until a newline, `\0`, end of stream or an * exception occurs. * * @note The stream must conform to @ref OFReadyForReadingObserving in order Index: src/OFStream.m ================================================================== --- src/OFStream.m +++ src/OFStream.m @@ -630,10 +630,37 @@ return line; } #ifdef OF_HAVE_SOCKETS +- (void)asyncReadString +{ + [self asyncReadStringWithEncoding: OFStringEncodingUTF8 + runLoopMode: OFDefaultRunLoopMode]; +} + +- (void)asyncReadStringWithEncoding: (OFStringEncoding)encoding +{ + [self asyncReadStringWithEncoding: encoding + runLoopMode: OFDefaultRunLoopMode]; +} + +- (void)asyncReadStringWithEncoding: (OFStringEncoding)encoding + runLoopMode: (OFRunLoopMode)runLoopMode +{ + OFStream *stream = + (OFStream *)self; + + [OFRunLoop of_addAsyncReadStringForStream: stream + encoding: encoding + mode: runLoopMode +# ifdef OF_HAVE_BLOCKS + block: NULL +# endif + delegate: _delegate]; +} + - (void)asyncReadLine { [self asyncReadLineWithEncoding: OFStringEncodingUTF8 runLoopMode: OFDefaultRunLoopMode]; } @@ -658,10 +685,39 @@ # endif delegate: _delegate]; } # ifdef OF_HAVE_BLOCKS +- (void)asyncReadStringWithBlock: (OFStreamAsyncReadStringBlock)block +{ + [self asyncReadStringWithEncoding: OFStringEncodingUTF8 + runLoopMode: OFDefaultRunLoopMode + block: block]; +} + +- (void)asyncReadStringWithEncoding: (OFStringEncoding)encoding + block: (OFStreamAsyncReadStringBlock)block +{ + [self asyncReadStringWithEncoding: encoding + runLoopMode: OFDefaultRunLoopMode + block: block]; +} + +- (void)asyncReadStringWithEncoding: (OFStringEncoding)encoding + runLoopMode: (OFRunLoopMode)runLoopMode + block: (OFStreamAsyncReadStringBlock)block +{ + OFStream *stream = + (OFStream *)self; + + [OFRunLoop of_addAsyncReadStringForStream: stream + encoding: encoding + mode: runLoopMode + block: block + delegate: nil]; +} + - (void)asyncReadLineWithBlock: (OFStreamAsyncReadLineBlock)block { [self asyncReadLineWithEncoding: OFStringEncodingUTF8 runLoopMode: OFDefaultRunLoopMode block: block];