Index: src/OFRunLoop+Private.h ================================================================== --- src/OFRunLoop+Private.h +++ src/OFRunLoop+Private.h @@ -16,10 +16,11 @@ */ #import "OFRunLoop.h" #import "OFStream.h" #ifdef OF_HAVE_SOCKETS +# import "OFTCPSocket.h" # import "OFUDPSocket.h" #endif OF_ASSUME_NONNULL_BEGIN Index: src/OFRunLoop.h ================================================================== --- src/OFRunLoop.h +++ src/OFRunLoop.h @@ -15,13 +15,10 @@ * file. */ #import "OFObject.h" #import "OFString.h" -#ifdef OF_HAVE_SOCKETS -# import "OFTCPSocket.h" -#endif OF_ASSUME_NONNULL_BEGIN /*! @file */ Index: src/OFStream.h ================================================================== --- src/OFStream.h +++ src/OFStream.h @@ -24,10 +24,11 @@ #include #import "OFObject.h" #import "OFString.h" +#import "OFRunLoop.h" #ifdef OF_HAVE_SOCKETS # import "OFKernelEventObserver.h" #endif OF_ASSUME_NONNULL_BEGIN @@ -209,10 +210,78 @@ length: (size_t)length target: (id)target selector: (SEL)selector context: (nullable id)context; +/*! + * @brief Asynchronously reads *at most* size bytes from the stream into a + * buffer. + * + * On network streams, this might read less than the specified number of bytes. + * If you want to read exactly the specified number of bytes, use + * @ref asyncReadIntoBuffer:exactLength:target:selector:context:. Note that a + * read can even return 0 bytes - this does not necessarily mean that the + * stream ended, so you still need to check @ref atEndOfStream. + * + * @note The stream must conform to @ref OFReadyForReadingObserving in order + * for this to work! + * + * @param buffer The buffer into which the data is read. + * The buffer must not be freed before the async read completed! + * @param length The length of the data that should be read at most. + * The buffer *must* be *at least* this big! + * @param runLoopMode The run loop mode in which to perform the async read + * @param target The target on which the selector should be called when the + * data has been received. If the method returns true, it will be + * called again with the same buffer and maximum length when more + * data has been received. If you want the next method in the + * queue to handle the data received next, you need to return + * false from the method. + * @param selector The selector to call on the target. The signature must be + * `bool (OFStream *stream, void *buffer, size_t length, + * id context, id exception)`. + * @param context A context object to pass along to the target + */ +- (void)asyncReadIntoBuffer: (void *)buffer + length: (size_t)length + runLoopMode: (of_run_loop_mode_t)runLoopMode + target: (id)target + selector: (SEL)selector + context: (nullable id)context; + +/*! + * @brief Asynchronously reads exactly the specified length bytes from the + * stream into a buffer. + * + * Unlike @ref asyncReadIntoBuffer:length:target:selector:context:, this method + * does not call the method when less than the specified length has been read - + * instead, it waits until it got exactly the specified length, the stream has + * ended or an exception occurred. + * + * @note The stream must conform to @ref OFReadyForReadingObserving in order + * for this to work! + * + * @param buffer The buffer into which the data is read + * @param length The length of the data that should be read. + * The buffer *must* be *at least* this big! + * @param target The target on which the selector should be called when the + * data has been received. If the method returns true, it will be + * called again with the same buffer and exact length when more + * data has been received. If you want the next method in the + * queue to handle the data received next, you need to return + * false from the method. + * @param selector The selector to call on the target. The signature must be + * `bool (OFStream *stream, void *buffer, size_t length, + * id context, id exception)`. + * @param context A context object to pass along to the target + */ +- (void)asyncReadIntoBuffer: (void *)buffer + exactLength: (size_t)length + target: (id)target + selector: (SEL)selector + context: (nullable id)context; + /*! * @brief Asynchronously reads exactly the specified length bytes from the * stream into a buffer. * * Unlike @ref asyncReadIntoBuffer:length:target:selector:context:, this method @@ -224,10 +293,11 @@ * for this to work! * * @param buffer The buffer into which the data is read * @param length The length of the data that should be read. * The buffer *must* be *at least* this big! + * @param runLoopMode The run loop mode in which to perform the async read * @param target The target on which the selector should be called when the * data has been received. If the method returns true, it will be * called again with the same buffer and exact length when more * data has been received. If you want the next method in the * queue to handle the data received next, you need to return @@ -237,10 +307,11 @@ * id context, id exception)`. * @param context A context object to pass along to the target */ - (void)asyncReadIntoBuffer: (void *)buffer exactLength: (size_t)length + runLoopMode: (of_run_loop_mode_t)runLoopMode target: (id)target selector: (SEL)selector context: (nullable id)context; # ifdef OF_HAVE_BLOCKS @@ -269,10 +340,64 @@ */ - (void)asyncReadIntoBuffer: (void *)buffer length: (size_t)length block: (of_stream_async_read_block_t)block; +/*! + * @brief Asynchronously reads *at most* ref size bytes from the stream into a + * buffer. + * + * On network streams, this might read less than the specified number of bytes. + * If you want to read exactly the specified number of bytes, use + * @ref asyncReadIntoBuffer:exactLength:block:. Note that a read can even + * return 0 bytes - this does not necessarily mean that the stream ended, so + * you still need to check @ref atEndOfStream. + * + * @note The stream must conform to @ref OFReadyForReadingObserving in order + * for this to work! + * + * @param buffer The buffer into which the data is read. + * The buffer must not be freed before the async read completed! + * @param length The length of the data that should be read at most. + * The buffer *must* be *at least* this big! + * @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 with the same + * buffer and maximum length when more data has been received. If + * you want the next block in the queue to handle the data + * received next, you need to return false from the block. + */ +- (void)asyncReadIntoBuffer: (void *)buffer + length: (size_t)length + runLoopMode: (of_run_loop_mode_t)runLoopMode + block: (of_stream_async_read_block_t)block; + +/*! + * @brief Asynchronously reads exactly the specified length bytes from the + * stream into a buffer. + * + * Unlike @ref asyncReadIntoBuffer:length:block:, this method does not invoke + * the block when less than the specified length has been read - instead, it + * waits until it got exactly the specified length, the stream has ended or an + * exception occurred. + * + * @note The stream must conform to @ref OFReadyForReadingObserving in order + * for this to work! + * + * @param buffer The buffer into which the data is read + * @param length The length of the data that should be read. + * The buffer *must* be *at least* this big! + * @param block The block to call when the data has been received. + * If the block returns true, it will be called again with the same + * buffer and exact length when more data has been received. If + * you want the next block in the queue to handle the data + * received next, you need to return false from the block. + */ +- (void)asyncReadIntoBuffer: (void *)buffer + exactLength: (size_t)length + block: (of_stream_async_read_block_t)block; + /*! * @brief Asynchronously reads exactly the specified length bytes from the * stream into a buffer. * * Unlike @ref asyncReadIntoBuffer:length:block:, this method does not invoke @@ -284,19 +409,21 @@ * for this to work! * * @param buffer The buffer into which the data is read * @param length The length of the data that should be read. * The buffer *must* be *at least* this big! + * @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 with the same * buffer and exact length when more data has been received. If * you want the next block in the queue to handle the data * received next, you need to return false from the block. */ - - (void)asyncReadIntoBuffer: (void *)buffer - exactLength: (size_t)length - block: (of_stream_async_read_block_t)block; +- (void)asyncReadIntoBuffer: (void *)buffer + exactLength: (size_t)length + runLoopMode: (of_run_loop_mode_t)runLoopMode + block: (of_stream_async_read_block_t)block; # endif #endif /*! * @brief Reads a uint8_t from the stream. @@ -687,10 +814,35 @@ - (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding target: (id)target selector: (SEL)selector context: (nullable id)context; +/*! + * @brief Asynchronously reads with the specified encoding until a newline, + * `\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 target The target on which to call the selector when the data has + * been received. If the method returns true, it will be called + * again when the next line has been received. If you want the + * next method in the queue to handle the next line, you need to + * return false from the method + * @param selector The selector to call on the target. The signature must be + * `bool (OFStream *stream, OFString *line, id context, + * id exception)`. + * @param context A context object to pass along to the target + */ +- (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding + runLoopMode: (of_run_loop_mode_t)runLoopMode + target: (id)target + selector: (SEL)selector + context: (nullable id)context; + # ifdef OF_HAVE_BLOCKS /*! * @brief Asynchronously reads until a newline, `\0`, end of stream or an * exception occurs. * @@ -719,10 +871,29 @@ * to handle the next line, you need to return false from the * block. */ - (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding block: (of_stream_async_read_line_block_t)block; + +/*! + * @brief Asynchronously reads with the specified encoding until a newline, + * `\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 + * line has been received. If you want the next block in the queue + * to handle the next line, you need to return false from the + * block. + */ +- (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding + runLoopMode: (of_run_loop_mode_t)runLoopMode + block: (of_stream_async_read_line_block_t)block; # endif #endif /*! * @brief Tries to read a line from the stream (see @ref readLine) and returns @@ -833,10 +1004,40 @@ length: (size_t)length target: (id)target selector: (SEL)selector context: (nullable id)context; +/*! + * @brief Asynchronously writes a buffer into the stream. + * + * @note The stream must conform to @ref OFReadyForWritingObserving in order + * for this to work! + * + * @param buffer The buffer from which the data is written into the stream. The + * buffer needs to be valid until the write request is completed! + * @param length The length of the data that should be written + * @param runLoopMode The run loop mode in which to perform the async write + * @param target The target on which the selector should be called when the + * data has been written. The method should return the length for + * the next write with the same callback or 0 if it should not + * repeat. The buffer may be changed, so that every time a new + * buffer and length can be specified while the callback stays + * the same. + * @param selector The selector to call on the target. It should return the + * length for the next write with the same callback or 0 if it + * should not repeat. The signature must be `size_t (OFStream + * *stream, const void *buffer, size_t bytesWritten, id + * context, id exception)`. + * @param context A context object to pass along to the target + */ +- (void)asyncWriteBuffer: (const void *)buffer + length: (size_t)length + runLoopMode: (of_run_loop_mode_t)runLoopMode + target: (id)target + selector: (SEL)selector + context: (nullable id)context; + # ifdef OF_HAVE_BLOCKS /*! * @brief Asynchronously writes a buffer into the stream. * * @note The stream must conform to @ref OFReadyForWritingObserving in order @@ -852,10 +1053,31 @@ * callback stays the same. */ - (void)asyncWriteBuffer: (const void *)buffer length: (size_t)length block: (of_stream_async_write_block_t)block; + +/*! + * @brief Asynchronously writes a buffer into the stream. + * + * @note The stream must conform to @ref OFReadyForWritingObserving in order + * for this to work! + * + * @param buffer The buffer from which the data is written into the stream. The + * buffer needs to be valid until the write request is completed! + * @param length The length of the data that should be written + * @param runLoopMode The run loop mode in which to perform the async write + * @param block The block to call when the data has been written. It should + * return the length for the next write with the same callback or + * 0 if it should not repeat. The buffer may be changed, so that + * every time a new buffer and length can be specified while the + * callback stays the same. + */ +- (void)asyncWriteBuffer: (const void *)buffer + length: (size_t)length + runLoopMode: (of_run_loop_mode_t)runLoopMode + block: (of_stream_async_write_block_t)block; # endif #endif /*! * @brief Writes a uint8_t into the stream. Index: src/OFStream.m ================================================================== --- src/OFStream.m +++ src/OFStream.m @@ -198,18 +198,33 @@ - (void)asyncReadIntoBuffer: (void *)buffer length: (size_t)length target: (id)target selector: (SEL)selector context: (id)context +{ + [self asyncReadIntoBuffer: buffer + length: length + runLoopMode: of_run_loop_mode_default + target: target + selector: selector + context: context]; +} + +- (void)asyncReadIntoBuffer: (void *)buffer + length: (size_t)length + runLoopMode: (of_run_loop_mode_t)runLoopMode + target: (id)target + selector: (SEL)selector + context: (id)context { OFStream *stream = (OFStream *)self; [OFRunLoop of_addAsyncReadForStream: stream buffer: buffer length: length - mode: of_run_loop_mode_default + mode: runLoopMode target: target selector: selector context: context]; } @@ -216,18 +231,33 @@ - (void)asyncReadIntoBuffer: (void *)buffer exactLength: (size_t)length target: (id)target selector: (SEL)selector context: (id)context +{ + [self asyncReadIntoBuffer: buffer + exactLength: length + runLoopMode: of_run_loop_mode_default + target: target + selector: selector + context: context]; +} + +- (void)asyncReadIntoBuffer: (void *)buffer + exactLength: (size_t)length + runLoopMode: (of_run_loop_mode_t)runLoopMode + target: (id)target + selector: (SEL)selector + context: (id)context { OFStream *stream = (OFStream *)self; [OFRunLoop of_addAsyncReadForStream: stream buffer: buffer exactLength: length - mode: of_run_loop_mode_default + mode: runLoopMode target: target selector: selector context: context]; } @@ -234,31 +264,53 @@ # ifdef OF_HAVE_BLOCKS - (void)asyncReadIntoBuffer: (void *)buffer length: (size_t)length block: (of_stream_async_read_block_t)block { + [self asyncReadIntoBuffer: buffer + length: length + runLoopMode: of_run_loop_mode_default + block: block]; +} + +- (void)asyncReadIntoBuffer: (void *)buffer + length: (size_t)length + runLoopMode: (of_run_loop_mode_t)runLoopMode + block: (of_stream_async_read_block_t)block +{ OFStream *stream = (OFStream *)self; [OFRunLoop of_addAsyncReadForStream: stream buffer: buffer length: length - mode: of_run_loop_mode_default + mode: runLoopMode block: block]; } - (void)asyncReadIntoBuffer: (void *)buffer exactLength: (size_t)length block: (of_stream_async_read_block_t)block { + [self asyncReadIntoBuffer: buffer + exactLength: length + runLoopMode: of_run_loop_mode_default + block: block]; +} + +- (void)asyncReadIntoBuffer: (void *)buffer + exactLength: (size_t)length + runLoopMode: (of_run_loop_mode_t)runLoopMode + block: (of_stream_async_read_block_t)block +{ OFStream *stream = (OFStream *)self; [OFRunLoop of_addAsyncReadForStream: stream buffer: buffer exactLength: length - mode: of_run_loop_mode_default + mode: runLoopMode block: block]; } # endif #endif @@ -839,47 +891,71 @@ - (void)asyncReadLineWithTarget: (id)target selector: (SEL)selector context: (id)context { [self asyncReadLineWithEncoding: OF_STRING_ENCODING_UTF_8 + runLoopMode: of_run_loop_mode_default + target: target + selector: selector + context: context]; +} + +- (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding + target: (id)target + selector: (SEL)selector + context: (id)context +{ + [self asyncReadLineWithEncoding: encoding + runLoopMode: of_run_loop_mode_default target: target selector: selector context: context]; } - (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding + runLoopMode: (of_run_loop_mode_t)runLoopMode target: (id)target selector: (SEL)selector context: (id)context { OFStream *stream = (OFStream *)self; [OFRunLoop of_addAsyncReadLineForStream: stream encoding: encoding - mode: of_run_loop_mode_default + mode: runLoopMode target: target selector: selector context: context]; } # ifdef OF_HAVE_BLOCKS - (void)asyncReadLineWithBlock: (of_stream_async_read_line_block_t)block { [self asyncReadLineWithEncoding: OF_STRING_ENCODING_UTF_8 + runLoopMode: of_run_loop_mode_default + block: block]; +} + +- (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding + block: (of_stream_async_read_line_block_t)block +{ + [self asyncReadLineWithEncoding: encoding + runLoopMode: of_run_loop_mode_default block: block]; } - (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding + runLoopMode: (of_run_loop_mode_t)runLoopMode block: (of_stream_async_read_line_block_t)block { OFStream *stream = (OFStream *)self; [OFRunLoop of_addAsyncReadLineForStream: stream encoding: encoding - mode: of_run_loop_mode_default + mode: runLoopMode block: block]; } # endif #endif @@ -1131,18 +1207,33 @@ - (void)asyncWriteBuffer: (const void *)buffer length: (size_t)length target: (id)target selector: (SEL)selector context: (id)context +{ + [self asyncWriteBuffer: buffer + length: length + runLoopMode: of_run_loop_mode_default + target: target + selector: selector + context: context]; +} + +- (void)asyncWriteBuffer: (const void *)buffer + length: (size_t)length + runLoopMode: (of_run_loop_mode_t)runLoopMode + target: (id)target + selector: (SEL)selector + context: (id)context { OFStream *stream = (OFStream *)self; [OFRunLoop of_addAsyncWriteForStream: stream buffer: buffer length: length - mode: of_run_loop_mode_default + mode: runLoopMode target: target selector: selector context: context]; } @@ -1149,17 +1240,28 @@ # ifdef OF_HAVE_BLOCKS - (void)asyncWriteBuffer: (const void *)buffer length: (size_t)length block: (of_stream_async_write_block_t)block { + [self asyncWriteBuffer: buffer + length: length + runLoopMode: of_run_loop_mode_default + block: block]; +} + +- (void)asyncWriteBuffer: (const void *)buffer + length: (size_t)length + runLoopMode: (of_run_loop_mode_t)runLoopMode + block: (of_stream_async_write_block_t)block +{ OFStream *stream = (OFStream *)self; [OFRunLoop of_addAsyncWriteForStream: stream buffer: buffer length: length - mode: of_run_loop_mode_default + mode: runLoopMode block: block]; } # endif #endif