Index: src/OFHTTPClient.m ================================================================== --- src/OFHTTPClient.m +++ src/OFHTTPClient.m @@ -767,15 +767,13 @@ [_handler release]; [super dealloc]; } -- (size_t)lowlevelWriteBuffer: (const void *)buffer - length: (size_t)length +- (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length { - size_t requestedLength = length; - size_t ret; + /* TODO: Use non-blocking writes */ if (_socket == nil) @throw [OFNotOpenException exceptionWithObject: self]; /* @@ -785,43 +783,32 @@ */ if (length == 0) return 0; if (_atEndOfStream) - @throw [OFWriteFailedException - exceptionWithObject: self - requestedLength: requestedLength - bytesWritten: 0 - errNo: 0]; + @throw [OFWriteFailedException exceptionWithObject: self + requestedLength: length + bytesWritten: 0 + errNo: ENOTCONN]; if (_chunked) [_socket writeFormat: @"%zX\r\n", length]; else if (length > _toWrite) - length = (size_t)_toWrite; + @throw [OFOutOfRangeException exception]; - ret = [_socket writeBuffer: buffer length: length]; + [_socket writeBuffer: buffer length: length]; if (_chunked) [_socket writeString: @"\r\n"]; - if (ret > length) - @throw [OFOutOfRangeException exception]; - if (!_chunked) { - _toWrite -= ret; + _toWrite -= length; if (_toWrite == 0) _atEndOfStream = true; } - if (requestedLength > length) - @throw [OFWriteFailedException - exceptionWithObject: self - requestedLength: requestedLength - bytesWritten: ret - errNo: 0]; - - return ret; + return length; } - (bool)lowlevelIsAtEndOfStream { return _atEndOfStream; Index: src/OFHTTPServer.m ================================================================== --- src/OFHTTPServer.m +++ src/OFHTTPServer.m @@ -13,10 +13,11 @@ * file. */ #include "config.h" +#include #include #include #import "OFHTTPServer.h" #import "OFArray.h" @@ -226,12 +227,22 @@ @throw [OFNotOpenException exceptionWithObject: self]; if (!_headersSent) [self of_sendHeaders]; - if (!_chunked) - return [_socket writeBuffer: buffer length: length]; + if (!_chunked) { + @try { + [_socket writeBuffer: buffer length: length]; + } @catch (OFWriteFailedException *e) { + if (e.errNo == EWOULDBLOCK) + return e.bytesWritten; + + @throw e; + } + + return length; + } pool = objc_autoreleasePoolPush(); [_socket writeString: [OFString stringWithFormat: @"%zX\r\n", length]]; objc_autoreleasePoolPop(pool); Index: src/OFLHAArchive.m ================================================================== --- src/OFLHAArchive.m +++ src/OFLHAArchive.m @@ -12,10 +12,12 @@ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this * file. */ #include "config.h" + +#include #import "OFLHAArchive.h" #import "OFLHAArchiveEntry.h" #import "OFLHAArchiveEntry+Private.h" #import "OFCRC16.h" @@ -471,32 +473,34 @@ [super dealloc]; } - (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length { - uint32_t bytesWritten; - if (_stream == nil) @throw [OFNotOpenException exceptionWithObject: self]; if (UINT32_MAX - _bytesWritten < length) @throw [OFOutOfRangeException exception]; @try { - bytesWritten = (uint32_t)[_stream writeBuffer: buffer - length: length]; + [_stream writeBuffer: buffer length: length]; } @catch (OFWriteFailedException *e) { - _bytesWritten += e.bytesWritten; + OFEnsure(e.bytesWritten < length); + + _bytesWritten += (uint32_t)e.bytesWritten; _CRC16 = OFCRC16(_CRC16, buffer, e.bytesWritten); + + if (e.errNo == EWOULDBLOCK) + return e.bytesWritten; @throw e; } - _bytesWritten += (uint32_t)bytesWritten; - _CRC16 = OFCRC16(_CRC16, buffer, bytesWritten); + _bytesWritten += (uint32_t)length; + _CRC16 = OFCRC16(_CRC16, buffer, length); - return bytesWritten; + return length; } - (bool)lowlevelIsAtEndOfStream { if (_stream == nil) Index: src/OFRunLoop.m ================================================================== --- src/OFRunLoop.m +++ src/OFRunLoop.m @@ -40,10 +40,11 @@ #import "OFTimer.h" #import "OFTimer+Private.h" #import "OFDate.h" #import "OFObserveFailedException.h" +#import "OFWriteFailedException.h" #include "OFRunLoopConstants.inc" static OFRunLoop *mainRunLoop = nil; @@ -567,19 +568,24 @@ size_t dataLength = _data.count * _data.itemSize; OFData *newData, *oldData; @try { const char *dataItems = _data.items; + length = dataLength - _writtenLength; + [object writeBuffer: dataItems + _writtenLength length: length]; + } @catch (OFWriteFailedException *e) { + length = e.bytesWritten; - length = [object writeBuffer: dataItems + _writtenLength - length: dataLength - _writtenLength]; + if (e.errNo != EWOULDBLOCK) + exception = e; } @catch (id e) { length = 0; exception = e; } _writtenLength += length; + OFEnsure(_writtenLength <= dataLength); if (_writtenLength != dataLength && exception == nil) return true; # ifdef OF_HAVE_BLOCKS @@ -639,19 +645,24 @@ size_t cStringLength = [_string cStringLengthWithEncoding: _encoding]; OFString *newString, *oldString; @try { const char *cString = [_string cStringWithEncoding: _encoding]; + length = cStringLength - _writtenLength; + [object writeBuffer: cString + _writtenLength length: length]; + } @catch (OFWriteFailedException *e) { + length = e.bytesWritten; - length = [object writeBuffer: cString + _writtenLength - length: cStringLength - _writtenLength]; + if (e.errNo != EWOULDBLOCK) + exception = e; } @catch (id e) { length = 0; exception = e; } _writtenLength += length; + OFEnsure(_writtenLength <= cStringLength); if (_writtenLength != cStringLength && exception == nil) return true; # ifdef OF_HAVE_BLOCKS Index: src/OFStream.h ================================================================== --- src/OFStream.h +++ src/OFStream.h @@ -970,17 +970,20 @@ */ - (void)flushWriteBuffer; /** * @brief Writes from a buffer into the stream. + * + * In non-blocking mode, if less than the specified length could be written, an + * @ref OFWriteFailedException is thrown with @ref OFWriteFailedException#errNo + * being set to `EWOULDBLOCK` and @ref OFWriteFailedException#bytesWritten + * being set to the number of bytes that were written, if any. * * @param buffer The buffer from which the data is written into the stream * @param length The length of the data that should be written - * @return The number of bytes written. This can only differ from the specified - * length in non-blocking mode. */ -- (size_t)writeBuffer: (const void *)buffer length: (size_t)length; +- (void)writeBuffer: (const void *)buffer length: (size_t)length; #ifdef OF_HAVE_SOCKETS /** * @brief Asynchronously writes data into the stream. * @@ -1129,265 +1132,301 @@ #endif /** * @brief Writes a uint8_t into the stream. * + * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:. + * * @param int8 A uint8_t */ - (void)writeInt8: (uint8_t)int8; /** * @brief Writes a uint16_t into the stream, encoded in big endian. * + * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:. + * * @param int16 A uint16_t */ - (void)writeBigEndianInt16: (uint16_t)int16; /** * @brief Writes a uint32_t into the stream, encoded in big endian. + * + * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:. * * @param int32 A uint32_t */ - (void)writeBigEndianInt32: (uint32_t)int32; /** * @brief Writes a uint64_t into the stream, encoded in big endian. * + * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:. + * * @param int64 A uint64_t */ - (void)writeBigEndianInt64: (uint64_t)int64; /** * @brief Writes a float into the stream, encoded in big endian. * + * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:. + * * @param float_ A float */ - (void)writeBigEndianFloat: (float)float_; /** * @brief Writes a double into the stream, encoded in big endian. + * + * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:. * * @param double_ A double */ - (void)writeBigEndianDouble: (double)double_; /** * @brief Writes the specified number of uint16_ts into the stream, encoded in * big endian. * + * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:. + * * @param buffer The buffer from which the data is written to the stream after * it has been byte swapped if necessary * @param count The number of uint16_ts to write - * @return The number of bytes written to the stream */ -- (size_t)writeBigEndianInt16s: (const uint16_t *)buffer count: (size_t)count; +- (void)writeBigEndianInt16s: (const uint16_t *)buffer count: (size_t)count; /** * @brief Writes the specified number of uint32_ts into the stream, encoded in * big endian. * + * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:. + * * @param buffer The buffer from which the data is written to the stream after * it has been byte swapped if necessary * @param count The number of uint32_ts to write - * @return The number of bytes written to the stream */ -- (size_t)writeBigEndianInt32s: (const uint32_t *)buffer count: (size_t)count; +- (void)writeBigEndianInt32s: (const uint32_t *)buffer count: (size_t)count; /** * @brief Writes the specified number of uint64_ts into the stream, encoded in * big endian. * + * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:. + * * @param buffer The buffer from which the data is written to the stream after * it has been byte swapped if necessary * @param count The number of uint64_ts to write - * @return The number of bytes written to the stream */ -- (size_t)writeBigEndianInt64s: (const uint64_t *)buffer count: (size_t)count; +- (void)writeBigEndianInt64s: (const uint64_t *)buffer count: (size_t)count; /** * @brief Writes the specified number of floats into the stream, encoded in big * endian. * + * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:. + * * @param buffer The buffer from which the data is written to the stream after * it has been byte swapped if necessary * @param count The number of floats to write - * @return The number of bytes written to the stream */ -- (size_t)writeBigEndianFloats: (const float *)buffer count: (size_t)count; +- (void)writeBigEndianFloats: (const float *)buffer count: (size_t)count; /** * @brief Writes the specified number of doubles into the stream, encoded in * big endian. * + * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:. + * * @param buffer The buffer from which the data is written to the stream after * it has been byte swapped if necessary * @param count The number of doubles to write - * @return The number of bytes written to the stream */ -- (size_t)writeBigEndianDoubles: (const double *)buffer count: (size_t)count; +- (void)writeBigEndianDoubles: (const double *)buffer count: (size_t)count; /** * @brief Writes a uint16_t into the stream, encoded in little endian. + * + * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:. * * @param int16 A uint16_t */ - (void)writeLittleEndianInt16: (uint16_t)int16; /** * @brief Writes a uint32_t into the stream, encoded in little endian. * + * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:. + * * @param int32 A uint32_t */ - (void)writeLittleEndianInt32: (uint32_t)int32; /** * @brief Writes a uint64_t into the stream, encoded in little endian. + * + * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:. * * @param int64 A uint64_t */ - (void)writeLittleEndianInt64: (uint64_t)int64; /** * @brief Writes a float into the stream, encoded in little endian. * + * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:. + * * @param float_ A float */ - (void)writeLittleEndianFloat: (float)float_; /** * @brief Writes a double into the stream, encoded in little endian. + * + * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:. * * @param double_ A double */ - (void)writeLittleEndianDouble: (double)double_; /** * @brief Writes the specified number of uint16_ts into the stream, encoded in * little endian. * + * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:. + * * @param buffer The buffer from which the data is written to the stream after * it has been byte swapped if necessary * @param count The number of uint16_ts to write - * @return The number of bytes written to the stream */ -- (size_t)writeLittleEndianInt16s: (const uint16_t *)buffer - count: (size_t)count; +- (void)writeLittleEndianInt16s: (const uint16_t *)buffer count: (size_t)count; /** * @brief Writes the specified number of uint32_ts into the stream, encoded in * little endian. * + * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:. + * * @param count The number of uint32_ts to write * @param buffer The buffer from which the data is written to the stream after * it has been byte swapped if necessary - * @return The number of bytes written to the stream */ -- (size_t)writeLittleEndianInt32s: (const uint32_t *)buffer - count: (size_t)count; +- (void)writeLittleEndianInt32s: (const uint32_t *)buffer count: (size_t)count; /** * @brief Writes the specified number of uint64_ts into the stream, encoded in * little endian. * + * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:. + * * @param buffer The buffer from which the data is written to the stream after * it has been byte swapped if necessary * @param count The number of uint64_ts to write - * @return The number of bytes written to the stream */ -- (size_t)writeLittleEndianInt64s: (const uint64_t *)buffer - count: (size_t)count; +- (void)writeLittleEndianInt64s: (const uint64_t *)buffer count: (size_t)count; /** * @brief Writes the specified number of floats into the stream, encoded in * little endian. * + * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:. + * * @param buffer The buffer from which the data is written to the stream after * it has been byte swapped if necessary * @param count The number of floats to write - * @return The number of bytes written to the stream */ -- (size_t)writeLittleEndianFloats: (const float *)buffer count: (size_t)count; +- (void)writeLittleEndianFloats: (const float *)buffer count: (size_t)count; /** * @brief Writes the specified number of doubles into the stream, encoded in * little endian. * + * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:. + * * @param buffer The buffer from which the data is written to the stream after * it has been byte swapped if necessary * @param count The number of doubles to write - * @return The number of bytes written to the stream */ -- (size_t)writeLittleEndianDoubles: (const double *)buffer count: (size_t)count; +- (void)writeLittleEndianDoubles: (const double *)buffer count: (size_t)count; /** * @brief Writes OFData into the stream. * + * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:. + * * @param data The OFData to write into the stream - * @return The number of bytes written */ -- (size_t)writeData: (OFData *)data; +- (void)writeData: (OFData *)data; /** * @brief Writes a string into the stream, without the trailing zero. * + * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:. + * * @param string The string from which the data is written to the stream - * @return The number of bytes written */ -- (size_t)writeString: (OFString *)string; +- (void)writeString: (OFString *)string; /** * @brief Writes a string into the stream in the specified encoding, without * the trailing zero. * + * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:. + * * @param string The string from which the data is written to the stream * @param encoding The encoding in which to write the string to the stream - * @return The number of bytes written */ -- (size_t)writeString: (OFString *)string encoding: (OFStringEncoding)encoding; +- (void)writeString: (OFString *)string encoding: (OFStringEncoding)encoding; /** * @brief Writes a string into the stream with a trailing newline. * + * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:. + * * @param string The string from which the data is written to the stream - * @return The number of bytes written */ -- (size_t)writeLine: (OFString *)string; +- (void)writeLine: (OFString *)string; /** * @brief Writes a string into the stream in the specified encoding with a * trailing newline. * + * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:. + * * @param string The string from which the data is written to the stream * @param encoding The encoding in which to write the string to the stream - * @return The number of bytes written + */ +- (void)writeLine: (OFString *)string encoding: (OFStringEncoding)encoding; + +/** + * @brief Writes a formatted string into the stream. + * + * See printf for the format syntax. As an addition, `%@` is available as + * format specifier for objects, `%C` for `OFUnichar` and `%S` for + * `const OFUnichar *`. + * + * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:. + * + * @param format A string used as format */ -- (size_t)writeLine: (OFString *)string encoding: (OFStringEncoding)encoding; +- (void)writeFormat: (OFConstantString *)format, ...; /** * @brief Writes a formatted string into the stream. * * See printf for the format syntax. As an addition, `%@` is available as * format specifier for objects, `%C` for `OFUnichar` and `%S` for * `const OFUnichar *`. * - * @param format A string used as format - * @return The number of bytes written - */ -- (size_t)writeFormat: (OFConstantString *)format, ...; - -/** - * @brief Writes a formatted string into the stream. - * - * See printf for the format syntax. As an addition, `%@` is available as - * format specifier for objects, `%C` for `OFUnichar` and `%S` for - * `const OFUnichar *`. + * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:. * * @param format A string used as format * @param arguments The arguments used in the format string - * @return The number of bytes written */ -- (size_t)writeFormat: (OFConstantString *)format arguments: (va_list)arguments; +- (void)writeFormat: (OFConstantString *)format arguments: (va_list)arguments; #ifdef OF_HAVE_SOCKETS /** * @brief Cancels all pending asynchronous requests on the stream. */ Index: src/OFStream.m ================================================================== --- src/OFStream.m +++ src/OFStream.m @@ -1056,42 +1056,60 @@ encoding: OFStringEncodingUTF8]; } - (void)flushWriteBuffer { + size_t bytesWritten; + if (_writeBuffer == NULL) return; - [self lowlevelWriteBuffer: _writeBuffer length: _writeBufferLength]; + bytesWritten = [self lowlevelWriteBuffer: _writeBuffer + length: _writeBufferLength]; - OFFreeMemory(_writeBuffer); - _writeBuffer = NULL; - _writeBufferLength = 0; + if (bytesWritten == 0) + return; + + if (bytesWritten == _writeBufferLength) { + OFFreeMemory(_writeBuffer); + _writeBuffer = NULL; + _writeBufferLength = 0; + } + + OFEnsure(bytesWritten < _writeBufferLength); + + memmove(_writeBuffer, _writeBuffer + bytesWritten, + _writeBufferLength - bytesWritten); + _writeBufferLength -= bytesWritten; + @try { + _writeBuffer = OFResizeMemory(_writeBuffer, + _writeBufferLength, 1); + } @catch (OFOutOfMemoryException *e) { + /* We don't care, as we only made it smaller. */ + } } -- (size_t)writeBuffer: (const void *)buffer - length: (size_t)length +- (void)writeBuffer: (const void *)buffer length: (size_t)length { if (!_buffersWrites) { size_t bytesWritten = [self lowlevelWriteBuffer: buffer length: length]; - if (_canBlock && bytesWritten < length) + if (bytesWritten < length) @throw [OFWriteFailedException exceptionWithObject: self requestedLength: length bytesWritten: bytesWritten errNo: 0]; - - return bytesWritten; } else { + if (SIZE_MAX - _writeBufferLength < length) + @throw [OFOutOfRangeException exception]; + _writeBuffer = OFResizeMemory(_writeBuffer, _writeBufferLength + length, 1); memcpy(_writeBuffer + _writeBufferLength, buffer, length); _writeBufferLength += length; - - return length; } } #ifdef OF_HAVE_SOCKETS - (void)asyncWriteData: (OFData *)data @@ -1237,11 +1255,11 @@ { double_ = OFToBigEndianDouble(double_); [self writeBuffer: (char *)&double_ length: 8]; } -- (size_t)writeBigEndianInt16s: (const uint16_t *)buffer count: (size_t)count +- (void)writeBigEndianInt16s: (const uint16_t *)buffer count: (size_t)count { size_t size; if OF_UNLIKELY (count > SIZE_MAX / sizeof(uint16_t)) @throw [OFOutOfRangeException exception]; @@ -1260,15 +1278,13 @@ [self writeBuffer: tmp length: size]; } @finally { OFFreeMemory(tmp); } #endif - - return size; } -- (size_t)writeBigEndianInt32s: (const uint32_t *)buffer count: (size_t)count +- (void)writeBigEndianInt32s: (const uint32_t *)buffer count: (size_t)count { size_t size; if OF_UNLIKELY (count > SIZE_MAX / sizeof(uint32_t)) @throw [OFOutOfRangeException exception]; @@ -1287,15 +1303,13 @@ [self writeBuffer: tmp length: size]; } @finally { OFFreeMemory(tmp); } #endif - - return size; } -- (size_t)writeBigEndianInt64s: (const uint64_t *)buffer count: (size_t)count +- (void)writeBigEndianInt64s: (const uint64_t *)buffer count: (size_t)count { size_t size; if OF_UNLIKELY (count > SIZE_MAX / sizeof(uint64_t)) @throw [OFOutOfRangeException exception]; @@ -1314,15 +1328,13 @@ [self writeBuffer: tmp length: size]; } @finally { OFFreeMemory(tmp); } #endif - - return size; } -- (size_t)writeBigEndianFloats: (const float *)buffer count: (size_t)count +- (void)writeBigEndianFloats: (const float *)buffer count: (size_t)count { size_t size; if OF_UNLIKELY (count > SIZE_MAX / sizeof(float)) @throw [OFOutOfRangeException exception]; @@ -1341,15 +1353,13 @@ [self writeBuffer: tmp length: size]; } @finally { OFFreeMemory(tmp); } #endif - - return size; } -- (size_t)writeBigEndianDoubles: (const double *)buffer count: (size_t)count +- (void)writeBigEndianDoubles: (const double *)buffer count: (size_t)count { size_t size; if OF_UNLIKELY (count > SIZE_MAX / sizeof(double)) @throw [OFOutOfRangeException exception]; @@ -1368,12 +1378,10 @@ [self writeBuffer: tmp length: size]; } @finally { OFFreeMemory(tmp); } #endif - - return size; } - (void)writeLittleEndianInt16: (uint16_t)int16 { int16 = OFToLittleEndian16(int16); @@ -1402,11 +1410,11 @@ { double_ = OFToLittleEndianDouble(double_); [self writeBuffer: (char *)&double_ length: 8]; } -- (size_t)writeLittleEndianInt16s: (const uint16_t *)buffer count: (size_t)count +- (void)writeLittleEndianInt16s: (const uint16_t *)buffer count: (size_t)count { size_t size; if OF_UNLIKELY (count > SIZE_MAX / sizeof(uint16_t)) @throw [OFOutOfRangeException exception]; @@ -1425,15 +1433,13 @@ [self writeBuffer: tmp length: size]; } @finally { OFFreeMemory(tmp); } #endif - - return size; } -- (size_t)writeLittleEndianInt32s: (const uint32_t *)buffer count: (size_t)count +- (void)writeLittleEndianInt32s: (const uint32_t *)buffer count: (size_t)count { size_t size; if OF_UNLIKELY (count > SIZE_MAX / sizeof(uint32_t)) @throw [OFOutOfRangeException exception]; @@ -1452,15 +1458,13 @@ [self writeBuffer: tmp length: size]; } @finally { OFFreeMemory(tmp); } #endif - - return size; } -- (size_t)writeLittleEndianInt64s: (const uint64_t *)buffer count: (size_t)count +- (void)writeLittleEndianInt64s: (const uint64_t *)buffer count: (size_t)count { size_t size; if OF_UNLIKELY (count > SIZE_MAX / sizeof(uint64_t)) @throw [OFOutOfRangeException exception]; @@ -1479,15 +1483,13 @@ [self writeBuffer: tmp length: size]; } @finally { OFFreeMemory(tmp); } #endif - - return size; } -- (size_t)writeLittleEndianFloats: (const float *)buffer count: (size_t)count +- (void)writeLittleEndianFloats: (const float *)buffer count: (size_t)count { size_t size; if OF_UNLIKELY (count > SIZE_MAX / sizeof(float)) @throw [OFOutOfRangeException exception]; @@ -1506,15 +1508,13 @@ [self writeBuffer: tmp length: size]; } @finally { OFFreeMemory(tmp); } #endif - - return size; } -- (size_t)writeLittleEndianDoubles: (const double *)buffer count: (size_t)count +- (void)writeLittleEndianDoubles: (const double *)buffer count: (size_t)count { size_t size; if OF_UNLIKELY (count > SIZE_MAX / sizeof(double)) @throw [OFOutOfRangeException exception]; @@ -1533,15 +1533,13 @@ [self writeBuffer: tmp length: size]; } @finally { OFFreeMemory(tmp); } #endif - - return size; } -- (size_t)writeData: (OFData *)data +- (void)writeData: (OFData *)data { void *pool; size_t length; if (data == nil) @@ -1551,20 +1549,18 @@ length = data.count * data.itemSize; [self writeBuffer: data.items length: length]; objc_autoreleasePoolPop(pool); +} - return length; +- (void)writeString: (OFString *)string +{ + [self writeString: string encoding: OFStringEncodingUTF8]; } -- (size_t)writeString: (OFString *)string -{ - return [self writeString: string encoding: OFStringEncodingUTF8]; -} - -- (size_t)writeString: (OFString *)string encoding: (OFStringEncoding)encoding +- (void)writeString: (OFString *)string encoding: (OFStringEncoding)encoding { void *pool; size_t length; if (string == nil) @@ -1575,20 +1571,18 @@ [self writeBuffer: [string cStringWithEncoding: encoding] length: length]; objc_autoreleasePoolPop(pool); +} - return length; +- (void)writeLine: (OFString *)string +{ + [self writeLine: string encoding: OFStringEncodingUTF8]; } -- (size_t)writeLine: (OFString *)string -{ - return [self writeLine: string encoding: OFStringEncodingUTF8]; -} - -- (size_t)writeLine: (OFString *)string encoding: (OFStringEncoding)encoding +- (void)writeLine: (OFString *)string encoding: (OFStringEncoding)encoding { size_t stringLength = [string cStringLengthWithEncoding: encoding]; char *buffer; buffer = OFAllocMemory(stringLength + 1, 1); @@ -1600,27 +1594,22 @@ [self writeBuffer: buffer length: stringLength + 1]; } @finally { OFFreeMemory(buffer); } - - return stringLength + 1; } -- (size_t)writeFormat: (OFConstantString *)format, ... +- (void)writeFormat: (OFConstantString *)format, ... { va_list arguments; - size_t ret; va_start(arguments, format); - ret = [self writeFormat: format arguments: arguments]; + [self writeFormat: format arguments: arguments]; va_end(arguments); - - return ret; } -- (size_t)writeFormat: (OFConstantString *)format arguments: (va_list)arguments +- (void)writeFormat: (OFConstantString *)format arguments: (va_list)arguments { char *UTF8String; int length; if (format == nil) @@ -1633,12 +1622,10 @@ @try { [self writeBuffer: UTF8String length: length]; } @finally { free(UTF8String); } - - return length; } - (bool)hasDataInReadBuffer { return (_readBufferLength > 0); Index: src/OFTarArchive.m ================================================================== --- src/OFTarArchive.m +++ src/OFTarArchive.m @@ -12,10 +12,12 @@ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this * file. */ #include "config.h" + +#include #import "OFTarArchive.h" #import "OFTarArchiveEntry.h" #import "OFTarArchiveEntry+Private.h" #import "OFDate.h" @@ -427,29 +429,30 @@ [super dealloc]; } - (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length { - size_t bytesWritten; - if (_stream == nil) @throw [OFNotOpenException exceptionWithObject: self]; if ((uint64_t)length > _toWrite) @throw [OFOutOfRangeException exception]; @try { - bytesWritten = [_stream writeBuffer: buffer - length: length]; + [_stream writeBuffer: buffer length: length]; } @catch (OFWriteFailedException *e) { _toWrite -= e.bytesWritten; + + if (e.errNo == EWOULDBLOCK) + return e.bytesWritten; + @throw e; } - _toWrite -= bytesWritten; + _toWrite -= length; - return bytesWritten; + return length; } - (bool)lowlevelIsAtEndOfStream { if (_stream == nil) Index: src/OFZIPArchive.m ================================================================== --- src/OFZIPArchive.m +++ src/OFZIPArchive.m @@ -40,10 +40,11 @@ #import "OFOpenItemFailedException.h" #import "OFOutOfRangeException.h" #import "OFSeekFailedException.h" #import "OFTruncatedDataException.h" #import "OFUnsupportedVersionException.h" +#import "OFWriteFailedException.h" /* * FIXME: Current limitations: * - Split archives are not supported. * - Encrypted files cannot be read. @@ -874,26 +875,36 @@ [super dealloc]; } - (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length { - size_t bytesWritten; - #if SIZE_MAX >= INT64_MAX if (length > INT64_MAX) @throw [OFOutOfRangeException exception]; #endif if (INT64_MAX - _bytesWritten < (int64_t)length) @throw [OFOutOfRangeException exception]; - bytesWritten = [_stream writeBuffer: buffer length: length]; + @try { + [_stream writeBuffer: buffer length: length]; + } @catch (OFWriteFailedException *e) { + OFEnsure(e.bytesWritten < length); + + _bytesWritten += (int64_t)e.bytesWritten; + _CRC32 = OFCRC32(_CRC32, buffer, e.bytesWritten); + + if (e.errNo == EWOULDBLOCK) + return e.bytesWritten; + + @throw e; + } - _bytesWritten += (int64_t)bytesWritten; + _bytesWritten += (int64_t)length; _CRC32 = OFCRC32(_CRC32, buffer, length); - return bytesWritten; + return length; } - (void)close { if (_stream == nil) Index: src/platform/POSIX/OFSubprocess.m ================================================================== --- src/platform/POSIX/OFSubprocess.m +++ src/platform/POSIX/OFSubprocess.m @@ -329,12 +329,11 @@ _atEndOfStream = true; return ret; } -- (size_t)lowlevelWriteBuffer: (const void *)buffer - length: (size_t)length +- (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length { ssize_t bytesWritten; if (_writePipe[1] == -1) @throw [OFNotOpenException exceptionWithObject: self]; Index: src/platform/Windows/OFSubprocess.m ================================================================== --- src/platform/Windows/OFSubprocess.m +++ src/platform/Windows/OFSubprocess.m @@ -359,11 +359,11 @@ if (GetLastError() == ERROR_BROKEN_PIPE) errNo = EPIPE; @throw [OFWriteFailedException exceptionWithObject: self requestedLength: length - bytesWritten: 0 + bytesWritten: bytesWritten errNo: errNo]; } return (size_t)bytesWritten; } Index: tests/OFTCPSocketTests.m ================================================================== --- tests/OFTCPSocketTests.m +++ tests/OFTCPSocketTests.m @@ -44,14 +44,14 @@ TEST(@"-[remoteAddress]", [OFSocketAddressString(accepted.remoteAddress) isEqual: @"127.0.0.1"]) - TEST(@"-[writeString:]", [client writeString: @"Hello!"]) + TEST(@"-[writeString:]", R([client writeString: @"Hello!"])) TEST(@"-[readIntoBuffer:length:]", [accepted readIntoBuffer: buffer length: 6] && !memcmp(buffer, "Hello!", 6)) objc_autoreleasePoolPop(pool); } @end