Index: src/OFArray.h ================================================================== --- src/OFArray.h +++ src/OFArray.h @@ -10,10 +10,11 @@ */ #include #import "OFObject.h" +#import "OFEnumerator.h" #import "OFFastEnumeration.h" @class OFDataArray; @class OFString; @@ -140,8 +141,26 @@ * * \param separator The string with which the objects should be joined * \return A string containing all objects joined by the separator */ - (OFString*)componentsJoinedByString: (OFString*)separator; + +/** + * \return An OFEnumerator to enumarate through the array's objects + */ +- (OFEnumerator*)enumerator; +@end + +@interface OFArrayEnumerator: OFEnumerator +{ + OFDataArray *array; + size_t count; + unsigned long mutations; + unsigned long *mutations_ptr; + size_t pos; +} + +- initWithDataArray: (OFDataArray*)data + mutationsPointer: (unsigned long*)mutations_ptr; @end #import "OFMutableArray.h" Index: src/OFArray.m ================================================================== --- src/OFArray.m +++ src/OFArray.m @@ -299,10 +299,17 @@ state->itemsPtr = [array cArray]; state->mutationsPtr = (unsigned long*)self; return count; } + +- (OFEnumerator*)enumerator +{ + return [[[OFArrayEnumerator alloc] + initWithDataArray: array + mutationsPointer: NULL] autorelease]; +} - (void)dealloc { OFObject **objs = [array cArray]; size_t i, count = [array count]; @@ -313,5 +320,41 @@ [array release]; [super dealloc]; } @end + +@implementation OFArrayEnumerator +- initWithDataArray: (OFDataArray*)array_ + mutationsPointer: (unsigned long*)mutations_ptr_; +{ + self = [super init]; + + array = array_; + count = [array_ count]; + mutations = *mutations_ptr_; + mutations_ptr = mutations_ptr_; + + return self; +} + +- (id)nextObject +{ + if (mutations_ptr != NULL && *mutations_ptr != mutations) + @throw [OFEnumerationMutationException newWithClass: isa]; + + if (pos < count) + return *(OFObject**)[array itemAtIndex: pos++]; + + return nil; +} + +- reset +{ + if (mutations_ptr != NULL && *mutations_ptr != mutations) + @throw [OFEnumerationMutationException newWithClass: isa]; + + pos = 0; + + return self; +} +@end Index: src/OFDictionary.h ================================================================== --- src/OFDictionary.h +++ src/OFDictionary.h @@ -10,10 +10,11 @@ */ #include #import "OFObject.h" +#import "OFEnumerator.h" #import "OFFastEnumeration.h" @class OFArray; struct of_dictionary_bucket @@ -143,9 +144,38 @@ /** * \return The number of objects in the dictionary */ - (size_t)count; + +/** + * \returns An OFEnumerator to enumerate through the dictionary's objects + */ +- (OFEnumerator*)objectEnumerator; + +/** + * \return An OFEnumerator to enumerate through the dictionary's keys + */ +- (OFEnumerator*)keyEnumerator; +@end + +@interface OFDictionaryEnumerator: OFEnumerator +{ + struct of_dictionary_bucket *data; + size_t size; + unsigned long mutations; + unsigned long *mutations_ptr; + size_t pos; +} + +- initWithData: (struct of_dictionary_bucket*)data + size: (size_t)size + mutationsPointer: (unsigned long*)mutations_ptr; +@end + +@interface OFDictionaryObjectEnumerator: OFDictionaryEnumerator @end -#import "OFEnumerator.h" +@interface OFDictionaryKeyEnumerator: OFDictionaryEnumerator +@end + #import "OFMutableDictionary.h" Index: src/OFDictionary.m ================================================================== --- src/OFDictionary.m +++ src/OFDictionary.m @@ -20,16 +20,10 @@ #import "OFExceptions.h" #import "OFMacros.h" #define BUCKET_SIZE sizeof(struct of_dictionary_bucket) -/* References for static linking */ -void _references_to_categories_of_OFDictionary() -{ - _OFEnumerator_reference = 1; -} - @implementation OFDictionary + dictionary; { return [[[self alloc] init] autorelease]; } @@ -529,10 +523,26 @@ state->itemsPtr = objects; state->mutationsPtr = (unsigned long*)self; return i; } + +- (OFEnumerator*)objectEnumerator +{ + return [[[OFDictionaryObjectEnumerator alloc] + initWithData: data + size: size + mutationsPointer: NULL] autorelease]; +} + +- (OFEnumerator*)keyEnumerator +{ + return [[[OFDictionaryKeyEnumerator alloc] + initWithData: data + size: size + mutationsPointer: NULL] autorelease]; +} - (void)dealloc { size_t i; @@ -572,5 +582,61 @@ OF_HASH_FINALIZE(hash); return hash; } @end + +@implementation OFDictionaryEnumerator +- initWithData: (struct of_dictionary_bucket*)data_ + size: (size_t)size_ + mutationsPointer: (unsigned long*)mutations_ptr_ +{ + self = [super init]; + + data = data_; + size = size_; + mutations = *mutations_ptr_; + mutations_ptr = mutations_ptr_; + + return self; +} + +- reset +{ + if (mutations_ptr != NULL && *mutations_ptr != mutations) + @throw [OFEnumerationMutationException newWithClass: isa]; + + pos = 0; + + return self; +} +@end + +@implementation OFDictionaryObjectEnumerator +- (id)nextObject +{ + if (mutations_ptr != NULL && *mutations_ptr != mutations) + @throw [OFEnumerationMutationException newWithClass: isa]; + + for (; pos < size && data[pos].key == nil; pos++); + + if (pos < size) + return data[pos++].object; + else + return nil; +} +@end + +@implementation OFDictionaryKeyEnumerator +- (id)nextObject +{ + if (mutations_ptr != NULL && *mutations_ptr != mutations) + @throw [OFEnumerationMutationException newWithClass: isa]; + + for (; pos < size && data[pos].key == nil; pos++); + + if (pos < size) + return data[pos++].key; + else + return nil; +} +@end Index: src/OFEnumerator.h ================================================================== --- src/OFEnumerator.h +++ src/OFEnumerator.h @@ -8,64 +8,21 @@ * Q Public License 1.0, which can be found in the file LICENSE included in * the packaging of this file. */ #import "OFObject.h" -#import "OFDictionary.h" - -/** - * An enumerator pair combines a key and its object in a single struct. - */ -typedef struct __of_enumerator_pair { - /// The key - id key; - /// The object for the key - id object; -} of_enumerator_pair_t; - -extern int _OFEnumerator_reference; - -/** - * The OFEnumerator class provides methods to enumerate through objects. - */ -@interface OFEnumerator: OFObject -{ - struct of_dictionary_bucket *data; - size_t size; - size_t pos; -} - -- initWithData: (struct of_dictionary_bucket*)data - size: (size_t)size; - -/** - * \return A struct containing the next key and object - */ -- (of_enumerator_pair_t)nextKeyObjectPair; + +/** + * The OFEnumerator class provides methods to enumerate through collections. + */ +@interface OFEnumerator: OFObject {} +/** + * \return The next object + */ +- (id)nextObject; /** * Resets the enumerator, so the next call to nextObject returns the first - * again. + * object again. */ - reset; @end - -/** - * The OFEnumerator category adds functions to get an interator to OFDictionary. - */ -@interface OFDictionary (OFEnumerator) -/** - * Creates an OFEnumerator for the dictionary. - * - * It will copy the data of the OFDictionary so that OFEnumerator will always - * operate on the data that was present when it was created. If you changed the - * OFDictionary and want to operate on the new data, you need to create a new - * OFEnumerator, as using reset will only reset the OFEnumerator, but won't - * update the data. It will also retain the data inside the OFDictionary so the - * OFEnumerator still works after you released the OFDictionary. Thus, if you - * want to get rid of the objects in the OFDictionary, you also need to release - * the OFEnumerator. - * - * \return An OFEnumerator for the OFDictionary - */ -- (OFEnumerator*)enumerator; -@end Index: src/OFEnumerator.m ================================================================== --- src/OFEnumerator.m +++ src/OFEnumerator.m @@ -10,87 +10,29 @@ */ #include "config.h" #import "OFEnumerator.h" -#import "OFDictionary.h" #import "OFExceptions.h" -/* Reference for static linking */ -int _OFEnumerator_reference; - @implementation OFEnumerator - init +{ + if (isa == [OFEnumerator class]) + @throw [OFNotImplementedException newWithClass: isa + selector: _cmd]; + + return [super init]; +} + +- (id)nextObject { @throw [OFNotImplementedException newWithClass: isa selector: _cmd]; } -- initWithData: (struct of_dictionary_bucket*)data_ - size: (size_t)size_ -{ - size_t i; - - self = [super init]; - - size = size_; - data = [self allocMemoryForNItems: size - withSize: sizeof(struct of_dictionary_bucket)]; - - for (i = 0; i < size; i++) { - if (data_[i].key != nil) { - data[i].key = [data_[i].key copy]; - data[i].object = [data_[i].object retain]; - } else - data[i].key = nil; - } - - return self; -} - -- (void)dealloc -{ - size_t i; - - for (i = 0; i < size; i++) { - if (data[i].key != nil) { - [data[i].key release]; - [data[i].object release]; - } - } - - [super dealloc]; -} - -- (of_enumerator_pair_t)nextKeyObjectPair -{ - of_enumerator_pair_t next; - - for (; pos < size && data[pos].key == nil; pos++); - - if (pos < size) { - next.key = data[pos].key; - next.object = data[pos].object; - pos++; - } else { - next.key = nil; - next.object = nil; - } - - return next; -} - - reset { - pos = 0; - - return self; -} -@end - -@implementation OFDictionary (OFEnumerator) -- (OFEnumerator*)enumerator -{ - return [[[OFEnumerator alloc] initWithData: data - size: size] autorelease]; + @throw [OFNotImplementedException newWithClass: isa + selector: _cmd]; } @end Index: src/OFMutableArray.m ================================================================== --- src/OFMutableArray.m +++ src/OFMutableArray.m @@ -238,6 +238,13 @@ state->itemsPtr = [array cArray]; state->mutationsPtr = &mutations; return count; } + +- (OFEnumerator*)enumerator +{ + return [[[OFArrayEnumerator alloc] + initWithDataArray: array + mutationsPointer: &mutations] autorelease]; +} @end Index: src/OFMutableDictionary.m ================================================================== --- src/OFMutableDictionary.m +++ src/OFMutableDictionary.m @@ -185,6 +185,22 @@ state->itemsPtr = objects; state->mutationsPtr = &mutations; return i; } + +- (OFEnumerator*)objectEnumerator +{ + return [[[OFDictionaryObjectEnumerator alloc] + initWithData: data + size: size + mutationsPointer: &mutations] autorelease]; +} + +- (OFEnumerator*)keyEnumerator +{ + return [[[OFDictionaryKeyEnumerator alloc] + initWithData: data + size: size + mutationsPointer: &mutations] autorelease]; +} @end Index: tests/OFArray.m ================================================================== --- tests/OFArray.m +++ tests/OFArray.m @@ -30,10 +30,14 @@ array_tests() { OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; OFArray *a[2]; OFMutableArray *m[2]; + OFEnumerator *enumerator; + id obj; + BOOL ok; + size_t i; TEST(@"+[array]", (m[0] = [OFMutableArray array])) TEST(@"+[arrayWithObjects:]", (a[0] = [OFArray arrayWithObjects: @"Foo", @"Bar", @"Baz", nil])) @@ -116,15 +120,36 @@ a[1] = [OFArray arrayWithObjects: @"foo", @"bar", @"baz", nil]; TEST(@"-[componentsJoinedByString:]", [[a[1] componentsJoinedByString: @" "] isEqual: @"foo bar baz"]) -#ifdef OF_HAVE_FAST_ENUMERATION - size_t i = 0; - BOOL ok = YES; + m[0] = [[a[0] mutableCopy] autorelease]; + ok = YES; + i = 0; + + TEST(@"-[enumerator]", (enumerator = [m[0] enumerator])) + + while ((obj = [enumerator nextObject]) != nil) { + if (![obj isEqual: c_ary[i]]) + ok = NO; + [m[0] replaceObjectAtIndex: i + withObject: @""]; + i++; + } + + TEST(@"OFEnumerator's -[nextObject]", ok) + + [enumerator reset]; + [m[0] removeObjectAtIndex: 0]; + + EXPECT_EXCEPTION(@"Detection of mutation during enumeration", + OFEnumerationMutationException, [enumerator nextObject]) +#ifdef OF_HAVE_FAST_ENUMERATION m[0] = [[a[0] mutableCopy] autorelease]; + ok = YES; + i = 0; for (OFString *s in m[0]) { if (![s isEqual: c_ary[i]]) ok = NO; [m[0] replaceObjectAtIndex: i Index: tests/OFDictionary.m ================================================================== --- tests/OFDictionary.m +++ tests/OFDictionary.m @@ -32,12 +32,11 @@ void dictionary_tests() { OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; OFMutableDictionary *dict = [OFMutableDictionary dictionary], *dict2; - OFEnumerator *enumerator; - of_enumerator_pair_t pair[3]; + OFEnumerator *key_enum, *obj_enum; OFArray *akeys, *avalues; [dict setObject: values[0] forKey: keys[0]]; [dict setObject: values[1] @@ -46,21 +45,28 @@ TEST(@"-[objectForKey:]", [[dict objectForKey: keys[0]] isEqual: values[0]] && [[dict objectForKey: keys[1]] isEqual: values[1]] && [dict objectForKey: @"key3"] == nil) - TEST(@"-[enumerator]", (enumerator = [dict enumerator])) - - pair[0] = [enumerator nextKeyObjectPair]; - pair[1] = [enumerator nextKeyObjectPair]; - pair[2] = [enumerator nextKeyObjectPair]; - TEST(@"OFEnumerator's -[nextKeyObjectPair]", - [pair[0].key isEqual: keys[0]] && - [pair[0].object isEqual: values[0]] && - [pair[1].key isEqual: keys[1]] && - [pair[1].object isEqual: values[1]] && - pair[2].key == nil && pair[2].object == nil) + TEST(@"-[keyEnumerator]", (key_enum = [dict keyEnumerator])) + TEST(@"-[objectEnumerator]", (obj_enum = [dict objectEnumerator])) + + TEST(@"OFEnumerator's -[nextObject]", + [[key_enum nextObject] isEqual: keys[0]] && + [[obj_enum nextObject] isEqual: values[0]] && + [[key_enum nextObject] isEqual: keys[1]] && + [[obj_enum nextObject] isEqual: values[1]] && + [key_enum nextObject] == nil && [obj_enum nextObject] == nil) + + [key_enum reset]; + [dict removeObjectForKey: keys[0]]; + + EXPECT_EXCEPTION(@"Detection of mutation during enumeration", + OFEnumerationMutationException, [key_enum nextObject]); + + [dict setObject: values[0] + forKey: keys[0]]; #ifdef OF_HAVE_FAST_ENUMERATION size_t i = 0; BOOL ok = YES;