Index: TODO ================================================================== --- TODO +++ TODO @@ -1,12 +1,12 @@ Test if autorelease pool releases everything correctly when thread is ended Proper UTF-8 support! Tests for OFFile. Tests for OFNumber. - -OFThread +readLine: for OFFile. +Tests for readLine:. Serialization OFQueue OFSortedArray Index: src/OFTCPSocket.h ================================================================== --- src/OFTCPSocket.h +++ src/OFTCPSocket.h @@ -38,16 +38,18 @@ * The OFTCPSocket class provides functions to create and use sockets. */ @interface OFTCPSocket: OFObject { #ifndef _WIN32 - int sock; + int sock; #else - SOCKET sock; + SOCKET sock; #endif - struct sockaddr *saddr; - socklen_t saddr_len; + struct sockaddr *saddr; + socklen_t saddr_len; + char *cache; + size_t cache_len; } /** * \return A new autoreleased OFTCPSocket */ @@ -97,10 +99,34 @@ * \return An OFTCPSocket for the accepted connection, which is NOT * autoreleased! */ - (OFTCPSocket*)accept; +/** + * Read until a newline or \0 occurs. + * + * If you want to use readNBytes afterwards again, you have to clear the cache + * before and optionally get the cache before clearing it! + * + * \return The line that was read. Use freeMem: to free it! + */ +- (char*)readLine; + +/** + * Sets a specified pointer to the cache and returns the length of the cache. + * + * \param ptr A pointer to a pointer. It will be set to the cache. + * If it is NULL, only the number of bytes in the cache is returned. + * \return The number of bytes in the cache. + */ +- (size_t)getCache: (char**)ptr; + +/** + * Clears the cache. + */ +- clearCache; + /** * Enables/disables non-blocking I/O. */ - setBlocking: (BOOL)enable; Index: src/OFTCPSocket.m ================================================================== --- src/OFTCPSocket.m +++ src/OFTCPSocket.m @@ -17,14 +17,17 @@ #include #include #import "OFTCPSocket.h" #import "OFExceptions.h" +#import "OFMacros.h" #ifndef INVALID_SOCKET #define INVALID_SOCKET -1 #endif + +extern int getpagesize(void); @implementation OFTCPSocket + tcpSocket { return [[[OFTCPSocket alloc] init] autorelease]; @@ -47,10 +50,12 @@ self = [super init]; sock = INVALID_SOCKET; saddr = NULL; saddr_len = 0; + cache = NULL; + cache_len = 0; return self; } - free @@ -187,11 +192,11 @@ newsock = [OFTCPSocket new]; addrlen = sizeof(struct sockaddr); @try { addr = [newsock allocWithSize: sizeof(struct sockaddr)]; - } @catch (id e) { + } @catch (OFException *e) { [newsock free]; @throw e; } if ((s = accept(sock, addr, &addrlen)) == INVALID_SOCKET) { @@ -223,10 +228,128 @@ } /* This is safe, as we already checked < 1 */ return ret; } + +- (char*)readLine +{ + size_t i, len; + char *ret, *tmp, *tmp2; + + /* Look if there's a line or \0 in our cache */ + if (cache != NULL) { + for (i = 0; i < cache_len; i++) { + if (OF_UNLIKELY(cache[i] == '\n' || + cache[i] == '\0')) { + ret = [self allocWithSize: i + 1]; + memcpy(ret, cache, i); + ret[i] = '\0'; + + @try { + tmp = [self allocWithSize: cache_len - + i - 1]; + } @catch (OFException *e) { + [self freeMem: ret]; + @throw e; + } + memcpy(tmp, cache + i + 1, cache_len - i - 1); + + [self freeMem: cache]; + cache = tmp; + cache_len = cache_len - i - 1; + + return ret; + } + } + } + + /* Read until we get a newline or \0 */ + tmp = [self allocWithSize: getpagesize()]; + + for (;;) { + @try { + len = [self readNBytes: getpagesize() - 1 + intoBuffer: tmp]; + } @catch (OFException *e) { + [self freeMem: tmp]; + @throw e; + } + + /* Look if there's a newline or \0 */ + for (i = 0; i < len; i++) { + if (OF_UNLIKELY(tmp[i] == '\n' || tmp[i] == '\0')) { + @try { + ret = [self + allocWithSize: cache_len + i + 1]; + } @catch (OFException *e) { + [self freeMem: tmp]; + @throw e; + } + if (cache != NULL) + memcpy(ret, cache, cache_len); + memcpy(ret + cache_len, tmp, i); + ret[i] = '\0'; + + if (i < len) { + @try { + tmp2 = [self + allocWithSize: len - i - 1]; + } @catch (OFException *e) { + [self freeMem: ret]; + [self freeMem: tmp]; + @throw e; + } + memcpy(tmp2, tmp + i + 1, len - i - 1); + + if (cache != NULL) + [self freeMem: cache]; + cache = tmp2; + cache_len = len - i - 1; + } else { + if (cache != NULL) + [self freeMem: cache]; + cache = NULL; + cache_len = 0; + } + + [self freeMem: tmp]; + return ret; + } + } + + /* There was no newline or \0 */ + @try { + cache = [self resizeMem: cache + toSize: cache_len + len]; + } @catch (OFException *e) { + [self freeMem: tmp]; + @throw e; + } + memcpy(cache + cache_len, tmp, len); + cache_len += len; + } +} + +- (size_t)getCache: (char**)ptr +{ + if (ptr != NULL) + *ptr = cache; + + return cache_len; +} + +- clearCache +{ + if (cache != NULL) + [self freeMem: cache]; + + cache = NULL; + cache_len = 0; + + return self; +} - (size_t)writeNBytes: (size_t)size fromBuffer: (const char*)buf { ssize_t ret;