/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 * Jonathan Schleifer <js@heap.zone> * * All rights reserved. * * This file is part of ObjFW. It may be distributed under the terms of the * Q Public License 1.0, which can be found in the file LICENSE.QPL included in * the packaging of this file. * * Alternatively, it may be distributed under the terms of the GNU General * Public License, either version 2 or 3, which can be found in the file * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this * file. */ #define __NO_EXT_QNX #include "config.h" #include <assert.h> #include <errno.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include "platform.h" #ifndef OF_WINDOWS # include <signal.h> #endif #import "OFStream.h" #import "OFStream+Private.h" #import "OFString.h" #import "OFDataArray.h" #import "OFSystemInfo.h" #import "OFRunLoop.h" #import "OFRunLoop+Private.h" #import "OFInvalidArgumentException.h" #import "OFInvalidFormatException.h" #import "OFNotImplementedException.h" #import "OFOutOfRangeException.h" #import "OFSetOptionFailedException.h" #import "of_asprintf.h" #define MIN_READ_SIZE 512 @implementation OFStream @synthesize OF_isWaitingForDelimiter = _waitingForDelimiter; #ifndef OF_WINDOWS + (void)initialize { if (self == [OFStream class]) signal(SIGPIPE, SIG_IGN); } #endif - init { if (object_getClass(self) == [OFStream class]) { @try { [self doesNotRecognizeSelector: _cmd]; abort(); } @catch (id e) { [self release]; @throw e; } } self = [super init]; _blocking = true; return self; } - (bool)lowlevelIsAtEndOfStream { OF_UNRECOGNIZED_SELECTOR } - (size_t)lowlevelReadIntoBuffer: (void*)buffer length: (size_t)length { OF_UNRECOGNIZED_SELECTOR } - (void)lowlevelWriteBuffer: (const void*)buffer length: (size_t)length { OF_UNRECOGNIZED_SELECTOR } - copy { return [self retain]; } - (bool)isAtEndOfStream { if (_readBufferLength > 0) return false; return [self lowlevelIsAtEndOfStream]; } - (size_t)readIntoBuffer: (void*)buffer length: (size_t)length { if (_readBufferLength == 0) { /* * For small sizes, it is cheaper to read more and cache the * remainder - even if that means more copying of data - than * to do a syscall for every read. */ if (length < MIN_READ_SIZE) { char tmp[MIN_READ_SIZE], *readBuffer; size_t bytesRead; bytesRead = [self lowlevelReadIntoBuffer: tmp length: MIN_READ_SIZE]; if (bytesRead > length) { memcpy(buffer, tmp, length); readBuffer = [self allocMemoryWithSize: bytesRead - length]; memcpy(readBuffer, tmp + length, bytesRead - length); _readBuffer = _readBufferMemory = readBuffer; _readBufferLength = bytesRead - length; return length; } else { memcpy(buffer, tmp, bytesRead); return bytesRead; } } return [self lowlevelReadIntoBuffer: buffer length: length]; } if (length >= _readBufferLength) { size_t ret = _readBufferLength; memcpy(buffer, _readBuffer, _readBufferLength); [self freeMemory: _readBufferMemory]; _readBuffer = _readBufferMemory = NULL; _readBufferLength = 0; return ret; } else { memcpy(buffer, _readBuffer, length); _readBuffer += length; _readBufferLength -= length; return length; } } - (void)readIntoBuffer: (void*)buffer exactLength: (size_t)length { size_t readLength = 0; while (readLength < length) readLength += [self readIntoBuffer: (char*)buffer + readLength length: length - readLength]; } #ifdef OF_HAVE_SOCKETS - (void)asyncReadIntoBuffer: (void*)buffer length: (size_t)length target: (id)target selector: (SEL)selector { [OFRunLoop OF_addAsyncReadForStream: self buffer: buffer length: length target: target selector: selector]; } - (void)asyncReadIntoBuffer: (void*)buffer exactLength: (size_t)length target: (id)target selector: (SEL)selector { [OFRunLoop OF_addAsyncReadForStream: self buffer: buffer exactLength: length target: target selector: selector]; } # ifdef OF_HAVE_BLOCKS - (void)asyncReadIntoBuffer: (void*)buffer length: (size_t)length block: (of_stream_async_read_block_t)block { [OFRunLoop OF_addAsyncReadForStream: self buffer: buffer length: length block: block]; } - (void)asyncReadIntoBuffer: (void*)buffer exactLength: (size_t)length block: (of_stream_async_read_block_t)block { [OFRunLoop OF_addAsyncReadForStream: self buffer: buffer exactLength: length block: block]; } # endif #endif - (uint8_t)readInt8 { uint8_t ret; [self readIntoBuffer: (char*)&ret exactLength: 1]; return ret; } - (uint16_t)readBigEndianInt16 { uint16_t ret; [self readIntoBuffer: (char*)&ret exactLength: 2]; return OF_BSWAP16_IF_LE(ret); } - (uint32_t)readBigEndianInt32 { uint32_t ret; [self readIntoBuffer: (char*)&ret exactLength: 4]; return OF_BSWAP32_IF_LE(ret); } - (uint64_t)readBigEndianInt64 { uint64_t ret; [self readIntoBuffer: (char*)&ret exactLength: 8]; return OF_BSWAP64_IF_LE(ret); } - (float)readBigEndianFloat { float ret; [self readIntoBuffer: (char*)&ret exactLength: 4]; return OF_BSWAP_FLOAT_IF_LE(ret); } - (double)readBigEndianDouble { double ret; [self readIntoBuffer: (char*)&ret exactLength: 8]; return OF_BSWAP_DOUBLE_IF_LE(ret); } - (size_t)readBigEndianInt16sIntoBuffer: (uint16_t*)buffer count: (size_t)count { size_t size = count * sizeof(uint16_t); [self readIntoBuffer: buffer exactLength: size]; #ifndef OF_BIG_ENDIAN for (size_t i = 0; i < count; i++) buffer[i] = OF_BSWAP16(buffer[i]); #endif return size; } - (size_t)readBigEndianInt32sIntoBuffer: (uint32_t*)buffer count: (size_t)count { size_t size = count * sizeof(uint32_t); [self readIntoBuffer: buffer exactLength: size]; #ifndef OF_BIG_ENDIAN for (size_t i = 0; i < count; i++) buffer[i] = OF_BSWAP32(buffer[i]); #endif return size; } - (size_t)readBigEndianInt64sIntoBuffer: (uint64_t*)buffer count: (size_t)count { size_t size = count * sizeof(uint64_t); [self readIntoBuffer: buffer exactLength: size]; #ifndef OF_BIG_ENDIAN for (size_t i = 0; i < count; i++) buffer[i] = OF_BSWAP64(buffer[i]); #endif return size; } - (size_t)readBigEndianFloatsIntoBuffer: (float*)buffer count: (size_t)count { size_t size = count * sizeof(float); [self readIntoBuffer: buffer exactLength: size]; #ifndef OF_FLOAT_BIG_ENDIAN for (size_t i = 0; i < count; i++) buffer[i] = OF_BSWAP_FLOAT(buffer[i]); #endif return size; } - (size_t)readBigEndianDoublesIntoBuffer: (double*)buffer count: (size_t)count { size_t size = count * sizeof(double); [self readIntoBuffer: buffer exactLength: size]; #ifndef OF_FLOAT_BIG_ENDIAN for (size_t i = 0; i < count; i++) buffer[i] = OF_BSWAP_DOUBLE(buffer[i]); #endif return size; } - (uint16_t)readLittleEndianInt16 { uint16_t ret; [self readIntoBuffer: (char*)&ret exactLength: 2]; return OF_BSWAP16_IF_BE(ret); } - (uint32_t)readLittleEndianInt32 { uint32_t ret; [self readIntoBuffer: (char*)&ret exactLength: 4]; return OF_BSWAP32_IF_BE(ret); } - (uint64_t)readLittleEndianInt64 { uint64_t ret; [self readIntoBuffer: (char*)&ret exactLength: 8]; return OF_BSWAP64_IF_BE(ret); } - (float)readLittleEndianFloat { float ret; [self readIntoBuffer: (char*)&ret exactLength: 4]; return OF_BSWAP_FLOAT_IF_BE(ret); } - (double)readLittleEndianDouble { double ret; [self readIntoBuffer: (char*)&ret exactLength: 8]; return OF_BSWAP_DOUBLE_IF_BE(ret); } - (size_t)readLittleEndianInt16sIntoBuffer: (uint16_t*)buffer count: (size_t)count { size_t size = count * sizeof(uint16_t); [self readIntoBuffer: buffer exactLength: size]; #ifdef OF_BIG_ENDIAN for (size_t i = 0; i < count; i++) buffer[i] = OF_BSWAP16(buffer[i]); #endif return size; } - (size_t)readLittleEndianInt32sIntoBuffer: (uint32_t*)buffer count: (size_t)count { size_t size = count * sizeof(uint32_t); [self readIntoBuffer: buffer exactLength: size]; #ifdef OF_BIG_ENDIAN for (size_t i = 0; i < count; i++) buffer[i] = OF_BSWAP32(buffer[i]); #endif return size; } - (size_t)readLittleEndianInt64sIntoBuffer: (uint64_t*)buffer count: (size_t)count { size_t size = count * sizeof(uint64_t); [self readIntoBuffer: buffer exactLength: size]; #ifdef OF_BIG_ENDIAN for (size_t i = 0; i < count; i++) buffer[i] = OF_BSWAP64(buffer[i]); #endif return size; } - (size_t)readLittleEndianFloatsIntoBuffer: (float*)buffer count: (size_t)count { size_t size = count * sizeof(float); [self readIntoBuffer: buffer exactLength: size]; #ifdef OF_FLOAT_BIG_ENDIAN for (size_t i = 0; i < count; i++) buffer[i] = OF_BSWAP_FLOAT(buffer[i]); #endif return size; } - (size_t)readLittleEndianDoublesIntoBuffer: (double*)buffer count: (size_t)count { size_t size = count * sizeof(double); [self readIntoBuffer: buffer exactLength: size]; #ifdef OF_FLOAT_BIG_ENDIAN for (size_t i = 0; i < count; i++) buffer[i] = OF_BSWAP_DOUBLE(buffer[i]); #endif return size; } - (OFDataArray*)readDataArrayWithCount: (size_t)count { return [self readDataArrayWithItemSize: 1 count: count]; } - (OFDataArray*)readDataArrayWithItemSize: (size_t)itemSize count: (size_t)count { OFDataArray *dataArray; char *tmp; dataArray = [OFDataArray dataArrayWithItemSize: itemSize]; tmp = [self allocMemoryWithSize: itemSize count: count]; @try { [self readIntoBuffer: tmp exactLength: count * itemSize]; [dataArray addItems: tmp count: count]; } @finally { [self freeMemory: tmp]; } return dataArray; } - (OFDataArray*)readDataArrayTillEndOfStream { OFDataArray *dataArray; size_t pageSize; char *buffer; dataArray = [OFDataArray dataArray]; pageSize = [OFSystemInfo pageSize]; buffer = [self allocMemoryWithSize: pageSize]; @try { while (![self isAtEndOfStream]) { size_t length; length = [self readIntoBuffer: buffer length: pageSize]; [dataArray addItems: buffer count: length]; } } @finally { [self freeMemory: buffer]; } return dataArray; } - (OFString*)readStringWithLength: (size_t)length { return [self readStringWithLength: length encoding: OF_STRING_ENCODING_UTF_8]; } - (OFString*)readStringWithLength: (size_t)length encoding: (of_string_encoding_t)encoding { OFString *ret; char *buffer = [self allocMemoryWithSize: length + 1]; buffer[length] = 0; @try { [self readIntoBuffer: buffer exactLength: length]; ret = [OFString stringWithCString: buffer encoding: encoding]; } @finally { [self freeMemory: buffer]; } return ret; } - (OFString*)tryReadLineWithEncoding: (of_string_encoding_t)encoding { size_t pageSize, bufferLength, retLength; char *retCString, *buffer, *readBuffer; OFString *ret; /* Look if there's a line or \0 in our buffer */ if (!_waitingForDelimiter && _readBuffer != NULL) { for (size_t i = 0; i < _readBufferLength; i++) { if OF_UNLIKELY (_readBuffer[i] == '\n' || _readBuffer[i] == '\0') { retLength = i; if (i > 0 && _readBuffer[i - 1] == '\r') retLength--; ret = [OFString stringWithCString: _readBuffer encoding: encoding length: retLength]; _readBuffer += i + 1; _readBufferLength -= i + 1; _waitingForDelimiter = false; return ret; } } } /* Read and see if we got a newline or \0 */ pageSize = [OFSystemInfo pageSize]; buffer = [self allocMemoryWithSize: pageSize]; @try { if ([self lowlevelIsAtEndOfStream]) { if (_readBuffer == NULL) { _waitingForDelimiter = false; return nil; } retLength = _readBufferLength; if (retLength > 0 && _readBuffer[retLength - 1] == '\r') retLength--; ret = [OFString stringWithCString: _readBuffer encoding: encoding length: retLength]; [self freeMemory: _readBufferMemory]; _readBuffer = _readBufferMemory = NULL; _readBufferLength = 0; _waitingForDelimiter = false; return ret; } bufferLength = [self lowlevelReadIntoBuffer: buffer length: pageSize]; /* Look if there's a newline or \0 */ for (size_t i = 0; i < bufferLength; i++) { if OF_UNLIKELY (buffer[i] == '\n' || buffer[i] == '\0') { retLength = _readBufferLength + i; retCString = [self allocMemoryWithSize: retLength]; if (_readBuffer != NULL) memcpy(retCString, _readBuffer, _readBufferLength); memcpy(retCString + _readBufferLength, buffer, i); if (retLength > 0 && retCString[retLength - 1] == '\r') retLength--; @try { char *rcs = retCString; size_t rl = retLength; ret = [OFString stringWithCString: rcs encoding: encoding length: rl]; } @catch (id e) { if (bufferLength > 0) { /* * Append data to _readBuffer * to prevent loss of data due * to wrong encoding. */ readBuffer = [self allocMemoryWithSize: _readBufferLength + bufferLength]; memcpy(readBuffer, _readBuffer, _readBufferLength); memcpy(readBuffer + _readBufferLength, buffer, bufferLength); [self freeMemory: _readBufferMemory]; _readBuffer = readBuffer; _readBufferMemory = readBuffer; _readBufferLength += bufferLength; } @throw e; } @finally { [self freeMemory: retCString]; } readBuffer = [self allocMemoryWithSize: bufferLength - i - 1]; if (readBuffer != NULL) memcpy(readBuffer, buffer + i + 1, bufferLength - i - 1); [self freeMemory: _readBufferMemory]; _readBuffer = _readBufferMemory = readBuffer; _readBufferLength = bufferLength - i - 1; _waitingForDelimiter = false; return ret; } } /* There was no newline or \0 */ if (bufferLength > 0) { readBuffer = [self allocMemoryWithSize: _readBufferLength + bufferLength]; memcpy(readBuffer, _readBuffer, _readBufferLength); memcpy(readBuffer + _readBufferLength, buffer, bufferLength); [self freeMemory: _readBufferMemory]; _readBuffer = _readBufferMemory = readBuffer; _readBufferLength += bufferLength; } } @finally { [self freeMemory: buffer]; } _waitingForDelimiter = true; return nil; } - (OFString*)readLine { return [self readLineWithEncoding: OF_STRING_ENCODING_UTF_8]; } - (OFString*)readLineWithEncoding: (of_string_encoding_t)encoding { OFString *line = nil; while ((line = [self tryReadLineWithEncoding: encoding]) == nil) if ([self isAtEndOfStream]) return nil; return line; } #ifdef OF_HAVE_SOCKETS - (void)asyncReadLineWithTarget: (id)target selector: (SEL)selector { [self asyncReadLineWithEncoding: OF_STRING_ENCODING_UTF_8 target: target selector: selector]; } - (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding target: (id)target selector: (SEL)selector { [OFRunLoop OF_addAsyncReadLineForStream: self encoding: encoding target: target selector: selector]; } # ifdef OF_HAVE_BLOCKS - (void)asyncReadLineWithBlock: (of_stream_async_read_line_block_t)block { [self asyncReadLineWithEncoding: OF_STRING_ENCODING_UTF_8 block: block]; } - (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding block: (of_stream_async_read_line_block_t)block { [OFRunLoop OF_addAsyncReadLineForStream: self encoding: encoding block: block]; } # endif #endif - (OFString*)tryReadLine { return [self tryReadLineWithEncoding: OF_STRING_ENCODING_UTF_8]; } - (OFString*)tryReadTillDelimiter: (OFString*)delimiter encoding: (of_string_encoding_t)encoding { const char *delimiterCString; size_t j, delimiterLength, pageSize, bufferLength, retLength; char *retCString, *buffer, *readBuffer; OFString *ret; delimiterCString = [delimiter cStringWithEncoding: encoding]; delimiterLength = [delimiter cStringLengthWithEncoding: encoding]; j = 0; if (delimiterLength == 0) @throw [OFInvalidArgumentException exception]; /* Look if there's something in our buffer */ if (!_waitingForDelimiter && _readBuffer != NULL) { for (size_t i = 0; i < _readBufferLength; i++) { if (_readBuffer[i] != delimiterCString[j++]) j = 0; if (j == delimiterLength || _readBuffer[i] == '\0') { if (_readBuffer[i] == '\0') delimiterLength = 1; ret = [OFString stringWithCString: _readBuffer encoding: encoding length: i + 1 - delimiterLength]; _readBuffer += i + 1; _readBufferLength -= i + 1; _waitingForDelimiter = false; return ret; } } } /* Read and see if we got a delimiter or \0 */ pageSize = [OFSystemInfo pageSize]; buffer = [self allocMemoryWithSize: pageSize]; @try { if ([self lowlevelIsAtEndOfStream]) { if (_readBuffer == NULL) { _waitingForDelimiter = false; return nil; } ret = [OFString stringWithCString: _readBuffer encoding: encoding length: _readBufferLength]; [self freeMemory: _readBufferMemory]; _readBuffer = _readBufferMemory = NULL; _readBufferLength = 0; _waitingForDelimiter = false; return ret; } bufferLength = [self lowlevelReadIntoBuffer: buffer length: pageSize]; /* Look if there's a delimiter or \0 */ for (size_t i = 0; i < bufferLength; i++) { if (buffer[i] != delimiterCString[j++]) j = 0; if (j == delimiterLength || buffer[i] == '\0') { if (buffer[i] == '\0') delimiterLength = 1; retLength = _readBufferLength + i + 1 - delimiterLength; retCString = [self allocMemoryWithSize: retLength]; if (_readBuffer != NULL && _readBufferLength <= retLength) memcpy(retCString, _readBuffer, _readBufferLength); else if (_readBuffer != NULL) memcpy(retCString, _readBuffer, retLength); if (i >= delimiterLength) memcpy(retCString + _readBufferLength, buffer, i + 1 - delimiterLength); @try { char *rcs = retCString; size_t rl = retLength; ret = [OFString stringWithCString: rcs encoding: encoding length: rl]; } @finally { [self freeMemory: retCString]; } readBuffer = [self allocMemoryWithSize: bufferLength - i - 1]; if (readBuffer != NULL) memcpy(readBuffer, buffer + i + 1, bufferLength - i - 1); [self freeMemory: _readBufferMemory]; _readBuffer = _readBufferMemory = readBuffer; _readBufferLength = bufferLength - i - 1; _waitingForDelimiter = false; return ret; } } /* Neither the delimiter nor \0 was found */ if (bufferLength > 0) { readBuffer = [self allocMemoryWithSize: _readBufferLength + bufferLength]; memcpy(readBuffer, _readBuffer, _readBufferLength); memcpy(readBuffer + _readBufferLength, buffer, bufferLength); [self freeMemory: _readBufferMemory]; _readBuffer = _readBufferMemory = readBuffer; _readBufferLength += bufferLength; } } @finally { [self freeMemory: buffer]; } _waitingForDelimiter = true; return nil; } - (OFString*)readTillDelimiter: (OFString*)delimiter { return [self readTillDelimiter: delimiter encoding: OF_STRING_ENCODING_UTF_8]; } - (OFString*)readTillDelimiter: (OFString*)delimiter encoding: (of_string_encoding_t)encoding { OFString *ret = nil; while ((ret = [self tryReadTillDelimiter: delimiter encoding: encoding]) == nil) if ([self isAtEndOfStream]) return nil; return ret; } - (OFString*)tryReadTillDelimiter: (OFString*)delimiter { return [self tryReadTillDelimiter: delimiter encoding: OF_STRING_ENCODING_UTF_8]; } - (bool)isWriteBuffered { return _writeBuffered; } - (void)setWriteBuffered: (bool)enable { _writeBuffered = enable; } - (void)flushWriteBuffer { if (_writeBuffer == NULL) return; [self lowlevelWriteBuffer: _writeBuffer length: _writeBufferLength]; [self freeMemory: _writeBuffer]; _writeBuffer = NULL; _writeBufferLength = 0; } - (void)writeBuffer: (const void*)buffer length: (size_t)length { if (!_writeBuffered) [self lowlevelWriteBuffer: buffer length: length]; else { _writeBuffer = [self resizeMemory: _writeBuffer size: _writeBufferLength + length]; memcpy(_writeBuffer + _writeBufferLength, buffer, length); _writeBufferLength += length; } } - (void)writeInt8: (uint8_t)int8 { [self writeBuffer: (char*)&int8 length: 1]; } - (void)writeBigEndianInt16: (uint16_t)int16 { int16 = OF_BSWAP16_IF_LE(int16); [self writeBuffer: (char*)&int16 length: 2]; } - (void)writeBigEndianInt32: (uint32_t)int32 { int32 = OF_BSWAP32_IF_LE(int32); [self writeBuffer: (char*)&int32 length: 4]; } - (void)writeBigEndianInt64: (uint64_t)int64 { int64 = OF_BSWAP64_IF_LE(int64); [self writeBuffer: (char*)&int64 length: 8]; } - (void)writeBigEndianFloat: (float)float_ { float_ = OF_BSWAP_FLOAT_IF_LE(float_); [self writeBuffer: (char*)&float_ length: 4]; } - (void)writeBigEndianDouble: (double)double_ { double_ = OF_BSWAP_DOUBLE_IF_LE(double_); [self writeBuffer: (char*)&double_ length: 8]; } - (size_t)writeBigEndianInt16s: (const uint16_t*)buffer count: (size_t)count { size_t size = count * sizeof(uint16_t); #ifdef OF_BIG_ENDIAN [self writeBuffer: buffer length: size]; #else uint16_t *tmp = [self allocMemoryWithSize: sizeof(uint16_t) count: count]; @try { for (size_t i = 0; i < count; i++) tmp[i] = OF_BSWAP16(buffer[i]); [self writeBuffer: tmp length: size]; } @finally { [self freeMemory: tmp]; } #endif return size; } - (size_t)writeBigEndianInt32s: (const uint32_t*)buffer count: (size_t)count { size_t size = count * sizeof(uint32_t); #ifdef OF_BIG_ENDIAN [self writeBuffer: buffer length: size]; #else uint32_t *tmp = [self allocMemoryWithSize: sizeof(uint32_t) count: count]; @try { for (size_t i = 0; i < count; i++) tmp[i] = OF_BSWAP32(buffer[i]); [self writeBuffer: tmp length: size]; } @finally { [self freeMemory: tmp]; } #endif return size; } - (size_t)writeBigEndianInt64s: (const uint64_t*)buffer count: (size_t)count { size_t size = count * sizeof(uint64_t); #ifdef OF_BIG_ENDIAN [self writeBuffer: buffer length: size]; #else uint64_t *tmp = [self allocMemoryWithSize: sizeof(uint64_t) count: count]; @try { for (size_t i = 0; i < count; i++) tmp[i] = OF_BSWAP64(buffer[i]); [self writeBuffer: tmp length: size]; } @finally { [self freeMemory: tmp]; } #endif return size; } - (size_t)writeBigEndianFloats: (const float*)buffer count: (size_t)count { size_t size = count * sizeof(float); #ifdef OF_FLOAT_BIG_ENDIAN [self writeBuffer: buffer length: size]; #else float *tmp = [self allocMemoryWithSize: sizeof(float) count: count]; @try { for (size_t i = 0; i < count; i++) tmp[i] = OF_BSWAP_FLOAT(buffer[i]); [self writeBuffer: tmp length: size]; } @finally { [self freeMemory: tmp]; } #endif return size; } - (size_t)writeBigEndianDoubles: (const double*)buffer count: (size_t)count { size_t size = count * sizeof(double); #ifdef OF_FLOAT_BIG_ENDIAN [self writeBuffer: buffer length: size]; #else double *tmp = [self allocMemoryWithSize: sizeof(double) count: count]; @try { for (size_t i = 0; i < count; i++) tmp[i] = OF_BSWAP_DOUBLE(buffer[i]); [self writeBuffer: tmp length: size]; } @finally { [self freeMemory: tmp]; } #endif return size; } - (void)writeLittleEndianInt16: (uint16_t)int16 { int16 = OF_BSWAP16_IF_BE(int16); [self writeBuffer: (char*)&int16 length: 2]; } - (void)writeLittleEndianInt32: (uint32_t)int32 { int32 = OF_BSWAP32_IF_BE(int32); [self writeBuffer: (char*)&int32 length: 4]; } - (void)writeLittleEndianInt64: (uint64_t)int64 { int64 = OF_BSWAP64_IF_BE(int64); [self writeBuffer: (char*)&int64 length: 8]; } - (void)writeLittleEndianFloat: (float)float_ { float_ = OF_BSWAP_FLOAT_IF_BE(float_); [self writeBuffer: (char*)&float_ length: 4]; } - (void)writeLittleEndianDouble: (double)double_ { double_ = OF_BSWAP_DOUBLE_IF_BE(double_); [self writeBuffer: (char*)&double_ length: 8]; } - (size_t)writeLittleEndianInt16s: (const uint16_t*)buffer count: (size_t)count { size_t size = count * sizeof(uint16_t); #ifndef OF_BIG_ENDIAN [self writeBuffer: buffer length: size]; #else uint16_t *tmp = [self allocMemoryWithSize: sizeof(uint16_t) count: count]; @try { for (size_t i = 0; i < count; i++) tmp[i] = OF_BSWAP16(buffer[i]); [self writeBuffer: tmp length: size]; } @finally { [self freeMemory: tmp]; } #endif return size; } - (size_t)writeLittleEndianInt32s: (const uint32_t*)buffer count: (size_t)count { size_t size = count * sizeof(uint32_t); #ifndef OF_BIG_ENDIAN [self writeBuffer: buffer length: size]; #else uint32_t *tmp = [self allocMemoryWithSize: sizeof(uint32_t) count: count]; @try { for (size_t i = 0; i < count; i++) tmp[i] = OF_BSWAP32(buffer[i]); [self writeBuffer: tmp length: size]; } @finally { [self freeMemory: tmp]; } #endif return size; } - (size_t)writeLittleEndianInt64s: (const uint64_t*)buffer count: (size_t)count { size_t size = count * sizeof(uint64_t); #ifndef OF_BIG_ENDIAN [self writeBuffer: buffer length: size]; #else uint64_t *tmp = [self allocMemoryWithSize: sizeof(uint64_t) count: count]; @try { for (size_t i = 0; i < count; i++) tmp[i] = OF_BSWAP64(buffer[i]); [self writeBuffer: tmp length: size]; } @finally { [self freeMemory: tmp]; } #endif return size; } - (size_t)writeLittleEndianFloats: (const float*)buffer count: (size_t)count { size_t size = count * sizeof(float); #ifndef OF_FLOAT_BIG_ENDIAN [self writeBuffer: buffer length: size]; #else float *tmp = [self allocMemoryWithSize: sizeof(float) count: count]; @try { for (size_t i = 0; i < count; i++) tmp[i] = OF_BSWAP_FLOAT(buffer[i]); [self writeBuffer: tmp length: size]; } @finally { [self freeMemory: tmp]; } #endif return size; } - (size_t)writeLittleEndianDoubles: (const double*)buffer count: (size_t)count { size_t size = count * sizeof(double); #ifndef OF_FLOAT_BIG_ENDIAN [self writeBuffer: buffer length: size]; #else double *tmp = [self allocMemoryWithSize: sizeof(double) count: count]; @try { for (size_t i = 0; i < count; i++) tmp[i] = OF_BSWAP_DOUBLE(buffer[i]); [self writeBuffer: tmp length: size]; } @finally { [self freeMemory: tmp]; } #endif return size; } - (size_t)writeDataArray: (OFDataArray*)dataArray { size_t length = [dataArray count] * [dataArray itemSize]; [self writeBuffer: [dataArray items] length: length]; return length; } - (size_t)writeString: (OFString*)string { return [self writeString: string encoding: OF_STRING_ENCODING_UTF_8]; } - (size_t)writeString: (OFString*)string encoding: (of_string_encoding_t)encoding { size_t length = [string cStringLengthWithEncoding: encoding]; [self writeBuffer: [string cStringWithEncoding: encoding] length: length]; return length; } - (size_t)writeLine: (OFString*)string { return [self writeLine: string encoding: OF_STRING_ENCODING_UTF_8]; } - (size_t)writeLine: (OFString*)string encoding: (of_string_encoding_t)encoding { size_t stringLength = [string cStringLengthWithEncoding: encoding]; char *buffer; buffer = [self allocMemoryWithSize: stringLength + 1]; @try { memcpy(buffer, [string cStringWithEncoding: encoding], stringLength); buffer[stringLength] = '\n'; [self writeBuffer: buffer length: stringLength + 1]; } @finally { [self freeMemory: buffer]; } return stringLength + 1; } - (size_t)writeFormat: (OFConstantString*)format, ... { va_list arguments; size_t ret; va_start(arguments, format); ret = [self writeFormat: format arguments: arguments]; va_end(arguments); return ret; } - (size_t)writeFormat: (OFConstantString*)format arguments: (va_list)arguments { char *UTF8String; int length; if (format == nil) @throw [OFInvalidArgumentException exception]; if ((length = of_vasprintf(&UTF8String, [format UTF8String], arguments)) == -1) @throw [OFInvalidFormatException exception]; @try { [self writeBuffer: UTF8String length: length]; } @finally { free(UTF8String); } return length; } - (bool)hasDataInReadBuffer { return (_readBufferLength > 0); } - (bool)isBlocking { return _blocking; } - (void)setBlocking: (bool)enable { #ifdef HAVE_FCNTL bool readImplemented = false, writeImplemented = false; @try { int readFlags; readFlags = fcntl([self fileDescriptorForReading], F_GETFL); readImplemented = true; if (readFlags == -1) @throw [OFSetOptionFailedException exceptionWithStream: self errNo: errno]; if (enable) readFlags &= ~O_NONBLOCK; else readFlags |= O_NONBLOCK; if (fcntl([self fileDescriptorForReading], F_SETFL, readFlags) == -1) @throw [OFSetOptionFailedException exceptionWithStream: self errNo: errno]; } @catch (OFNotImplementedException *e) { } @try { int writeFlags; writeFlags = fcntl([self fileDescriptorForWriting], F_GETFL); writeImplemented = true; if (writeFlags == -1) @throw [OFSetOptionFailedException exceptionWithStream: self errNo: errno]; if (enable) writeFlags &= ~O_NONBLOCK; else writeFlags |= O_NONBLOCK; if (fcntl([self fileDescriptorForWriting], F_SETFL, writeFlags) == -1) @throw [OFSetOptionFailedException exceptionWithStream: self errNo: errno]; } @catch (OFNotImplementedException *e) { } if (!readImplemented && !writeImplemented) @throw [OFNotImplementedException exceptionWithSelector: _cmd object: self]; _blocking = enable; #else OF_UNRECOGNIZED_SELECTOR #endif } - (int)fileDescriptorForReading { OF_UNRECOGNIZED_SELECTOR } - (int)fileDescriptorForWriting { OF_UNRECOGNIZED_SELECTOR } #ifdef OF_HAVE_SOCKETS - (void)cancelAsyncRequests { [OFRunLoop OF_cancelAsyncRequestsForObject: self]; } #endif - (void)unreadFromBuffer: (const void*)buffer length: (size_t)length { char *readBuffer; if (length > SIZE_MAX - _readBufferLength) @throw [OFOutOfRangeException exception]; readBuffer = [self allocMemoryWithSize: _readBufferLength + length]; memcpy(readBuffer, buffer, length); memcpy(readBuffer + length, _readBuffer, _readBufferLength); [self freeMemory: _readBufferMemory]; _readBuffer = _readBufferMemory = readBuffer; _readBufferLength += length; } - (void)close { [self freeMemory: _readBufferMemory]; _readBuffer = _readBufferMemory = NULL; _readBufferLength = 0; [self freeMemory: _writeBuffer]; _writeBuffer = NULL; _writeBufferLength = 0; _writeBuffered = false; _waitingForDelimiter = false; _blocking = false; } @end