Artifact 9d67fa5621ca3f6c60a21d1e4db8642511657f60bfad9d3f1d29ecc86d48e184:
- File
src/OFArray.m
— part of check-in
[e1e7ffa903]
at
2011-09-22 23:25:42
on branch trunk
— Exceptions are now autoreleased.
This is safe as an "exception loop" can't happen, since if allocating
an exception fails, it throws an OFAllocFailedException which is
preallocated and can always be thrown.So, the worst case would be that an autorelease of an exception fails,
triggering an OFOutOfMemoryException for which there is no memory,
resulting in an OFAllocFailedException to be thrown. (user: js, size: 13172) [annotate] [blame] [check-ins using]
/* * Copyright (c) 2008, 2009, 2010, 2011 * Jonathan Schleifer <js@webkeks.org> * * 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 <stdarg.h> #import "OFArray.h" #import "OFArray_subarray.h" #import "OFArray_adjacent.h" #import "OFString.h" #import "OFXMLElement.h" #import "OFAutoreleasePool.h" #import "OFEnumerationMutationException.h" #import "OFInvalidArgumentException.h" #import "OFNotImplementedException.h" #import "OFOutOfRangeException.h" #import "macros.h" static struct { Class isa; } placeholder; @implementation OFArray_placeholder - init { return (id)[[OFArray_adjacent alloc] init]; } - initWithObject: (id)object { return (id)[[OFArray_adjacent alloc] initWithObject: object]; } - initWithObjects: (id)firstObject, ... { id ret; va_list arguments; va_start(arguments, firstObject); ret = [[OFArray_adjacent alloc] initWithObject: firstObject arguments: arguments]; va_end(arguments); return ret; } - initWithObject: (id)firstObject arguments: (va_list)arguments { return (id)[[OFArray_adjacent alloc] initWithObject: firstObject arguments: arguments]; } - initWithArray: (OFArray*)array { return (id)[[OFArray_adjacent alloc] initWithArray: array]; } - initWithCArray: (id*)objects { return (id)[[OFArray_adjacent alloc] initWithCArray: objects]; } - initWithCArray: (id*)objects length: (size_t)length { return (id)[[OFArray_adjacent alloc] initWithCArray: objects length: length]; } - initWithSerialization: (OFXMLElement*)element { return (id)[[OFArray_adjacent alloc] initWithSerialization: element]; } - retain { return self; } - autorelease { return self; } - (void)release { } - (void)dealloc { @throw [OFNotImplementedException exceptionWithClass: isa selector: _cmd]; [super dealloc]; /* Get rid of a stupid warning */ } @end @implementation OFArray + (void)initialize { if (self == [OFArray class]) placeholder.isa = [OFArray_placeholder class]; } + alloc { if (self == [OFArray class]) return (id)&placeholder; return [super alloc]; } + array { return [[[self alloc] init] autorelease]; } + arrayWithObject: (id)object { return [[[self alloc] initWithObject: object] autorelease]; } + arrayWithObjects: (id)firstObject, ... { id ret; va_list arguments; va_start(arguments, firstObject); ret = [[[self alloc] initWithObject: firstObject arguments: arguments] autorelease]; va_end(arguments); return ret; } + arrayWithArray: (OFArray*)array { return [[[self alloc] initWithArray: array] autorelease]; } + arrayWithCArray: (id*)objects { return [[[self alloc] initWithCArray: objects] autorelease]; } + arrayWithCArray: (id*)objects length: (size_t)length { return [[[self alloc] initWithCArray: objects length: length] autorelease]; } - init { if (isa == [OFArray class]) { Class c = isa; [self release]; @throw [OFNotImplementedException exceptionWithClass: c selector: _cmd]; } return [super init]; } - initWithObject: (id)object { return [self initWithObjects: object, nil]; } - initWithObjects: (id)firstObject, ... { id ret; va_list arguments; va_start(arguments, firstObject); ret = [self initWithObject: firstObject arguments: arguments]; va_end(arguments); return ret; } - initWithObject: (id)firstObject arguments: (va_list)arguments { Class c = isa; [self release]; @throw [OFNotImplementedException exceptionWithClass: c selector: _cmd]; } - initWithArray: (OFArray*)array { Class c = isa; [self release]; @throw [OFNotImplementedException exceptionWithClass: c selector: _cmd]; } - initWithCArray: (id*)objects { Class c = isa; [self release]; @throw [OFNotImplementedException exceptionWithClass: c selector: _cmd]; } - initWithCArray: (id*)objects length: (size_t)length { Class c = isa; [self release]; @throw [OFNotImplementedException exceptionWithClass: c selector: _cmd]; } - initWithSerialization: (OFXMLElement*)element { Class c = isa; [self release]; @throw [OFNotImplementedException exceptionWithClass: c selector: _cmd]; } - (size_t)count { @throw [OFNotImplementedException exceptionWithClass: isa selector: _cmd]; } - (void)getObjects: (id*)buffer inRange: (of_range_t)range { size_t i; for (i = 0; i < range.length; i++) buffer[i] = [self objectAtIndex: range.start + i]; } - (id*)cArray { OFObject *container; size_t count; id *buffer; container = [[[OFObject alloc] init] autorelease]; count = [self count]; buffer = [container allocMemoryForNItems: [self count] ofSize: sizeof(*buffer)]; [self getObjects: buffer inRange: of_range(0, count)]; return buffer; } - copy { return [self retain]; } - mutableCopy { return [[OFMutableArray alloc] initWithArray: self]; } - (id)objectAtIndex: (size_t)index { @throw [OFNotImplementedException exceptionWithClass: isa selector: _cmd]; } - (size_t)indexOfObject: (id)object { size_t i, count = [self count]; for (i = 0; i < count; i++) if ([[self objectAtIndex: i] isEqual: object]) return i; return OF_INVALID_INDEX; } - (size_t)indexOfObjectIdenticalTo: (id)object { size_t i, count = [self count]; for (i = 0; i < count; i++) if ([self objectAtIndex: i] == object) return i; return OF_INVALID_INDEX; } - (BOOL)containsObject: (id)object { return ([self indexOfObject: object] != OF_INVALID_INDEX); } - (BOOL)containsObjectIdenticalTo: (id)object { return ([self indexOfObjectIdenticalTo: object] != OF_INVALID_INDEX); } - (id)firstObject { if ([self count] > 0) return [self objectAtIndex: 0]; return nil; } - (id)lastObject { size_t count = [self count]; if (count > 0) return [self objectAtIndex: count - 1]; return nil; } - (OFArray*)objectsInRange: (of_range_t)range { OFArray *ret; id *buffer; if (![self isKindOfClass: [OFMutableArray class]]) return [OFArray_subarray arrayWithArray: self range: range]; buffer = [self allocMemoryForNItems: range.length ofSize: sizeof(*buffer)]; @try { [self getObjects: buffer inRange: range]; ret = [OFArray arrayWithCArray: buffer length: range.length]; } @finally { [self freeMemory: buffer]; } return ret; } - (OFString*)componentsJoinedByString: (OFString*)separator { OFAutoreleasePool *pool, *pool2; OFMutableString *ret; id *cArray; size_t i, count = [self count]; IMP append; if (count == 0) return @""; if (count == 1) return [[self firstObject] description]; ret = [OFMutableString string]; append = [ret methodForSelector: @selector(appendString:)]; pool = [[OFAutoreleasePool alloc] init]; cArray = [self cArray]; pool2 = [[OFAutoreleasePool alloc] init]; for (i = 0; i < count - 1; i++) { append(ret, @selector(appendString:), [cArray[i] description]); append(ret, @selector(appendString:), separator); [pool2 releaseObjects]; } append(ret, @selector(appendString:), [cArray[i] description]); [ret makeImmutable]; [pool release]; return ret; } - (BOOL)isEqual: (id)object { /* FIXME: Optimize (for example, buffer of 16 for each) */ OFArray *otherArray; size_t i, count; if (![object isKindOfClass: [OFArray class]]) return NO; otherArray = object; count = [self count]; if (count != [otherArray count]) return NO; for (i = 0; i < count; i++) if (![[self objectAtIndex: i] isEqual: [otherArray objectAtIndex: i]]) return NO; return YES; } - (uint32_t)hash { id *cArray = [self cArray]; size_t i, count = [self count]; uint32_t hash; OF_HASH_INIT(hash); for (i = 0; i < count; i++) { uint32_t h = [cArray[i] hash]; OF_HASH_ADD(hash, h >> 24); OF_HASH_ADD(hash, (h >> 16) & 0xFF); OF_HASH_ADD(hash, (h >> 8) & 0xFF); OF_HASH_ADD(hash, h & 0xFF); } OF_HASH_FINALIZE(hash); return hash; } - (OFString*)description { OFAutoreleasePool *pool; OFMutableString *ret; if ([self count] == 0) return @"()"; pool = [[OFAutoreleasePool alloc] init]; ret = [[self componentsJoinedByString: @",\n"] mutableCopy]; @try { [ret prependString: @"(\n"]; [ret replaceOccurrencesOfString: @"\n" withString: @"\n\t"]; [ret appendString: @"\n)"]; } @catch (id e) { [ret release]; @throw e; } [pool release]; [ret makeImmutable]; [ret autorelease]; return ret; } - (OFXMLElement*)XMLElementBySerializing { OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; OFAutoreleasePool *pool2; OFXMLElement *element; id <OFSerialization> *cArray = [self cArray]; size_t i, count = [self count]; if ([self isKindOfClass: [OFMutableArray class]]) element = [OFXMLElement elementWithName: @"OFMutableArray" namespace: OF_SERIALIZATION_NS]; else element = [OFXMLElement elementWithName: @"OFArray" namespace: OF_SERIALIZATION_NS]; pool2 = [[OFAutoreleasePool alloc] init]; for (i = 0; i < count; i++) { [element addChild: [cArray[i] XMLElementBySerializing]]; [pool2 releaseObjects]; } [element retain]; [pool release]; [element autorelease]; return element; } - (void)makeObjectsPerformSelector: (SEL)selector { id *cArray = [self cArray]; size_t i, count = [self count]; for (i = 0; i < count; i++) ((void(*)(id, SEL))[cArray[i] methodForSelector: selector])(cArray[i], selector); } - (void)makeObjectsPerformSelector: (SEL)selector withObject: (id)object { id *cArray = [self cArray]; size_t i, count = [self count]; for (i = 0; i < count; i++) ((void(*)(id, SEL, id))[cArray[i] methodForSelector: selector])(cArray[i], selector, object); } - (int)countByEnumeratingWithState: (of_fast_enumeration_state_t*)state objects: (id*)objects count: (int)count_ { /* FIXME: Use -[getObjects:inRange:] on the passed objects */ size_t count = [self count]; if (count > INT_MAX) @throw [OFOutOfRangeException exceptionWithClass: isa]; if (state->state >= count) return 0; state->state = count; state->itemsPtr = [self cArray]; state->mutationsPtr = (unsigned long*)self; return (int)count; } - (OFEnumerator*)objectEnumerator { return [[[OFArrayEnumerator alloc] initWithArray: self mutationsPtr: NULL] autorelease]; } #if defined(OF_HAVE_BLOCKS) && defined(OF_HAVE_FAST_ENUMERATION) - (void)enumerateObjectsUsingBlock: (of_array_enumeration_block_t)block { size_t i = 0; BOOL stop = NO; for (id object in self) { block(object, i++, &stop); if (stop) break; } } #endif #ifdef OF_HAVE_BLOCKS - (OFArray*)mappedArrayUsingBlock: (of_array_map_block_t)block { OFArray *ret; size_t count = [self count]; id *tmp = [self allocMemoryForNItems: count ofSize: sizeof(id)]; @try { [self enumerateObjectsUsingBlock: ^ (id object, size_t index, BOOL *stop) { tmp[index] = block(object, index); }]; ret = [OFArray arrayWithCArray: tmp length: count]; } @finally { [self freeMemory: tmp]; } return ret; } - (OFArray*)filteredArrayUsingBlock: (of_array_filter_block_t)block { OFArray *ret; size_t count = [self count]; id *tmp = [self allocMemoryForNItems: count ofSize: sizeof(id)]; @try { __block size_t i = 0; [self enumerateObjectsUsingBlock: ^ (id object, size_t index, BOOL *stop) { if (block(object, index)) tmp[i++] = object; }]; ret = [OFArray arrayWithCArray: tmp length: i]; } @finally { [self freeMemory: tmp]; } return ret; } - (id)foldUsingBlock: (of_array_fold_block_t)block { size_t count = [self count]; __block id current; if (count == 0) return nil; if (count == 1) return [[[self firstObject] retain] autorelease]; [self enumerateObjectsUsingBlock: ^ (id object, size_t index, BOOL *stop) { id new; if (index == 0) { current = [object retain]; return; } @try { new = [block(current, object) retain]; } @finally { [current release]; } current = new; }]; return [current autorelease]; } #endif @end @implementation OFArrayEnumerator - initWithArray: (OFArray*)array_ mutationsPtr: (unsigned long*)mutationsPtr_ { self = [super init]; array = [array_ retain]; count = [array count]; mutations = (mutationsPtr_ != NULL ? *mutationsPtr_ : 0); mutationsPtr = mutationsPtr_; return self; } - (void)dealloc { [array release]; [super dealloc]; } - (id)nextObject { if (mutationsPtr != NULL && *mutationsPtr != mutations) @throw [OFEnumerationMutationException exceptionWithClass: isa object: array]; if (position < count) return [array objectAtIndex: position++]; return nil; } - (void)reset { if (mutationsPtr != NULL && *mutationsPtr != mutations) @throw [OFEnumerationMutationException exceptionWithClass: isa object: array]; position = 0; } @end