@@ -56,10 +56,17 @@ + (instancetype)dataArrayWithItemSize: (size_t)itemSize { return [[[self alloc] initWithItemSize: itemSize] autorelease]; } + ++ (instancetype)dataArrayWithItemSize: (size_t)itemSize + capacity: (size_t)capacity +{ + return [[[self alloc] initWithItemSize: itemSize + capacity: capacity] autorelease]; +} + (instancetype)dataArrayWithContentsOfFile: (OFString*)path { return [[[self alloc] initWithContentsOfFile: path] autorelease]; } @@ -80,18 +87,22 @@ return [[[self alloc] initWithBase64EncodedString: string] autorelease]; } - init { - self = [super init]; + return [self initWithItemSize: 1 + capacity: 0]; +} - _itemSize = 1; - - return self; +- initWithItemSize: (size_t)itemSize +{ + return [self initWithItemSize: itemSize + capacity: 0]; } - initWithItemSize: (size_t)itemSize + capacity: (size_t)capacity { self = [super init]; if (itemSize == 0) { Class c = [self class]; @@ -98,24 +109,32 @@ [self release]; @throw [OFInvalidArgumentException exceptionWithClass: c selector: _cmd]; } + _items = [self allocMemoryWithSize: itemSize + count: capacity]; + _itemSize = itemSize; + _capacity = capacity; return self; } - initWithContentsOfFile: (OFString*)path { - self = [super init]; - @try { OFFile *file = [[OFFile alloc] initWithPath: path mode: @"rb"]; + off_t size = [OFFile sizeOfFileAtPath: path]; - _itemSize = 1; + if (size > SIZE_MAX) + @throw [OFOutOfRangeException + exceptionWithClass: [self class]]; + + self = [self initWithItemSize: 1 + capacity: size]; @try { size_t pageSize = [OFSystemInfo pageSize]; char *buffer = [self allocMemoryWithSize: pageSize]; @@ -169,10 +188,14 @@ @throw [OFHTTPRequestFailedException exceptionWithClass: [request class] request: request reply: reply]; + /* + * TODO: This can be optimized by allocating a data array with the + * capacity from the Content-Length header. + */ self = [[reply readDataArrayTillEndOfStream] retain]; headers = [reply headers]; if ((contentLength = [headers objectForKey: @"Content-Length"]) != nil) if ([self count] != (size_t)[contentLength decimalValue]) @@ -240,15 +263,14 @@ return self; } - initWithBase64EncodedString: (OFString*)string { - self = [super init]; + self = [self initWithItemSize: 1 + capacity: [string length] / 3]; @try { - _itemSize = 1; - if (!of_base64_decode(self, [string cStringWithEncoding: OF_STRING_ENCODING_ASCII], [string cStringLengthWithEncoding: OF_STRING_ENCODING_ASCII])) { Class c = [self class]; [self release]; @@ -262,14 +284,10 @@ return self; } - initWithSerialization: (OFXMLElement*)element { - self = [super init]; - - _itemSize = 1; - @try { void *pool = objc_autoreleasePoolPush(); OFString *stringValue; if (![[element name] isEqual: [self className]] || @@ -278,16 +296,11 @@ exceptionWithClass: [self class] selector: _cmd]; stringValue = [element stringValue]; - if (!of_base64_decode(self, [stringValue - cStringWithEncoding: OF_STRING_ENCODING_ASCII], - [stringValue cStringLengthWithEncoding: - OF_STRING_ENCODING_ASCII])) - @throw [OFInvalidFormatException - exceptionWithClass: [self class]]; + self = [self initWithBase64EncodedString: stringValue]; objc_autoreleasePoolPop(pool); } @catch (id e) { [self release]; @throw e; @@ -338,13 +351,16 @@ - (void)addItem: (const void*)item { if (SIZE_MAX - _count < 1) @throw [OFOutOfRangeException exceptionWithClass: [self class]]; - _items = [self resizeMemory: _items - size: _itemSize - count: _count + 1]; + if (_count + 1 > _capacity) { + _items = [self resizeMemory: _items + size: _itemSize + count: _count + 1]; + _capacity = _count + 1; + } memcpy(_items + _count * _itemSize, item, _itemSize); _count++; } @@ -361,13 +377,16 @@ count: (size_t)count { if (count > SIZE_MAX - count) @throw [OFOutOfRangeException exceptionWithClass: [self class]]; - _items = [self resizeMemory: _items - size: _itemSize - count: _count + count]; + if (_count + count > _capacity) { + _items = [self resizeMemory: _items + size: _itemSize + count: _count + count]; + _capacity = _count + count; + } memcpy(_items + _count * _itemSize, items, count * _itemSize); _count += count; } @@ -376,13 +395,16 @@ count: (size_t)count { if (count > SIZE_MAX - _count || index > _count) @throw [OFOutOfRangeException exceptionWithClass: [self class]]; - _items = [self resizeMemory: _items - size: _itemSize - count: _count + count]; + if (_count + count > _capacity) { + _items = [self resizeMemory: _items + size: _itemSize + count: _count + count]; + _capacity = _count + count; + } memmove(_items + (index + count) * _itemSize, _items + index * _itemSize, (_count - index) * _itemSize); memcpy(_items + index * _itemSize, items, count * _itemSize); @@ -407,10 +429,11 @@ _count -= range.length; @try { _items = [self resizeMemory: _items size: _itemSize count: _count]; + _capacity = _count; } @catch (OFOutOfMemoryException *e) { /* We don't really care, as we only made it smaller */ } } @@ -422,10 +445,11 @@ _count--; @try { _items = [self resizeMemory: _items size: _itemSize count: _count]; + _capacity = _count; } @catch (OFOutOfMemoryException *e) { /* We don't care, as we only made it smaller */ } } @@ -433,15 +457,17 @@ { [self freeMemory: _items]; _items = NULL; _count = 0; + _capacity = 0; } - copy { - OFDataArray *copy = [[[self class] alloc] initWithItemSize: _itemSize]; + OFDataArray *copy = [[[self class] alloc] initWithItemSize: _itemSize + capacity: _count]; [copy addItems: _items count: _count]; return copy; @@ -590,10 +616,21 @@ return [element autorelease]; } @end @implementation OFBigDataArray +- initWithItemSize: (size_t)itemSize + capacity: (size_t)capacity +{ + size_t lastPageByte = [OFSystemInfo pageSize] - 1; + + capacity = (capacity * itemSize + lastPageByte) & ~lastPageByte; + + return [super initWithItemSize: itemSize + capacity: capacity]; +} + - (void)addItem: (const void*)item { size_t size, lastPageByte; if (SIZE_MAX - _count < 1 || _count + 1 > SIZE_MAX / _itemSize) @@ -600,13 +637,15 @@ @throw [OFOutOfRangeException exceptionWithClass: [self class]]; lastPageByte = [OFSystemInfo pageSize] - 1; size = ((_count + 1) * _itemSize + lastPageByte) & ~lastPageByte; - if (_size != size) + if (size > _capacity) { _items = [self resizeMemory: _items size: size]; + _capacity = size; + } memcpy(_items + _count * _itemSize, item, _itemSize); _count++; _size = size; @@ -621,13 +660,15 @@ @throw [OFOutOfRangeException exceptionWithClass: [self class]]; lastPageByte = [OFSystemInfo pageSize] - 1; size = ((_count + count) * _itemSize + lastPageByte) & ~lastPageByte; - if (_size != size) + if (size > _capacity) { _items = [self resizeMemory: _items size: size]; + _capacity = size; + } memcpy(_items + _count * _itemSize, items, count * _itemSize); _count += count; _size = size; @@ -644,13 +685,15 @@ @throw [OFOutOfRangeException exceptionWithClass: [self class]]; lastPageByte = [OFSystemInfo pageSize] - 1; size = ((_count + count) * _itemSize + lastPageByte) & ~lastPageByte; - if (_size != size) + if (size > _capacity) { _items = [self resizeMemory: _items size: size]; + _capacity = size; + } memmove(_items + (index + count) * _itemSize, _items + index * _itemSize, (_count - index) * _itemSize); memcpy(_items + index * _itemSize, items, count * _itemSize); @@ -676,10 +719,11 @@ if (_size != size && size >= pageSize) { @try { _items = [self resizeMemory: _items size: size]; + _capacity = size; } @catch (OFOutOfMemoryException *e) { /* We don't care, as we only made it smaller */ } _size = size; @@ -699,10 +743,11 @@ if (_size != size && size >= pageSize) { @try { _items = [self resizeMemory: _items size: size]; + _capacity = size; } @catch (OFOutOfMemoryException *e) { /* We don't care, as we only made it smaller */ } _size = size; @@ -714,13 +759,14 @@ size_t pageSize = [OFSystemInfo pageSize]; @try { _items = [self resizeMemory: _items size: pageSize]; + _capacity = pageSize; _size = pageSize; } @catch (OFOutOfMemoryException *e) { /* We don't care, as we only made it smaller */ } _count = 0; } @end