Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -181,19 +181,21 @@ OFBase64.m \ OFBitSetCharacterSet.m \ OFBytesValue.m \ OFCRC16.m \ OFCRC32.m \ + OFConcreteData.m \ OFCountedMapTableSet.m \ OFEmbeddedIRIHandler.m \ OFHuffmanTree.m \ OFINIFileSettings.m \ OFInvertedCharacterSet.m \ OFLHADecompressingStream.m \ OFMapTableDictionary.m \ OFMapTableSet.m \ OFMutableAdjacentArray.m \ + OFMutableConcreteData.m \ OFMutableMapTableDictionary.m \ OFMutableMapTableSet.m \ OFMutableUTF8String.m \ OFNonretainedObjectValue.m \ OFPointValue.m \ ADDED src/OFConcreteData.h Index: src/OFConcreteData.h ================================================================== --- src/OFConcreteData.h +++ src/OFConcreteData.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2008-2023 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#import "OFData.h" + +OF_ASSUME_NONNULL_BEGIN + +@interface OFConcreteData: OFData +{ + unsigned char *_Nullable _items; + size_t _capacity, _count, _itemSize; + bool _freeWhenDone; + OFData *_Nullable _parentData; +} +@end + +OF_ASSUME_NONNULL_END ADDED src/OFConcreteData.m Index: src/OFConcreteData.m ================================================================== --- src/OFConcreteData.m +++ src/OFConcreteData.m @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2008-2023 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include +#include + +#import "OFConcreteData.h" + +#import "OFInvalidArgumentException.h" +#import "OFOutOfRangeException.h" + +@implementation OFConcreteData +- (instancetype)init +{ + return [self initWithItemSize: 1]; +} + +- (instancetype)initWithItemSize: (size_t)itemSize +{ + self = [super init]; + + @try { + if (itemSize == 0) + @throw [OFInvalidArgumentException exception]; + + _itemSize = itemSize; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (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); + _capacity = _count = count; + _itemSize = itemSize; + _freeWhenDone = true; + + memcpy(_items, items, count * itemSize); + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (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; + _capacity = _count = count; + _itemSize = itemSize; + _freeWhenDone = freeWhenDone; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + if (_freeWhenDone) + OFFreeMemory(_items); + + [_parentData release]; + + [super dealloc]; +} + +- (size_t)count +{ + return _count; +} + +- (size_t)itemSize +{ + return _itemSize; +} + +- (const void *)items +{ + return _items; +} + +- (OFData *)subdataWithRange: (OFRange)range +{ + OFConcreteData *ret; + + if (range.length > SIZE_MAX - range.location || + range.location + range.length > _count) + @throw [OFOutOfRangeException exception]; + + ret = [OFConcreteData + dataWithItemsNoCopy: _items + (range.location * _itemSize) + count: range.length + itemSize: _itemSize + freeWhenDone: false]; + ret->_parentData = [(_parentData != nil ? _parentData : self) copy]; + + return ret; +} +@end Index: src/OFData+CryptographicHashing.m ================================================================== --- src/OFData+CryptographicHashing.m +++ src/OFData+CryptographicHashing.m @@ -37,12 +37,12 @@ [class hashWithAllowsSwappableMemory: true]; size_t digestSize = [class digestSize]; const unsigned char *digest; char cString[digestSize * 2]; - [hash updateWithBuffer: self->_items - length: self->_count * self->_itemSize]; + [hash updateWithBuffer: self.items + length: self.count * self.itemSize]; [hash calculate]; digest = hash.digest; for (size_t i = 0; i < digestSize; i++) { uint8_t high, low; Index: src/OFData.h ================================================================== --- src/OFData.h +++ src/OFData.h @@ -38,19 +38,10 @@ * * @brief A class for storing arbitrary data in an array. */ @interface OFData: OFObject -{ - unsigned char *_Nullable _items; - size_t _count, _itemSize; - bool _freeWhenDone; -@private - OFData *_Nullable _parentData; - OF_RESERVE_IVARS(OFData, 4) -} - /** * @brief The size of a single item in the OFData in bytes. */ @property (readonly, nonatomic) size_t itemSize; @@ -90,10 +81,25 @@ /** * @brief A string containing the data in Base64 encoding. */ @property (readonly, nonatomic) OFString *stringByBase64Encoding; +/** + * @brief Creates a new OFData that is empty with an item size of 1. + * + * @return A new autoreleased OFData + */ ++ (instancetype)data; + +/** + * @brief Creates a new OFData that is empty with the specified item size. + * + * @param itemSize The size of a single element in the OFData + * @return A new autoreleased OFData + */ ++ (instancetype)dataWithItemSize: (size_t)itemSize; + /** * @brief Creates a new OFData with the specified `count` items of size 1. * * @param items The items to store in the OFData * @param count The number of items @@ -190,10 +196,27 @@ * @throw OFInvalidFormatException The specified string is not correctly * formatted */ + (instancetype)dataWithBase64EncodedString: (OFString *)string; +/** + * @brief Initializes an already allocated OFData to be empty with an item size + * of 1. + * + * @return An initialized OFData + */ +- (instancetype)init; + +/** + * @brief Initializes an already allocated OFData to be empty with the + * specified item size. + * + * @param itemSize The size of a single element in the OFData + * @return An initialized OFData + */ +- (instancetype)initWithItemSize: (size_t)itemSize; + /** * @brief Initializes an already allocated OFData with the specified `count` * items of size 1. * * @param items The items to store in the OFData Index: src/OFData.m ================================================================== --- src/OFData.m +++ src/OFData.m @@ -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 ADDED src/OFMutableConcreteData.h Index: src/OFMutableConcreteData.h ================================================================== --- src/OFMutableConcreteData.h +++ src/OFMutableConcreteData.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2008-2023 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#import "OFMutableData.h" + +OF_ASSUME_NONNULL_BEGIN + +@interface OFMutableConcreteData: OFMutableData +{ + unsigned char *_Nullable _items; + size_t _capacity, _count, _itemSize; + bool _freeWhenDone; + OFData *_Nullable _parentData; +} +@end + +OF_ASSUME_NONNULL_END ADDED src/OFMutableConcreteData.m Index: src/OFMutableConcreteData.m ================================================================== --- src/OFMutableConcreteData.m +++ src/OFMutableConcreteData.m @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2008-2023 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include +#include + +#import "OFMutableConcreteData.h" +#import "OFConcreteData.h" + +#import "OFInvalidArgumentException.h" +#import "OFOutOfMemoryException.h" +#import "OFOutOfRangeException.h" + +@implementation OFMutableConcreteData ++ (void)initialize +{ + if (self == [OFMutableConcreteData class]) + [self inheritMethodsFromClass: [OFConcreteData class]]; +} + +- (instancetype)initWithItemSize: (size_t)itemSize capacity: (size_t)capacity +{ + self = [super init]; + + @try { + if (itemSize == 0) + @throw [OFInvalidArgumentException exception]; + + _items = OFAllocMemory(capacity, itemSize); + _itemSize = itemSize; + _capacity = capacity; + _freeWhenDone = true; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (instancetype)initWithItemsNoCopy: (void *)items + count: (size_t)count + itemSize: (size_t)itemSize + freeWhenDone: (bool)freeWhenDone +{ + self = [self initWithItems: items count: count itemSize: itemSize]; + + if (freeWhenDone) + OFFreeMemory(items); + + return self; +} + +- (void *)mutableItems +{ + return _items; +} + +- (void)addItem: (const void *)item +{ + if (SIZE_MAX - _count < 1) + @throw [OFOutOfRangeException exception]; + + if (_count + 1 > _capacity) { + _items = OFResizeMemory(_items, _count + 1, _itemSize); + _capacity = _count + 1; + } + + memcpy(_items + _count * _itemSize, item, _itemSize); + + _count++; +} + +- (void)addItems: (const void *)items count: (size_t)count +{ + if (count > SIZE_MAX - _count) + @throw [OFOutOfRangeException exception]; + + if (_count + count > _capacity) { + _items = OFResizeMemory(_items, _count + count, _itemSize); + _capacity = _count + count; + } + + memcpy(_items + _count * _itemSize, items, count * _itemSize); + _count += count; +} + +- (void)insertItems: (const void *)items + atIndex: (size_t)idx + count: (size_t)count +{ + if (count > SIZE_MAX - _count || idx > _count) + @throw [OFOutOfRangeException exception]; + + if (_count + count > _capacity) { + _items = OFResizeMemory(_items, _count + count, _itemSize); + _capacity = _count + count; + } + + memmove(_items + (idx + count) * _itemSize, _items + idx * _itemSize, + (_count - idx) * _itemSize); + memcpy(_items + idx * _itemSize, items, count * _itemSize); + + _count += count; +} + +- (void)increaseCountBy: (size_t)count +{ + if (count > SIZE_MAX - _count) + @throw [OFOutOfRangeException exception]; + + if (_count + count > _capacity) { + _items = OFResizeMemory(_items, _count + count, _itemSize); + _capacity = _count + count; + } + + memset(_items + _count * _itemSize, '\0', count * _itemSize); + _count += count; +} + +- (void)removeItemsInRange: (OFRange)range +{ + if (range.length > SIZE_MAX - range.location || + range.location + range.length > _count) + @throw [OFOutOfRangeException exception]; + + memmove(_items + range.location * _itemSize, + _items + (range.location + range.length) * _itemSize, + (_count - range.location - range.length) * _itemSize); + + _count -= range.length; + @try { + _items = OFResizeMemory(_items, _count, _itemSize); + _capacity = _count; + } @catch (OFOutOfMemoryException *e) { + /* We don't really care, as we only made it smaller */ + } +} + +- (void)removeLastItem +{ + if (_count == 0) + return; + + _count--; + @try { + _items = OFResizeMemory(_items, _count, _itemSize); + _capacity = _count; + } @catch (OFOutOfMemoryException *e) { + /* We don't care, as we only made it smaller */ + } +} + +- (void)removeAllItems +{ + OFFreeMemory(_items); + _items = NULL; + _count = 0; + _capacity = 0; +} + +- (void)makeImmutable +{ + if (_capacity != _count) { + @try { + _items = OFResizeMemory(_items, _count, _itemSize); + _capacity = _count; + } @catch (OFOutOfMemoryException *e) { + /* We don't care, as we only made it smaller */ + } + } + + object_setClass(self, [OFConcreteData class]); +} +@end Index: src/OFMutableData.h ================================================================== --- src/OFMutableData.h +++ src/OFMutableData.h @@ -15,23 +15,16 @@ #import "OFData.h" OF_ASSUME_NONNULL_BEGIN -@class OFString; - /** * @class OFMutableData OFMutableData.h ObjFW/OFMutableData.h * * @brief A class for storing and manipulating arbitrary data in an array. */ @interface OFMutableData: OFData -{ - size_t _capacity; - OF_RESERVE_IVARS(OFMutableData, 4) -} - /** * @brief All items of the OFMutableData as a C array. * * @warning The pointer is only valid until the OFMutableData is changed! * @@ -51,26 +44,10 @@ * @brief The last item of the OFMutableData or `NULL`. */ @property OF_NULLABLE_PROPERTY (readonly, nonatomic) void *mutableLastItem OF_RETURNS_INNER_POINTER; -/** - * @brief Creates a new OFMutableData with an item size of 1. - * - * @return A new autoreleased OFMutableData - */ -+ (instancetype)data; - -/** - * @brief Creates a new OFMutableData whose items all have the same specified - * size. - * - * @param itemSize The size of a single element in the OFMutableData - * @return A new autoreleased OFMutableData - */ -+ (instancetype)dataWithItemSize: (size_t)itemSize; - /** * @brief Creates a new OFMutableData with enough memory to hold the specified * number of items which all have an item size of 1. * * @param capacity The initial capacity for the OFMutableData @@ -86,26 +63,10 @@ * @param capacity The initial capacity for the OFMutableData * @return A new autoreleased OFMutableData */ + (instancetype)dataWithItemSize: (size_t)itemSize capacity: (size_t)capacity; -/** - * @brief Initializes an already allocated OFMutableData with an item size of 1. - * - * @return An initialized OFMutableData - */ -- (instancetype)init; - -/** - * @brief Initializes an already allocated OFMutableData whose items all have - * the same size. - * - * @param itemSize The size of a single element in the OFMutableData - * @return An initialized OFMutableData - */ -- (instancetype)initWithItemSize: (size_t)itemSize; - /** * @brief Initializes an already allocated OFMutableData with enough memory to * hold the the specified number of items which all have an item size of * 1. * Index: src/OFMutableData.m ================================================================== --- src/OFMutableData.m +++ src/OFMutableData.m @@ -18,20 +18,118 @@ #include #include #include #import "OFMutableData.h" -#import "OFString.h" +#import "OFMutableConcreteData.h" -#import "OFInvalidArgumentException.h" -#import "OFOutOfMemoryException.h" #import "OFOutOfRangeException.h" + +static struct { + Class isa; +} placeholder; + +@interface OFMutableDataPlaceholder: OFMutableData +@end + +@implementation OFMutableDataPlaceholder +- (instancetype)init +{ + return (id)[[OFMutableConcreteData alloc] init]; +} + +- (instancetype)initWithItemSize: (size_t)itemSize +{ + return (id)[[OFMutableConcreteData alloc] initWithItemSize: itemSize]; +} + +- (instancetype)initWithItems: (const void *)items count: (size_t)count +{ + return (id)[[OFMutableConcreteData alloc] initWithItems: items + count: count]; +} + +- (instancetype)initWithItems: (const void *)items + count: (size_t)count + itemSize: (size_t)itemSize +{ + return (id)[[OFMutableConcreteData alloc] initWithItems: items + count: count + itemSize: itemSize]; +} + +- (instancetype)initWithItemsNoCopy: (void *)items + count: (size_t)count + freeWhenDone: (bool)freeWhenDone +{ + return (id)[[OFMutableConcreteData alloc] + initWithItemsNoCopy: items + count: count + freeWhenDone: freeWhenDone]; +} + +- (instancetype)initWithItemsNoCopy: (void *)items + count: (size_t)count + itemSize: (size_t)itemSize + freeWhenDone: (bool)freeWhenDone +{ + return (id)[[OFMutableConcreteData alloc] + initWithItemsNoCopy: items + count: count + itemSize: itemSize + freeWhenDone: freeWhenDone]; +} + +#ifdef OF_HAVE_FILES +- (instancetype)initWithContentsOfFile: (OFString *)path +{ + return (id)[[OFMutableConcreteData alloc] initWithContentsOfFile: path]; +} +#endif + +- (instancetype)initWithContentsOfIRI: (OFIRI *)IRI +{ + return (id)[[OFMutableConcreteData alloc] initWithContentsOfIRI: IRI]; +} + +- (instancetype)initWithStringRepresentation: (OFString *)string +{ + return (id)[[OFMutableConcreteData alloc] + initWithStringRepresentation: string]; +} + +- (instancetype)initWithBase64EncodedString: (OFString *)string +{ + return (id)[[OFMutableConcreteData alloc] + initWithBase64EncodedString: string]; +} + +- (instancetype)initWithCapacity: (size_t)capacity +{ + return (id)[[OFMutableConcreteData alloc] initWithCapacity: capacity]; +} + +- (instancetype)initWithItemSize: (size_t)itemSize capacity: (size_t)capacity +{ + return (id)[[OFMutableConcreteData alloc] initWithItemSize: itemSize + capacity: capacity]; +} +@end @implementation OFMutableData -+ (instancetype)data ++ (void)initialize +{ + if (self == [OFMutableData class]) + placeholder.isa = [OFMutableDataPlaceholder class]; +} + ++ (instancetype)alloc { - return [[[self alloc] init] autorelease]; + if (self == [OFMutableData class]) + return (id)&placeholder; + + return [super alloc]; } + (instancetype)dataWithItemSize: (size_t)itemSize { return [[[self alloc] initWithItemSize: itemSize] autorelease]; @@ -46,73 +144,23 @@ { return [[[self alloc] initWithItemSize: itemSize capacity: capacity] autorelease]; } -- (instancetype)init -{ - self = [super init]; - - _itemSize = 1; - _freeWhenDone = true; - - return self; -} - - (instancetype)initWithItemSize: (size_t)itemSize { - self = [super init]; - - @try { - if (itemSize == 0) - @throw [OFInvalidArgumentException exception]; - - _itemSize = itemSize; - _freeWhenDone = true; - } @catch (id e) { - [self release]; - @throw e; - } - - return self; + return [self initWithItemSize: 1 capacity: 0]; } - (instancetype)initWithCapacity: (size_t)capacity { - return [self initWithItemSize: 1 - capacity: capacity]; + return [self initWithItemSize: 1 capacity: capacity]; } - (instancetype)initWithItemSize: (size_t)itemSize capacity: (size_t)capacity { - self = [super init]; - - @try { - if (itemSize == 0) - @throw [OFInvalidArgumentException exception]; - - _items = OFAllocMemory(capacity, itemSize); - _itemSize = itemSize; - _capacity = capacity; - _freeWhenDone = true; - } @catch (id e) { - [self release]; - @throw e; - } - - return self; -} - -- (instancetype)initWithItems: (const void *)items - count: (size_t)count - itemSize: (size_t)itemSize -{ - self = [super initWithItems: items count: count itemSize: itemSize]; - - _capacity = _count; - - return self; + OF_INVALID_INIT_METHOD } - (instancetype)initWithItemsNoCopy: (void *)items count: (size_t)count itemSize: (size_t)itemSize @@ -124,188 +172,117 @@ OFFreeMemory(items); return self; } -- (instancetype)initWithStringRepresentation: (OFString *)string -{ - self = [super initWithStringRepresentation: string]; - - _capacity = _count; - - return self; -} - - (void *)mutableItems { - return _items; + OF_UNRECOGNIZED_SELECTOR } - (void *)mutableItemAtIndex: (size_t)idx { - if (idx >= _count) + if (idx >= self.count) @throw [OFOutOfRangeException exception]; - return _items + idx * _itemSize; + return (unsigned char *)self.mutableItems + idx * self.itemSize; } - (void *)mutableFirstItem { - if (_items == NULL || _count == 0) + void *mutableItems = self.mutableItems; + + if (mutableItems == NULL || self.count == 0) return NULL; - return _items; + return mutableItems; } - (void *)mutableLastItem { - if (_items == NULL || _count == 0) + unsigned char *mutableItems = self.mutableItems; + size_t count = self.count; + + if (mutableItems == NULL || count == 0) return NULL; - return _items + (_count - 1) * _itemSize; + return mutableItems + (count - 1) * self.itemSize; } - (OFData *)subdataWithRange: (OFRange)range { + size_t itemSize; + if (range.length > SIZE_MAX - range.location || - range.location + range.length > _count) + range.location + range.length > self.count) @throw [OFOutOfRangeException exception]; - return [OFData dataWithItems: _items + (range.location * _itemSize) + itemSize = self.itemSize; + return [OFData dataWithItems: (unsigned char *)self.mutableItems + + (range.location * itemSize) count: range.length - itemSize: _itemSize]; + itemSize: itemSize]; } - (void)addItem: (const void *)item { - if (SIZE_MAX - _count < 1) - @throw [OFOutOfRangeException exception]; - - if (_count + 1 > _capacity) { - _items = OFResizeMemory(_items, _count + 1, _itemSize); - _capacity = _count + 1; - } - - memcpy(_items + _count * _itemSize, item, _itemSize); - - _count++; + [self insertItems: item atIndex: self.count count: 1]; } - (void)insertItem: (const void *)item atIndex: (size_t)idx { [self insertItems: item atIndex: idx count: 1]; } - (void)addItems: (const void *)items count: (size_t)count { - if (count > SIZE_MAX - _count) - @throw [OFOutOfRangeException exception]; - - if (_count + count > _capacity) { - _items = OFResizeMemory(_items, _count + count, _itemSize); - _capacity = _count + count; - } - - memcpy(_items + _count * _itemSize, items, count * _itemSize); - _count += count; + [self insertItems: items atIndex: self.count count: count]; } - (void)insertItems: (const void *)items atIndex: (size_t)idx count: (size_t)count { - if (count > SIZE_MAX - _count || idx > _count) - @throw [OFOutOfRangeException exception]; - - if (_count + count > _capacity) { - _items = OFResizeMemory(_items, _count + count, _itemSize); - _capacity = _count + count; - } - - memmove(_items + (idx + count) * _itemSize, _items + idx * _itemSize, - (_count - idx) * _itemSize); - memcpy(_items + idx * _itemSize, items, count * _itemSize); - - _count += count; + OF_UNRECOGNIZED_SELECTOR } - (void)increaseCountBy: (size_t)count { - if (count > SIZE_MAX - _count) - @throw [OFOutOfRangeException exception]; - - if (_count + count > _capacity) { - _items = OFResizeMemory(_items, _count + count, _itemSize); - _capacity = _count + count; - } - - memset(_items + _count * _itemSize, '\0', count * _itemSize); - _count += count; + OF_UNRECOGNIZED_SELECTOR } - (void)removeItemAtIndex: (size_t)idx { [self removeItemsInRange: OFMakeRange(idx, 1)]; } - (void)removeItemsInRange: (OFRange)range { - if (range.length > SIZE_MAX - range.location || - range.location + range.length > _count) - @throw [OFOutOfRangeException exception]; - - memmove(_items + range.location * _itemSize, - _items + (range.location + range.length) * _itemSize, - (_count - range.location - range.length) * _itemSize); - - _count -= range.length; - @try { - _items = OFResizeMemory(_items, _count, _itemSize); - _capacity = _count; - } @catch (OFOutOfMemoryException *e) { - /* We don't really care, as we only made it smaller */ - } + OF_UNRECOGNIZED_SELECTOR } - (void)removeLastItem { - if (_count == 0) + size_t count = self.count; + + if (count == 0) return; - _count--; - @try { - _items = OFResizeMemory(_items, _count, _itemSize); - _capacity = _count; - } @catch (OFOutOfMemoryException *e) { - /* We don't care, as we only made it smaller */ - } + [self removeItemsInRange: OFMakeRange(count - 1, 1)]; } - (void)removeAllItems { - OFFreeMemory(_items); - _items = NULL; - _count = 0; - _capacity = 0; + [self removeItemsInRange: OFMakeRange(0, self.count)]; } - (id)copy { - return [[OFData alloc] initWithItems: _items - count: _count - itemSize: _itemSize]; + return [[OFData alloc] initWithItems: self.mutableItems + count: self.count + itemSize: self.itemSize]; } - (void)makeImmutable { - if (_capacity != _count) { - @try { - _items = OFResizeMemory(_items, _count, _itemSize); - _capacity = _count; - } @catch (OFOutOfMemoryException *e) { - /* We don't care, as we only made it smaller */ - } - } - - object_setClass(self, [OFData class]); } @end Index: src/OFSecureData.h ================================================================== --- src/OFSecureData.h +++ src/OFSecureData.h @@ -30,12 +30,14 @@ * memory. */ OF_SUBCLASSING_RESTRICTED @interface OFSecureData: OFData { + unsigned char *_Nullable _items; + size_t _count, _itemSize; + bool _freeWhenDone, _allowsSwappableMemory; void *_page; - bool _allowsSwappableMemory; } /** * @brief Whether the data may be stored in swappable memory. */ @@ -133,10 +135,12 @@ - (instancetype)initWithCount: (size_t)count itemSize: (size_t)itemSize allowsSwappableMemory: (bool)allowsSwappableMemory OF_DESIGNATED_INITIALIZER; +- (instancetype)init OF_UNAVAILABLE; +- (instancetype)initWithItemSize: (size_t)itemSize OF_UNAVAILABLE; - (instancetype)initWithItems: (const void *)items count: (size_t)count OF_UNAVAILABLE; - (instancetype)initWithItems: (const void *)items count: (size_t)count itemSize: (size_t)itemSize OF_UNAVAILABLE; Index: src/OFSecureData.m ================================================================== --- src/OFSecureData.m +++ src/OFSecureData.m @@ -337,61 +337,10 @@ return [[[self alloc] initWithCount: count itemSize: itemSize allowsSwappableMemory: allowsSwappableMemory] autorelease]; } - -+ (instancetype)dataWithItems: (const void *)items - count: (size_t)count -{ - OF_UNRECOGNIZED_SELECTOR -} - -+ (instancetype)dataWithItems: (const void *)items - count: (size_t)count - itemSize: (size_t)itemSize -{ - OF_UNRECOGNIZED_SELECTOR -} - -+ (instancetype)dataWithItemsNoCopy: (void *)items - count: (size_t)count - freeWhenDone: (bool)freeWhenDone -{ - OF_UNRECOGNIZED_SELECTOR -} - -+ (instancetype)dataWithItemsNoCopy: (void *)items - count: (size_t)count - itemSize: (size_t)itemSize - freeWhenDone: (bool)freeWhenDone -{ - OF_UNRECOGNIZED_SELECTOR -} - -#ifdef OF_HAVE_FILES -+ (instancetype)dataWithContentsOfFile: (OFString *)path -{ - OF_UNRECOGNIZED_SELECTOR -} -#endif - -+ (instancetype)dataWithContentsOfIRI: (OFIRI *)IRI -{ - OF_UNRECOGNIZED_SELECTOR -} - -+ (instancetype)dataWithStringRepresentation: (OFString *)string -{ - OF_UNRECOGNIZED_SELECTOR -} - -+ (instancetype)dataWithBase64EncodedString: (OFString *)string -{ - OF_UNRECOGNIZED_SELECTOR -} - - (instancetype)initWithCount: (size_t)count allowsSwappableMemory: (bool)allowsSwappableMemory { return [self initWithCount: count @@ -463,33 +412,16 @@ } return self; } -- (instancetype)initWithItems: (const void *)items count: (size_t)count +- (instancetype)init { OF_INVALID_INIT_METHOD } -- (instancetype)initWithItems: (const void *)items - count: (size_t)count - itemSize: (size_t)itemSize -{ - OF_INVALID_INIT_METHOD -} - -- (instancetype)initWithItemsNoCopy: (void *)items - count: (size_t)count - freeWhenDone: (bool)freeWhenDone -{ - OF_INVALID_INIT_METHOD -} - -- (instancetype)initWithItemsNoCopy: (void *)items - count: (size_t)count - itemSize: (size_t)itemSize - freeWhenDone: (bool)freeWhenDone +- (instancetype)initWithItemSize: (size_t)itemSize { OF_INVALID_INIT_METHOD } #ifdef OF_HAVE_FILES @@ -533,12 +465,30 @@ removePageIfEmpty(_page); } } #endif + if (_freeWhenDone) + OFFreeMemory(_items); + [super dealloc]; } + +- (size_t)count +{ + return _count; +} + +- (size_t)itemSize +{ + return _itemSize; +} + +- (const void *)items +{ + return _items; +} - (void *)mutableItems { return _items; } @@ -581,27 +531,29 @@ } - (bool)isEqual: (id)object { OFData *otherData; + const unsigned char *otherDataItems; unsigned char diff; if (object == self) return true; if (![object isKindOfClass: [OFData class]]) return false; otherData = object; + otherDataItems = otherData.items; - if (otherData->_count != _count || otherData->_itemSize != _itemSize) + if (otherData.count != _count || otherData.itemSize != _itemSize) return false; diff = 0; for (size_t i = 0; i < _count * _itemSize; i++) - diff |= otherData->_items[i] ^ _items[i]; + diff |= otherDataItems[i] ^ _items[i]; return (diff == 0); } - (OFString *)description