@@ -19,10 +19,11 @@ #include #include #import "OFData.h" #import "OFBase64.h" +#import "OFConcreteData.h" #import "OFDictionary.h" #ifdef OF_HAVE_FILES # import "OFFile.h" # import "OFFileManager.h" #endif @@ -37,21 +38,119 @@ #import "OFNotImplementedException.h" #import "OFOutOfMemoryException.h" #import "OFOutOfRangeException.h" #import "OFTruncatedDataException.h" #import "OFUnsupportedProtocolException.h" + +static struct { + Class isa; +} placeholder; + +@interface OFDataPlaceholder: OFString +@end /* References for static linking */ void _references_to_categories_of_OFData(void) { _OFData_CryptographicHashing_reference = 1; _OFData_MessagePackParsing_reference = 1; } + +@implementation OFDataPlaceholder +- (instancetype)init +{ + return (id)[[OFConcreteData alloc] init]; +} + +- (instancetype)initWithItemSize: (size_t)itemSize +{ + return (id)[[OFConcreteData alloc] initWithItemSize: itemSize]; +} + +- (instancetype)initWithItems: (const void *)items count: (size_t)count +{ + return (id)[[OFConcreteData alloc] initWithItems: items count: count]; +} + +- (instancetype)initWithItems: (const void *)items + count: (size_t)count + itemSize: (size_t)itemSize +{ + return (id)[[OFConcreteData alloc] initWithItems: items + count: count + itemSize: itemSize]; +} + +- (instancetype)initWithItemsNoCopy: (void *)items + count: (size_t)count + freeWhenDone: (bool)freeWhenDone +{ + return (id)[[OFConcreteData alloc] initWithItemsNoCopy: items + count: count + freeWhenDone: freeWhenDone]; +} + +- (instancetype)initWithItemsNoCopy: (void *)items + count: (size_t)count + itemSize: (size_t)itemSize + freeWhenDone: (bool)freeWhenDone +{ + return (id)[[OFConcreteData alloc] initWithItemsNoCopy: items + count: count + itemSize: itemSize + freeWhenDone: freeWhenDone]; +} + +#ifdef OF_HAVE_FILES +- (instancetype)initWithContentsOfFile: (OFString *)path +{ + return (id)[[OFConcreteData alloc] initWithContentsOfFile: path]; +} +#endif + +- (instancetype)initWithContentsOfIRI: (OFIRI *)IRI +{ + return (id)[[OFConcreteData alloc] initWithContentsOfIRI: IRI]; +} + +- (instancetype)initWithStringRepresentation: (OFString *)string +{ + return (id)[[OFConcreteData alloc] + initWithStringRepresentation: string]; +} + +- (instancetype)initWithBase64EncodedString: (OFString *)string +{ + return (id)[[OFConcreteData alloc] initWithBase64EncodedString: string]; +} +@end @implementation OFData -@synthesize itemSize = _itemSize; ++ (void)initialize +{ + if (self == [OFData class]) + placeholder.isa = [OFDataPlaceholder class]; +} + ++ (instancetype)alloc +{ + if (self == [OFData class]) + return (id)&placeholder; + + return [super alloc]; +} + ++ (instancetype)data +{ + return [[[self alloc] init] autorelease]; +} + ++ (instancetype)dataWithItemSize: (size_t)itemSize +{ + return [[[self alloc] initWithItemSize: itemSize] autorelease]; +} + (instancetype)dataWithItems: (const void *)items count: (size_t)count { return [[[self alloc] initWithItems: items count: count] autorelease]; } @@ -105,10 +204,31 @@ + (instancetype)dataWithBase64EncodedString: (OFString *)string { return [[[self alloc] initWithBase64EncodedString: string] autorelease]; } + +- (instancetype)init +{ + if ([self isMemberOfClass: [OFString class]]) { + @try { + [self doesNotRecognizeSelector: _cmd]; + } @catch (id e) { + [self release]; + @throw e; + } + + abort(); + } + + return [super init]; +} + +- (instancetype)initWithItemSize: (size_t)itemSize +{ + OF_INVALID_INIT_METHOD +} - (instancetype)initWithItems: (const void *)items count: (size_t)count { return [self initWithItems: items count: count itemSize: 1]; } @@ -115,28 +235,11 @@ - (instancetype)initWithItems: (const void *)items count: (size_t)count itemSize: (size_t)itemSize { - self = [super init]; - - @try { - if (itemSize == 0) - @throw [OFInvalidArgumentException exception]; - - _items = OFAllocMemory(count, itemSize); - _count = count; - _itemSize = itemSize; - _freeWhenDone = true; - - memcpy(_items, items, count * itemSize); - } @catch (id e) { - [self release]; - @throw e; - } - - return self; + OF_INVALID_INIT_METHOD } - (instancetype)initWithItemsNoCopy: (void *)items count: (size_t)count freeWhenDone: (bool)freeWhenDone @@ -150,26 +253,11 @@ - (instancetype)initWithItemsNoCopy: (void *)items count: (size_t)count itemSize: (size_t)itemSize freeWhenDone: (bool)freeWhenDone { - self = [super init]; - - @try { - if (itemSize == 0) - @throw [OFInvalidArgumentException exception]; - - _items = (unsigned char *)items; - _count = count; - _itemSize = itemSize; - _freeWhenDone = freeWhenDone; - } @catch (id e) { - [self release]; - @throw e; - } - - return self; + OF_INVALID_INIT_METHOD } #ifdef OF_HAVE_FILES - (instancetype)initWithContentsOfFile: (OFString *)path { @@ -210,71 +298,70 @@ } #endif - (instancetype)initWithContentsOfIRI: (OFIRI *)IRI { - self = [super init]; + char *items = NULL, *buffer = NULL; + size_t count = 0; @try { void *pool = objc_autoreleasePoolPush(); OFStream *stream = [OFIRIHandler openItemAtIRI: IRI mode: @"r"]; size_t pageSize; - unsigned char *buffer; - - _count = 0; - _itemSize = 1; - _freeWhenDone = true; pageSize = [OFSystemInfo pageSize]; buffer = OFAllocMemory(1, pageSize); - @try { - while (!stream.atEndOfStream) { - size_t length = [stream - readIntoBuffer: buffer - length: pageSize]; - - if (SIZE_MAX - _count < length) - @throw [OFOutOfRangeException - exception]; - - _items = OFResizeMemory(_items, - _count + length, 1); - memcpy(_items + _count, buffer, length); - _count += length; - } - } @finally { - OFFreeMemory(buffer); + while (!stream.atEndOfStream) { + size_t length = [stream readIntoBuffer: buffer + length: pageSize]; + + if (SIZE_MAX - count < length) + @throw [OFOutOfRangeException exception]; + + items = OFResizeMemory(items, count + length, 1); + memcpy(items + count, buffer, length); + count += length; } objc_autoreleasePoolPop(pool); } @catch (id e) { + OFFreeMemory(buffer); + OFFreeMemory(items); [self release]; + + @throw e; + } + + @try { + self = [self initWithItemsNoCopy: items + count: count + freeWhenDone: true]; + } @catch (id e) { + OFFreeMemory(items); @throw e; } return self; } - (instancetype)initWithStringRepresentation: (OFString *)string { - self = [super init]; + char *items = NULL; + size_t count = 0; @try { - size_t count = [string - cStringLengthWithEncoding: OFStringEncodingASCII]; const char *cString; + count = [string + cStringLengthWithEncoding: OFStringEncodingASCII]; + if (count % 2 != 0) @throw [OFInvalidFormatException exception]; count /= 2; - - _items = OFAllocMemory(count, 1); - _count = count; - _itemSize = 1; - _freeWhenDone = true; + items = OFAllocMemory(count, 1); cString = [string cStringWithEncoding: OFStringEncodingASCII]; for (size_t i = 0; i < count; i++) { uint8_t c1 = cString[2 * i]; @@ -297,14 +384,25 @@ else if (c2 >= 'A' && c2 <= 'F') byte |= c2 - 'A' + 10; else @throw [OFInvalidFormatException exception]; - _items[i] = byte; + items[i] = byte; } } @catch (id e) { + OFFreeMemory(items); [self release]; + + @throw e; + } + + @try { + self = [self initWithItemsNoCopy: items + count: count + freeWhenDone: true]; + } @catch (id e) { + OFFreeMemory(items); @throw e; } return self; } @@ -334,105 +432,109 @@ [(OFMutableData *)self makeImmutable]; return self; } -- (void)dealloc -{ - if (_freeWhenDone) - OFFreeMemory(_items); - - [_parentData release]; - - [super dealloc]; -} - - (size_t)count { - return _count; + OF_UNRECOGNIZED_SELECTOR +} + +- (size_t)itemSize +{ + OF_UNRECOGNIZED_SELECTOR } - (const void *)items { - return _items; + OF_UNRECOGNIZED_SELECTOR } - (const void *)itemAtIndex: (size_t)idx { - if (idx >= _count) + if (idx >= self.count) @throw [OFOutOfRangeException exception]; - return _items + idx * _itemSize; + return (const unsigned char *)self.items + idx * self.itemSize; } - (const void *)firstItem { - if (_items == NULL || _count == 0) + const void *items = self.items; + + if (items == NULL || self.count == 0) return NULL; - return _items; + return items; } - (const void *)lastItem { - if (_items == NULL || _count == 0) + const unsigned char *items = self.items; + size_t count = self.count; + + if (items == NULL || count == 0) return NULL; - return _items + (_count - 1) * _itemSize; + return items + (count - 1) * self.itemSize; } - (id)copy { return [self retain]; } - (id)mutableCopy { - return [[OFMutableData alloc] initWithItems: _items - count: _count - itemSize: _itemSize]; + return [[OFMutableData alloc] initWithItems: self.items + count: self.count + itemSize: self.itemSize]; } - (bool)isEqual: (id)object { + size_t count, itemSize; OFData *data; if (object == self) return true; if (![object isKindOfClass: [OFData class]]) return false; + count = self.count; + itemSize = self.itemSize; data = object; - if (data.count != _count || data.itemSize != _itemSize) + if (data.count != count || data.itemSize != itemSize) return false; - if (memcmp(data.items, _items, _count * _itemSize) != 0) + if (memcmp(data.items, self.items, count * itemSize) != 0) return false; return true; } - (OFComparisonResult)compare: (OFData *)data { int comparison; - size_t count, minCount; + size_t count, dataCount, minCount; if (![data isKindOfClass: [OFData class]]) @throw [OFInvalidArgumentException exception]; - if (data.itemSize != _itemSize) + if (data.itemSize != self.itemSize) @throw [OFInvalidArgumentException exception]; - count = data.count; - minCount = (_count > count ? count : _count); + count = self.count; + dataCount = data.count; + minCount = (count > dataCount ? dataCount : count); - if ((comparison = memcmp(_items, data.items, - minCount * _itemSize)) == 0) { - if (_count > count) + if ((comparison = memcmp(self.items, data.items, + minCount * self.itemSize)) == 0) { + if (count > dataCount) return OFOrderedDescending; - if (_count < count) + if (count < dataCount) return OFOrderedAscending; return OFOrderedSame; } @@ -442,49 +544,51 @@ return OFOrderedAscending; } - (unsigned long)hash { + const unsigned char *items = self.items; + size_t count = self.count, itemSize = self.itemSize; unsigned long hash; OFHashInit(&hash); - for (size_t i = 0; i < _count * _itemSize; i++) - OFHashAddByte(&hash, ((uint8_t *)_items)[i]); + for (size_t i = 0; i < count * itemSize; i++) + OFHashAddByte(&hash, items[i]); OFHashFinalize(&hash); return hash; } - (OFData *)subdataWithRange: (OFRange)range { - OFData *ret; + size_t itemSize; if (range.length > SIZE_MAX - range.location || - range.location + range.length > _count) + range.location + range.length > self.count) @throw [OFOutOfRangeException exception]; - ret = [OFData dataWithItemsNoCopy: _items + (range.location * _itemSize) - count: range.length - itemSize: _itemSize - freeWhenDone: false]; - ret->_parentData = [(_parentData != nil ? _parentData : self) copy]; - - return ret; + itemSize = self.itemSize; + return [OFData dataWithItems: (const unsigned char *)self.items + + (range.location * itemSize) + count: range.length + itemSize: itemSize]; } - (OFString *)description { OFMutableString *ret = [OFMutableString stringWithString: @"<"]; + const unsigned char *items = self.items; + size_t count = self.count, itemSize = self.itemSize; - for (size_t i = 0; i < _count; i++) { + for (size_t i = 0; i < count; i++) { if (i > 0) [ret appendString: @" "]; - for (size_t j = 0; j < _itemSize; j++) - [ret appendFormat: @"%02x", _items[i * _itemSize + j]]; + for (size_t j = 0; j < itemSize; j++) + [ret appendFormat: @"%02x", items[i * itemSize + j]]; } [ret appendString: @">"]; [ret makeImmutable]; @@ -492,36 +596,40 @@ } - (OFString *)stringRepresentation { OFMutableString *ret = [OFMutableString string]; + const unsigned char *items = self.items; + size_t count = self.count, itemSize = self.itemSize; - for (size_t i = 0; i < _count; i++) - for (size_t j = 0; j < _itemSize; j++) - [ret appendFormat: @"%02x", _items[i * _itemSize + j]]; + for (size_t i = 0; i < count; i++) + for (size_t j = 0; j < itemSize; j++) + [ret appendFormat: @"%02x", items[i * itemSize + j]]; [ret makeImmutable]; return ret; } - (OFString *)stringByBase64Encoding { - return OFBase64Encode(_items, _count * _itemSize); + return OFBase64Encode(self.items, self.count * self.itemSize); } - (OFRange)rangeOfData: (OFData *)data options: (OFDataSearchOptions)options range: (OFRange)range { + const unsigned char *items = self.items; + size_t count = self.count, itemSize = self.itemSize; const char *search; size_t searchLength; if (range.length > SIZE_MAX - range.location || - range.location + range.length > _count) + range.location + range.length > count) @throw [OFOutOfRangeException exception]; - if (data == nil || data.itemSize != _itemSize) + if (data == nil || data.itemSize != itemSize) @throw [OFInvalidArgumentException exception]; if ((searchLength = data.count) == 0) return OFMakeRange(0, 0); @@ -530,23 +638,23 @@ search = data.items; if (options & OFDataSearchBackwards) { for (size_t i = range.length - searchLength;; i--) { - if (memcmp(_items + i * _itemSize, search, - searchLength * _itemSize) == 0) + if (memcmp(items + i * itemSize, search, + searchLength * itemSize) == 0) return OFMakeRange(i, searchLength); /* No match and we're at the last item */ if (i == 0) break; } } else { for (size_t i = range.location; i <= range.length - searchLength; i++) - if (memcmp(_items + i * _itemSize, search, - searchLength * _itemSize) == 0) + if (memcmp(items + i * itemSize, search, + searchLength * itemSize) == 0) return OFMakeRange(i, searchLength); } return OFMakeRange(OFNotFound, 0); } @@ -554,11 +662,12 @@ #ifdef OF_HAVE_FILES - (void)writeToFile: (OFString *)path { OFFile *file = [[OFFile alloc] initWithPath: path mode: @"w"]; @try { - [file writeBuffer: _items length: _count * _itemSize]; + [file writeBuffer: self.items + length: self.count * self.itemSize]; } @finally { [file release]; } } #endif @@ -573,40 +682,43 @@ } - (OFData *)messagePackRepresentation { OFMutableData *data; + size_t count; - if (_itemSize != 1) + if (self.itemSize != 1) @throw [OFNotImplementedException exceptionWithSelector: _cmd object: self]; - if (_count <= UINT8_MAX) { + count = self.count; + + if (count <= UINT8_MAX) { uint8_t type = 0xC4; - uint8_t tmp = (uint8_t)_count; + uint8_t tmp = (uint8_t)count; - data = [OFMutableData dataWithCapacity: _count + 2]; + data = [OFMutableData dataWithCapacity: count + 2]; [data addItem: &type]; [data addItem: &tmp]; - } else if (_count <= UINT16_MAX) { + } else if (count <= UINT16_MAX) { uint8_t type = 0xC5; - uint16_t tmp = OFToBigEndian16((uint16_t)_count); + uint16_t tmp = OFToBigEndian16((uint16_t)count); - data = [OFMutableData dataWithCapacity: _count + 3]; + data = [OFMutableData dataWithCapacity: count + 3]; [data addItem: &type]; [data addItems: &tmp count: sizeof(tmp)]; - } else if (_count <= UINT32_MAX) { + } else if (count <= UINT32_MAX) { uint8_t type = 0xC6; - uint32_t tmp = OFToBigEndian32((uint32_t)_count); + uint32_t tmp = OFToBigEndian32((uint32_t)count); - data = [OFMutableData dataWithCapacity: _count + 5]; + data = [OFMutableData dataWithCapacity: count + 5]; [data addItem: &type]; [data addItems: &tmp count: sizeof(tmp)]; } else @throw [OFOutOfRangeException exception]; - [data addItems: _items count: _count]; + [data addItems: self.items count: count]; [data makeImmutable]; return data; } @end