/*
* 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