Index: TODO ================================================================== --- TODO +++ TODO @@ -1,9 +1,8 @@ Test if autorelease pool releases everything correctly when thread is ended Make strings \0-safe -Make OFStream caching transparent to the coder Cleanup OFXMLElement Add finalization to OFXMLParser Add namespaces to OFXMLParser Index: src/OFExceptions.h ================================================================== --- src/OFExceptions.h +++ src/OFExceptions.h @@ -277,45 +277,21 @@ * An OFException indicating a read or write to the file failed. */ @interface OFReadOrWriteFailedException: OFException { size_t req_size; - size_t req_items; - BOOL has_items; int err; } -/** - * \param class_ The class of the object which caused the exception - * \param size The requested size of the data that couldn't be read / written - * \param items The requested number of items that couldn't be read / written - * \return A new open file failed exception - */ -+ newWithClass: (Class)class_ - size: (size_t)size - items: (size_t)items; - /** * \param class_ The class of the object which caused the exception * \param size The requested size of the data that couldn't be read / written * \return A new open file failed exception */ + newWithClass: (Class)class_ size: (size_t)size; -/** - * Initializes an already allocated read or write failed exception. - * - * \param class_ The class of the object which caused the exception - * \param size The requested size of the data that couldn't be read / written - * \param items The requested number of items that couldn't be read / written - * \return A new open file failed exception - */ -- initWithClass: (Class)class_ - size: (size_t)size - items: (size_t)items; - /** * Initializes an already allocated read or write failed exception. * * \param class_ The class of the object which caused the exception * \param size The requested size of the data that couldn't be read / written @@ -331,20 +307,10 @@ /** * \return The requested size of the data that couldn't be read / written */ - (size_t)requestedSize; - -/** - * \return The requested number of items that coudln't be read / written - */ -- (size_t)requestedItems; - -/** - * \return Whether NItems was specified - */ -- (BOOL)hasNItems; @end /** * An OFException indicating a read to the file failed. */ Index: src/OFExceptions.m ================================================================== --- src/OFExceptions.m +++ src/OFExceptions.m @@ -404,19 +404,10 @@ @end @implementation OFReadOrWriteFailedException + newWithClass: (Class)class__ size: (size_t)size - items: (size_t)items -{ - return [[self alloc] initWithClass: class__ - size: size - items: items]; -} - -+ newWithClass: (Class)class__ - size: (size_t)size { return [[self alloc] initWithClass: class__ size: size]; } @@ -426,35 +417,16 @@ selector: _cmd]; } - initWithClass: (Class)class__ size: (size_t)size - items: (size_t)items { self = [super initWithClass: class__]; req_size = size; - req_items = items; - has_items = YES; - - if (class__ == [OFTCPSocket class]) - err = GET_SOCK_ERR; - else - err = GET_ERR; - - return self; -} - -- initWithClass: (Class)class__ - size: (size_t)size -{ - self = [super initWithClass: class__]; - - req_size = size; - req_items = 0; - has_items = NO; - + + /* FIXME: We need something that works for subclasses as well */ if (class__ == [OFTCPSocket class]) err = GET_SOCK_ERR; else err = GET_ERR; @@ -468,36 +440,21 @@ - (size_t)requestedSize { return req_size; } - -- (size_t)requestedItems -{ - return req_items; -} - -- (BOOL)hasNItems -{ - return has_items; -} @end @implementation OFReadFailedException - (OFString*)string { if (string != nil) return string;; - if (has_items) - string = [[OFString alloc] initWithFormat: - @"Failed to read %zu items of size %zu in class %s! " - ERRFMT, req_items, req_size, [class_ className], ERRPARAM]; - else - string = [[OFString alloc] initWithFormat: - @"Failed to read %zu bytes in class %s! " ERRFMT, req_size, - [class_ className], ERRPARAM]; + string = [[OFString alloc] initWithFormat: + @"Failed to read %zu bytes in class %s! " ERRFMT, req_size, + [class_ className], ERRPARAM]; return string; } @end @@ -505,18 +462,13 @@ - (OFString*)string { if (string != nil) return string; - if (has_items) - string = [[OFString alloc] initWithFormat: - @"Failed to write %zu items of size %zu in class %s! " - ERRFMT, req_items, req_size, [class_ className], ERRPARAM]; - else - string = [[OFString alloc] initWithFormat: - @"Failed to write %zu bytes in class %s! " ERRFMT, req_size, - [class_ className], ERRPARAM]; + string = [[OFString alloc] initWithFormat: + @"Failed to write %zu bytes in class %s! " ERRFMT, req_size, + [class_ className], ERRPARAM]; return string; } @end Index: src/OFFile.h ================================================================== --- src/OFFile.h +++ src/OFFile.h @@ -124,36 +124,10 @@ * * \param fp A file pointer, returned from for example fopen(). * It is not closed when the OFFile object is deallocated! */ - initWithFilePointer: (FILE*)fp; - -/** - * Reads from the file into a buffer. - * - * \param buf The buffer into which the data is read - * \param size The size of the data that should be read. - * The buffer MUST be at least size * nitems big! - * \param nitems nitem The number of items to read - * The buffer MUST be at least size * nitems big! - * \return The number of bytes read - */ -- (size_t)readNItems: (size_t)nitems - ofSize: (size_t)size - intoBuffer: (char*)buf; - -/** - * Writes from a buffer into the file. - * - * \param nitems The number of items to write - * \param size The size of the data that should be written - * \param buf The buffer from which the data is written to the file - * \return The number of bytes written - */ -- (size_t)writeNItems: (size_t)nitems - ofSize: (size_t)size - fromBuffer: (const char*)buf; @end @interface OFFileSingleton: OFFile @end Index: src/OFFile.m ================================================================== --- src/OFFile.m +++ src/OFFile.m @@ -188,71 +188,42 @@ fclose(fp); [super dealloc]; } -- (BOOL)atEndOfStream +- (BOOL)atEndOfStreamWithoutCache { if (fp == NULL) return YES; return (feof(fp) == 0 ? NO : YES); } -- (size_t)readNItems: (size_t)nitems - ofSize: (size_t)size - intoBuffer: (char*)buf -{ - size_t ret; - - if (fp == NULL || feof(fp) || - ((ret = fread(buf, size, nitems, fp)) == 0 && - size != 0 && nitems != 0 && !feof(fp))) - @throw [OFReadFailedException newWithClass: isa - size: size - items: nitems]; - - return ret; -} - -- (size_t)readNBytes: (size_t)size - intoBuffer: (char*)buf -{ - return [self readNItems: size - ofSize: 1 - intoBuffer: buf]; -} - -- (size_t)writeNItems: (size_t)nitems - ofSize: (size_t)size - fromBuffer: (const char*)buf -{ - size_t ret; - - if (fp == NULL || feof(fp) || - ((ret = fwrite(buf, size, nitems, fp)) < nitems && - size != 0 && nitems != 0)) - @throw [OFWriteFailedException newWithClass: isa - size: size - items: nitems]; +- (size_t)readNBytesWithoutCache: (size_t)size + intoBuffer: (char*)buf +{ + size_t ret; + + if (fp == NULL || feof(fp) || ((ret = fread(buf, 1, size, fp)) == 0 && + size != 0 && !feof(fp))) + @throw [OFReadFailedException newWithClass: isa + size: size]; return ret; } - (size_t)writeNBytes: (size_t)size fromBuffer: (const char*)buf { - return [self writeNItems: size - ofSize: 1 - fromBuffer: buf]; -} - -- (size_t)writeCString: (const char*)str -{ - return [self writeNItems: strlen(str) - ofSize: 1 - fromBuffer: str]; + size_t ret; + + if (fp == NULL || feof(fp) || + ((ret = fwrite(buf, 1, size, fp)) < size && size != 0)) + @throw [OFWriteFailedException newWithClass: isa + size: size]; + + return ret; } - close { fclose(fp); Index: src/OFSocket.m ================================================================== --- src/OFSocket.m +++ src/OFSocket.m @@ -48,17 +48,17 @@ saddr = NULL; return self; } -- (BOOL)atEndOfStream +- (BOOL)atEndOfStreamWithoutCache { return eos; } -- (size_t)readNBytes: (size_t)size - intoBuffer: (char*)buf +- (size_t)readNBytesWithoutCache: (size_t)size + intoBuffer: (char*)buf { ssize_t ret; if (sock == INVALID_SOCKET || eos) @throw [OFNotConnectedException newWithClass: isa]; Index: src/OFStream.h ================================================================== --- src/OFStream.h +++ src/OFStream.h @@ -20,16 +20,37 @@ char *cache; size_t cache_len; } /** + * Returns a boolean whether the end of the stream has been reached. + * + * IMPORTANT: Do *NOT* override this in subclasses! Override + * atEndOfStreamWithoutCache instead, as otherwise, you *WILL* break caching and + * thus get broken results! + * * \return A boolean whether the end of the stream has been reached */ - (BOOL)atEndOfStream; +/** + * Returns a boolean whether the end of the stream has been reached without + * looking at the cache. + * + * IMPORTANT: Do *NOT* use this! Use atEndOfCache instead, as this is *ONLY* + * for being overriden in subclasses! + * + * \return A boolean whether the end of the stream has been reached + */ +- (BOOL)atEndOfStreamWithoutCache; + /** * Reads from the stream into a buffer. + * + * IMPORTANT: Do *NOT* override this in subclasses! Override + * readNBytesWithoutCache:intoBuffer: instead, as otherwise, you *WILL* break + * caching and thus get broken results! * * \param buf The buffer into which the data is read * \param size The size of the data that should be read. * The buffer MUST be at least size big! * \return The number of bytes read @@ -36,18 +57,25 @@ */ - (size_t)readNBytes: (size_t)size intoBuffer: (char*)buf; /** - * Read until a newline, \\0 or end of stream occurs. - * - * If you want to use readNBytes afterwards again, you have to clear the cache - * before and optionally get the cache before clearing it! - * - * You also need to pay attention to the cache if you want to know if there is - * still data left - atEndOfStream can return NO even if there is still data - * in the cache! + * Reads from the stream into a buffer without looking at the cache. + * + * IMPORTANT: Do *NOT* use this! Use readNBytes:intoBuffer: instead, as this is + * *ONLY* for being overriden in subclasses! + * + * \param buf The buffer into which the data is read + * \param size The size of the data that should be read. + * The buffer MUST be at least size big! + * \return The number of bytes read + */ +- (size_t)readNBytesWithoutCache: (size_t)size + intoBuffer: (char*)buf; + +/** + * Read until a newline, \\0 or end of stream occurs. * * \return The line that was read, autoreleased, or nil if the end of the * stream has been reached. */ - (OFString*)readLine; @@ -54,13 +82,10 @@ /** * Read with the specified encoding until a newline, \\0 or end of stream * 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, autoreleased, or nil if the end of the * stream has been reached. */ - (OFString*)readLineWithEncoding: (enum of_string_encoding)encoding; @@ -80,24 +105,10 @@ * \param str The string from which the data is written to the stream * \return The number of bytes written */ - (size_t)writeString: (OFString*)str; -/** - * 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; - /** * Closes the stream. */ - close; @end Index: src/OFStream.m ================================================================== --- src/OFStream.m +++ src/OFStream.m @@ -50,17 +50,55 @@ return self; } - (BOOL)atEndOfStream { + if (cache != NULL) + return NO; + + return [self atEndOfStreamWithoutCache]; +} + +- (BOOL)atEndOfStreamWithoutCache +{ @throw [OFNotImplementedException newWithClass: isa selector: _cmd]; } - (size_t)readNBytes: (size_t)size intoBuffer: (char*)buf { + if (cache == NULL) + return [self readNBytesWithoutCache: size + intoBuffer: buf]; + + if (size >= cache_len) { + size_t ret = cache_len; + memcpy(buf, cache, cache_len); + + [self freeMemory: cache]; + cache = NULL; + cache_len = 0; + + return ret; + } else { + char *tmp = [self allocMemoryWithSize: cache_len - size]; + memcpy(tmp, cache + size, cache_len - size); + + memcpy(buf, cache, size); + + [self freeMemory: cache]; + cache = tmp; + cache_len -= size; + + return size; + } +} + +- (size_t)readNBytesWithoutCache: (size_t)size + intoBuffer: (char*)buf +{ @throw [OFNotImplementedException newWithClass: isa selector: _cmd]; } - (OFString*)readLine @@ -98,11 +136,11 @@ /* Read until we get a newline or \0 */ tmp = [self allocMemoryWithSize: pagesize]; for (;;) { - if ([self atEndOfStream]) { + if ([self atEndOfStreamWithoutCache]) { [self freeMemory: tmp]; if (cache == NULL) return nil; @@ -116,12 +154,12 @@ return ret; } @try { - len = [self readNBytes: pagesize - 1 - intoBuffer: tmp]; + len = [self readNBytesWithoutCache: pagesize + intoBuffer: tmp]; } @catch (OFException *e) { [self freeMemory: tmp]; @throw e; } @@ -205,29 +243,11 @@ { return [self writeNBytes: [str cStringLength] fromBuffer: [str cString]]; } -- (size_t)getCache: (char**)ptr -{ - if (ptr != NULL) - *ptr = cache; - - return cache_len; -} - -- clearCache -{ - [self freeMemory: cache]; - - cache = NULL; - cache_len = 0; - - return self; -} - - close { @throw [OFNotImplementedException newWithClass: isa selector: _cmd]; } @end