@@ -1,7 +1,7 @@ /* - * Copyright (c) 2008-2022 Jonathan Schleifer + * Copyright (c) 2008-2024 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 @@ -16,20 +16,17 @@ #include "config.h" #include #include -#include - #import "OFArray.h" #import "OFArray+Private.h" -#import "OFAdjacentArray.h" +#import "OFConcreteArray.h" #import "OFData.h" #import "OFNull.h" #import "OFString.h" #import "OFSubarray.h" -#import "OFXMLElement.h" #import "OFEnumerationMutationException.h" #import "OFInvalidArgumentException.h" #import "OFOutOfRangeException.h" @@ -45,82 +42,69 @@ @interface OFPlaceholderArray: OFArray @end @implementation OFPlaceholderArray +#ifdef __clang__ +/* We intentionally don't call into super, so silence the warning. */ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunknown-pragmas" +# pragma clang diagnostic ignored "-Wobjc-designated-initializers" +#endif - (instancetype)init { - return (id)[[OFAdjacentArray alloc] init]; + return (id)[[OFConcreteArray alloc] init]; } - (instancetype)initWithObject: (id)object { - return (id)[[OFAdjacentArray alloc] initWithObject: object]; + return (id)[[OFConcreteArray alloc] initWithObject: object]; } - (instancetype)initWithObjects: (id)firstObject, ... { id ret; va_list arguments; va_start(arguments, firstObject); - ret = [[OFAdjacentArray alloc] initWithObject: firstObject + ret = [[OFConcreteArray alloc] initWithObject: firstObject arguments: arguments]; va_end(arguments); return ret; } - (instancetype)initWithObject: (id)firstObject arguments: (va_list)arguments { - return (id)[[OFAdjacentArray alloc] initWithObject: firstObject + return (id)[[OFConcreteArray alloc] initWithObject: firstObject arguments: arguments]; } - (instancetype)initWithArray: (OFArray *)array { - return (id)[[OFAdjacentArray alloc] initWithArray: array]; + return (id)[[OFConcreteArray alloc] initWithArray: array]; } - (instancetype)initWithObjects: (id const *)objects count: (size_t)count { - return (id)[[OFAdjacentArray alloc] initWithObjects: objects + return (id)[[OFConcreteArray alloc] initWithObjects: objects count: count]; } - -- (instancetype)initWithSerialization: (OFXMLElement *)element -{ - return (id)[[OFAdjacentArray alloc] initWithSerialization: element]; -} - -- (instancetype)retain -{ - return self; -} - -- (instancetype)autorelease -{ - return self; -} - -- (void)release -{ -} - -- (void)dealloc -{ - OF_DEALLOC_UNSUPPORTED -} +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + +OF_SINGLETON_METHODS @end @implementation OFArray + (void)initialize { if (self == [OFArray class]) - placeholder.isa = [OFPlaceholderArray class]; + object_setClass((id)&placeholder, [OFPlaceholderArray class]); } + (instancetype)alloc { if (self == [OFArray class]) @@ -164,11 +148,12 @@ count: count] autorelease]; } - (instancetype)init { - if ([self isMemberOfClass: [OFArray class]]) { + if ([self isMemberOfClass: [OFArray class]] || + [self isMemberOfClass: [OFMutableArray class]]) { @try { [self doesNotRecognizeSelector: _cmd]; } @catch (id e) { [self release]; @throw e; @@ -180,16 +165,11 @@ return [super init]; } - (instancetype)initWithObject: (id)object { - if (object == nil) { - [self release]; - @throw [OFInvalidArgumentException exception]; - } - - return [self initWithObjects: object, nil]; + return [self initWithObjects: &object count: 1]; } - (instancetype)initWithObjects: (id)firstObject, ... { id ret; @@ -200,31 +180,85 @@ va_end(arguments); return ret; } -- (instancetype)initWithObject: (id)firstObject - arguments: (va_list)arguments +- (instancetype)initWithObject: (id)firstObject arguments: (va_list)arguments { - OF_INVALID_INIT_METHOD + size_t count = 1; + va_list argumentsCopy; + id *objects; + + if (firstObject == nil) + return [self init]; + + va_copy(argumentsCopy, arguments); + while (va_arg(argumentsCopy, id) != nil) + count++; + + @try { + objects = OFAllocMemory(count, sizeof(id)); + } @catch (id e) { + [self release]; + @throw e; + } + + @try { + objects[0] = firstObject; + + for (size_t i = 1; i < count; i++) { + objects[i] = va_arg(arguments, id); + OFEnsure(objects[i] != nil); + } + + self = [self initWithObjects: objects count: count]; + } @finally { + OFFreeMemory(objects); + } + + return self; } - (instancetype)initWithArray: (OFArray *)array { - OF_INVALID_INIT_METHOD + id *objects; + size_t count; + + @try { + count = array.count; + objects = OFAllocMemory(count, sizeof(id)); + + [array getObjects: objects + inRange: OFMakeRange(0, count)]; + } @catch (id e) { + [self release]; + @throw e; + } + + @try { + self = [self initWithObjects: objects count: count]; + } @finally { + OFFreeMemory(objects); + } + + return self; } +#ifdef __clang__ +/* We intentionally don't call into super, so silence the warning. */ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunknown-pragmas" +# pragma clang diagnostic ignored "-Wobjc-designated-initializers" +#endif - (instancetype)initWithObjects: (id const *)objects count: (size_t)count { OF_INVALID_INIT_METHOD } - -- (instancetype)initWithSerialization: (OFXMLElement *)element -{ - OF_INVALID_INIT_METHOD -} +#ifdef __clang__ +# pragma clang diagnostic pop +#endif - (size_t)count { OF_UNRECOGNIZED_SELECTOR } @@ -375,11 +409,12 @@ if (range.length > SIZE_MAX - range.location || range.location + range.length < self.count) @throw [OFOutOfRangeException exception]; if (![self isKindOfClass: [OFMutableArray class]]) - return [OFSubarray arrayWithArray: self range: range]; + return [[[OFSubarray alloc] initWithArray: self + range: range] autorelease]; buffer = OFAllocMemory(range.length, sizeof(*buffer)); @try { [self getObjects: buffer inRange: range]; @@ -548,37 +583,10 @@ [ret makeImmutable]; return [ret autorelease]; } -- (OFXMLElement *)XMLElementBySerializing -{ - void *pool = objc_autoreleasePoolPush(); - OFXMLElement *element; - - if ([self isKindOfClass: [OFMutableArray class]]) - element = [OFXMLElement elementWithName: @"OFMutableArray" - namespace: OFSerializationNS]; - else - element = [OFXMLElement elementWithName: @"OFArray" - namespace: OFSerializationNS]; - - for (id object in self) { - void *pool2 = objc_autoreleasePoolPush(); - - [element addChild: object.XMLElementBySerializing]; - - objc_autoreleasePoolPop(pool2); - } - - [element retain]; - - objc_autoreleasePoolPop(pool); - - return [element autorelease]; -} - - (OFString *)JSONRepresentation { return [self of_JSONRepresentationWithOptions: 0 depth: 0]; } @@ -687,11 +695,11 @@ [data addItems: child.items count: child.count]; objc_autoreleasePoolPop(pool2); } - assert(i == count); + OFAssert(i == count); [data makeImmutable]; objc_autoreleasePoolPop(pool); @@ -725,10 +733,20 @@ OFMutableArray *new = [[self mutableCopy] autorelease]; [new sortUsingSelector: selector options: options]; [new makeImmutable]; return new; } + +- (OFArray *)sortedArrayUsingFunction: (OFCompareFunction)compare + context: (void *)context + options: (OFArraySortOptions)options +{ + OFMutableArray *new = [[self mutableCopy] autorelease]; + [new sortUsingFunction: compare context: context options: options]; + [new makeImmutable]; + return new; +} #ifdef OF_HAVE_BLOCKS - (OFArray *)sortedArrayUsingComparator: (OFComparator)comparator options: (OFArraySortOptions)options { @@ -749,10 +767,11 @@ - (int)countByEnumeratingWithState: (OFFastEnumerationState *)state objects: (id *)objects count: (int)count { + static unsigned long dummyMutations; OFRange range = OFMakeRange(state->state, count); if (range.length > SIZE_MAX - range.location) @throw [OFOutOfRangeException exception]; @@ -764,11 +783,11 @@ if (range.location + range.length > ULONG_MAX) @throw [OFOutOfRangeException exception]; state->state = (unsigned long)(range.location + range.length); state->itemsPtr = objects; - state->mutationsPtr = (unsigned long *)self; + state->mutationsPtr = &dummyMutations; return (int)range.length; } - (OFEnumerator *)objectEnumerator