Index: src/OFStream.h ================================================================== --- src/OFStream.h +++ src/OFStream.h @@ -35,10 +35,11 @@ char *cache; char *writeBuffer; size_t cacheLength, writeBufferLength; BOOL buffersWrites; BOOL blocking; + BOOL waitingForDelimiter; } #ifdef OF_HAVE_PROPERTIES @property (assign, getter=isBlocking) BOOL blocking; #endif @@ -801,6 +802,10 @@ /** * \brief Closes the stream. */ - (void)close; + +/// \cond internal +- (BOOL)_isWaitingForDelimiter; +/// \endcond @end Index: src/OFStream.m ================================================================== --- src/OFStream.m +++ src/OFStream.m @@ -505,19 +505,18 @@ } return ret; } -- (OFString*)_tryReadLineWithEncoding: (of_string_encoding_t)encoding - checkCache: (BOOL)checkCache +- (OFString*)tryReadLineWithEncoding: (of_string_encoding_t)encoding { size_t i, bufferLength, retLength; char *retCString, *buffer, *newCache; OFString *ret; /* Look if there's a line or \0 in our cache */ - if (checkCache && cache != NULL) { + if (!waitingForDelimiter && cache != NULL) { for (i = 0; i < cacheLength; i++) { if (OF_UNLIKELY(cache[i] == '\n' || cache[i] == '\0')) { retLength = i; @@ -536,10 +535,11 @@ [self freeMemory: cache]; cache = newCache; cacheLength -= i + 1; + waitingForDelimiter = NO; return ret; } } } @@ -546,12 +546,14 @@ /* Read and see if we get a newline or \0 */ buffer = [self allocMemoryWithSize: of_pagesize]; @try { if ([self _isAtEndOfStream]) { - if (cache == NULL) + if (cache == NULL) { + waitingForDelimiter = NO; return nil; + } retLength = cacheLength; if (retLength > 0 && cache[retLength - 1] == '\r') retLength--; @@ -562,10 +564,11 @@ [self freeMemory: cache]; cache = NULL; cacheLength = 0; + waitingForDelimiter = NO; return ret; } bufferLength = [self _readNBytes: of_pagesize intoBuffer: buffer]; @@ -623,10 +626,11 @@ [self freeMemory: cache]; cache = newCache; cacheLength = bufferLength - i - 1; + waitingForDelimiter = NO; return ret; } } /* There was no newline or \0 */ @@ -643,10 +647,11 @@ cacheLength += bufferLength; } @finally { [self freeMemory: buffer]; } + waitingForDelimiter = YES; return nil; } - (OFString*)readLine { @@ -655,16 +660,11 @@ - (OFString*)readLineWithEncoding: (of_string_encoding_t)encoding { OFString *line = nil; - if ((line = [self _tryReadLineWithEncoding: encoding - checkCache: YES]) != nil) - return line; - - while ((line = [self _tryReadLineWithEncoding: encoding - checkCache: NO]) == nil) + while ((line = [self tryReadLineWithEncoding: encoding]) == nil) if ([self isAtEndOfStream]) return nil; return line; } @@ -672,19 +672,12 @@ - (OFString*)tryReadLine { return [self tryReadLineWithEncoding: OF_STRING_ENCODING_UTF_8]; } -- (OFString*)tryReadLineWithEncoding: (of_string_encoding_t)encoding -{ - return [self _tryReadLineWithEncoding: encoding - checkCache: YES]; -} - -- (OFString*)_tryReadTillDelimiter: (OFString*)delimiter - withEncoding: (of_string_encoding_t)encoding - checkCache: (BOOL)checkCache +- (OFString*)tryReadTillDelimiter: (OFString*)delimiter + withEncoding: (of_string_encoding_t)encoding { const char *delimiterUTF8String; size_t i, j, delimiterLength, bufferLength, retLength; char *retCString, *buffer, *newCache; OFString *ret; @@ -697,11 +690,11 @@ if (delimiterLength == 0) @throw [OFInvalidArgumentException newWithClass: isa selector: _cmd]; /* Look if there's something in our cache */ - if (checkCache && cache != NULL) { + if (!waitingForDelimiter && cache != NULL) { for (i = 0; i < cacheLength; i++) { if (cache[i] != delimiterUTF8String[j++]) j = 0; if (j == delimiterLength || cache[i] == '\0') { @@ -721,10 +714,11 @@ [self freeMemory: cache]; cache = newCache; cacheLength -= i + 1; + waitingForDelimiter = NO; return ret; } } } @@ -731,21 +725,24 @@ /* Read and see if we get a delimiter or \0 */ buffer = [self allocMemoryWithSize: of_pagesize]; @try { if ([self _isAtEndOfStream]) { - if (cache == NULL) + if (cache == NULL) { + waitingForDelimiter = NO; return nil; + } ret = [OFString stringWithCString: cache encoding: encoding length: cacheLength]; [self freeMemory: cache]; cache = NULL; cacheLength = 0; + waitingForDelimiter = NO; return ret; } bufferLength = [self _readNBytes: of_pagesize intoBuffer: buffer]; @@ -792,10 +789,11 @@ [self freeMemory: cache]; cache = newCache; cacheLength = bufferLength - i - 1; + waitingForDelimiter = NO; return ret; } } /* Neither the delimiter nor \0 was found */ @@ -813,10 +811,11 @@ cacheLength += bufferLength; } @finally { [self freeMemory: buffer]; } + waitingForDelimiter = YES; return nil; } - (OFString*)readTillDelimiter: (OFString*)delimiter @@ -828,18 +827,13 @@ - (OFString*)readTillDelimiter: (OFString*)delimiter withEncoding: (of_string_encoding_t)encoding { OFString *ret = nil; - if ((ret = [self _tryReadTillDelimiter: delimiter - withEncoding: encoding - checkCache: YES]) != nil) - return ret; - - while ((ret = [self _tryReadTillDelimiter: delimiter - withEncoding: encoding - checkCache: NO]) == nil) + + while ((ret = [self tryReadTillDelimiter: delimiter + withEncoding: encoding]) == nil) if ([self isAtEndOfStream]) return nil; return ret; } @@ -848,18 +842,10 @@ { return [self tryReadTillDelimiter: delimiter withEncoding: OF_STRING_ENCODING_UTF_8]; } -- (OFString*)tryReadTillDelimiter: (OFString*)delimiter - withEncoding: (of_string_encoding_t)encoding -{ - return [self _tryReadTillDelimiter: delimiter - withEncoding: encoding - checkCache: YES]; -} - - (BOOL)buffersWrites { return buffersWrites; } @@ -1402,6 +1388,11 @@ - (void)close { @throw [OFNotImplementedException newWithClass: isa selector: _cmd]; } + +- (BOOL)_isWaitingForDelimiter +{ + return waitingForDelimiter; +} @end Index: src/OFStreamObserver.h ================================================================== --- src/OFStreamObserver.h +++ src/OFStreamObserver.h @@ -40,10 +40,17 @@ #ifdef OF_HAVE_OPTIONAL_PROTOCOLS @optional #endif /** * \brief This callback is called when a stream did get ready for reading. + * + * NOTE: When -[tryReadLine] or -[tryReadTillDelimiter:] has been called on the + * the stream, this callback will not be called again until new data has + * been received, even though there is still data in the cache. The reason + * for this is to prevent spinning in a loop when there is an incomplete + * string in the cache. Once the string is complete, the callback will be + * called again if there is data in the cache. * * \param stream The stream which did become ready for reading */ - (void)streamIsReadyForReading: (OFStream*)stream; Index: src/OFStreamObserver.m ================================================================== --- src/OFStreamObserver.m +++ src/OFStreamObserver.m @@ -354,11 +354,12 @@ BOOL foundInCache = NO; pool = [[OFAutoreleasePool alloc] init]; for (i = 0; i < count; i++) { - if ([cArray[i] pendingBytes] > 0) { + if ([cArray[i] pendingBytes] > 0 && + ![cArray[i] _isWaitingForDelimiter]) { [delegate streamIsReadyForReading: cArray[i]]; foundInCache = YES; [pool releaseObjects]; } }