Index: src/OFCountedSet_hashtable.h ================================================================== --- src/OFCountedSet_hashtable.h +++ src/OFCountedSet_hashtable.h @@ -14,12 +14,12 @@ * file. */ #import "OFCountedSet.h" -@class OFMutableDictionary_hashtable; +@class OFMapTable; @interface OFCountedSet_hashtable: OFCountedSet { - OFMutableDictionary_hashtable *dictionary; + OFMapTable *mapTable; } @end Index: src/OFCountedSet_hashtable.m ================================================================== --- src/OFCountedSet_hashtable.m +++ src/OFCountedSet_hashtable.m @@ -14,23 +14,22 @@ * file. */ #include "config.h" -#define OF_COUNTED_SET_HASHTABLE_M - #import "OFMutableSet_hashtable.h" #import "OFCountedSet_hashtable.h" -#import "OFMutableDictionary_hashtable.h" +#import "OFMapTable.h" #import "OFString.h" -#import "OFNumber.h" #import "OFArray.h" #import "OFXMLElement.h" #import "OFXMLAttribute.h" #import "OFInvalidArgumentException.h" #import "OFInvalidFormatException.h" +#import "OFEnumerationMutationException.h" +#import "OFOutOfRangeException.h" #import "autorelease.h" @implementation OFCountedSet_hashtable + (void)initialize @@ -154,27 +153,25 @@ enumerator = [objects objectEnumerator]; while ((objectElement = [enumerator nextObject]) != nil) { void *pool2 = objc_autoreleasePoolPush(); OFXMLElement *object; - OFXMLAttribute *count; - OFNumber *number; + OFXMLAttribute *count_; + size_t count; object = [[objectElement elementsForNamespace: OF_SERIALIZATION_NS] firstObject]; - count = [objectElement attributeForName: @"count"]; + count_ = [objectElement attributeForName: @"count"]; - if (object == nil || count == nil) + if (object == nil || count_ == nil) @throw [OFInvalidFormatException exceptionWithClass: [self class]]; - number = [OFNumber numberWithSize: - (size_t)[[count stringValue] decimalValue]]; + count = (size_t)[[count_ stringValue] decimalValue]; - [dictionary OF_setObject: number - forKey: [object objectByDeserializing] - copyKey: NO]; + [mapTable setValue: (void*)(uintptr_t)count + forKey: [object objectByDeserializing]]; objc_autoreleasePoolPop(pool2); } objc_autoreleasePoolPop(pool); @@ -186,61 +183,56 @@ return self; } - (size_t)countForObject: (id)object { - return [[dictionary objectForKey: object] sizeValue]; + return (size_t)(uintptr_t)[mapTable valueForKey: object]; } #ifdef OF_HAVE_BLOCKS - (void)enumerateObjectsAndCountUsingBlock: (of_counted_set_enumeration_block_t)block { - [dictionary enumerateKeysAndObjectsUsingBlock: ^ (id key, id object, - BOOL *stop) { - block(key, [object sizeValue], stop); - }]; + @try { + [mapTable enumerateKeysAndValuesUsingBlock: + ^ (void *key, void *value, BOOL *stop) { + block(key, (size_t)(uintptr_t)value, stop); + }]; + } @catch (OFEnumerationMutationException *e) { + @throw [OFEnumerationMutationException + exceptionWithClass: [self class] + object: self]; + } } #endif - (void)addObject: (id)object { - void *pool = objc_autoreleasePoolPush(); - OFNumber *count; - - count = [[dictionary objectForKey: object] numberByIncreasing]; - - if (count == nil) - count = [OFNumber numberWithSize: 1]; - - [dictionary OF_setObject: count - forKey: object - copyKey: NO]; - - objc_autoreleasePoolPop(pool); + size_t count = (size_t)(uintptr_t)[mapTable valueForKey: object]; + + if (SIZE_MAX - count < 1) + @throw [OFOutOfRangeException exceptionWithClass: [self class]]; + + [mapTable setValue: (void*)(uintptr_t)(count + 1) + forKey: object]; } - (void)removeObject: (id)object { - OFNumber *count = [dictionary objectForKey: object]; - void *pool; + size_t count = (size_t)(uintptr_t)[mapTable valueForKey: object]; - if (count == nil) + if (count == 0) return; - pool = objc_autoreleasePoolPush(); - count = [count numberByDecreasing]; - - if ([count sizeValue] > 0) - [dictionary OF_setObject: count - forKey: object - copyKey: NO]; - else - [dictionary removeObjectForKey: object]; - - objc_autoreleasePoolPop(pool); + count--; + + if (count > 0) + [mapTable setValue: (void*)(uintptr_t)count + forKey: object]; + else + [mapTable removeValueForKey: object]; } - (void)makeImmutable { } @end Index: src/OFMapTable.h ================================================================== --- src/OFMapTable.h +++ src/OFMapTable.h @@ -235,5 +235,15 @@ * @brief Resets the enumerator, so the next call to nextKey returns the first * key again. */ - (void)reset; @end + +@interface OFMapTableEnumeratorWrapper: OFEnumerator +{ + OFMapTableEnumerator *enumerator; + id object; +} + +- initWithEnumerator: (OFMapTableEnumerator*)enumerator + object: (id)object; +@end Index: src/OFMapTable.m ================================================================== --- src/OFMapTable.m +++ src/OFMapTable.m @@ -703,7 +703,54 @@ if (position < capacity) return buckets[position++]->value; else return NULL; +} +@end + +@implementation OFMapTableEnumeratorWrapper +- initWithEnumerator: (OFMapTableEnumerator*)enumerator_ + object: (id)object_ +{ + self = [super init]; + + enumerator = [enumerator_ retain]; + object = [object_ retain]; + + return self; +} + +- (void)dealloc +{ + [enumerator release]; + [object release]; + + [super dealloc]; +} + +- (id)nextObject +{ + id ret; + + @try { + ret = [enumerator nextValue]; + } @catch (OFEnumerationMutationException *e) { + @throw [OFEnumerationMutationException + exceptionWithClass: [object class] + object: object]; + } + + return ret; +} + +- (void)reset +{ + @try { + [enumerator reset]; + } @catch (OFEnumerationMutationException *e) { + @throw [OFEnumerationMutationException + exceptionWithClass: [object class] + object: object]; + } } @end Index: src/OFMutableSet_hashtable.h ================================================================== --- src/OFMutableSet_hashtable.h +++ src/OFMutableSet_hashtable.h @@ -14,12 +14,12 @@ * file. */ #import "OFMutableSet.h" -@class OFMutableDictionary_hashtable; +@class OFMapTable; @interface OFMutableSet_hashtable: OFMutableSet { - OFMutableDictionary_hashtable *dictionary; + OFMapTable *mapTable; } @end Index: src/OFMutableSet_hashtable.m ================================================================== --- src/OFMutableSet_hashtable.m +++ src/OFMutableSet_hashtable.m @@ -14,16 +14,13 @@ * file. */ #include "config.h" -#define OF_MUTABLE_SET_HASHTABLE_M - #import "OFSet_hashtable.h" #import "OFMutableSet_hashtable.h" -#import "OFMutableDictionary_hashtable.h" -#import "OFNumber.h" +#import "OFMapTable.h" @implementation OFMutableSet_hashtable + (void)initialize { if (self == [OFMutableSet_hashtable class]) @@ -30,20 +27,19 @@ [self inheritMethodsFromClass: [OFSet_hashtable class]]; } - (void)addObject: (id)object { - [dictionary OF_setObject: [OFNumber numberWithSize: 1] - forKey: object - copyKey: NO]; + [mapTable setValue: (void*)1 + forKey: object]; } - (void)removeObject: (id)object { - [dictionary removeObjectForKey: object]; + [mapTable removeValueForKey: object]; } - (void)makeImmutable { object_setClass(self, [OFSet_hashtable class]); } @end Index: src/OFSet_hashtable.h ================================================================== --- src/OFSet_hashtable.h +++ src/OFSet_hashtable.h @@ -14,12 +14,12 @@ * file. */ #import "OFSet.h" -@class OFMutableDictionary_hashtable; +@class OFMapTable; @interface OFSet_hashtable: OFSet { - OFMutableDictionary_hashtable *dictionary; + OFMapTable *mapTable; } @end Index: src/OFSet_hashtable.m ================================================================== --- src/OFSet_hashtable.m +++ src/OFSet_hashtable.m @@ -14,32 +14,70 @@ * file. */ #include "config.h" -#define OF_SET_HASHTABLE_M - #import "OFSet_hashtable.h" #import "OFMutableSet_hashtable.h" #import "OFCountedSet_hashtable.h" -#import "OFMutableDictionary_hashtable.h" +#import "OFMapTable.h" #import "OFArray.h" #import "OFString.h" -#import "OFNumber.h" #import "OFXMLElement.h" #import "OFInvalidArgumentException.h" +#import "OFEnumerationMutationException.h" #import "autorelease.h" +static void* +retain(void *value) +{ + return [(id)value retain]; +} + +static void +release(void *value) +{ + [(id)value release]; +} + +static uint32_t +hash(void *value) +{ + return [(id)value hash]; +} + +static BOOL +equal(void *value1, void *value2) +{ + return [(id)value1 isEqual: (id)value2]; +} + +static of_map_table_functions_t keyFunctions = { + .retain = retain, + .release = release, + .hash = hash, + .equal = equal +}; +static of_map_table_functions_t valueFunctions = {}; + @implementation OFSet_hashtable - init { + return [self initWithCapacity: 0]; +} + +- initWithCapacity: (size_t)capacity +{ self = [super init]; @try { - dictionary = [[OFMutableDictionary_hashtable alloc] init]; + mapTable = [[OFMapTable alloc] + initWithKeyFunctions: keyFunctions + valueFunctions: valueFunctions + capacity: capacity]; } @catch (id e) { [self release]; @throw e; } @@ -46,32 +84,33 @@ return self; } - initWithSet: (OFSet*)set { - self = [self init]; + size_t count; if (set == nil) - return self; + return [self init]; + + @try { + count = [set count]; + } @catch (id e) { + [self release]; + @throw e; + } + + self = [self initWithCapacity: count]; @try { void *pool = objc_autoreleasePoolPush(); - OFNumber *one; OFEnumerator *enumerator; id object; - one = [OFNumber numberWithSize: 1]; - - /* - * We can't just copy the dictionary as the specified set might - * be a counted set, but we're just a normal set. - */ enumerator = [set objectEnumerator]; while ((object = [enumerator nextObject]) != nil) - [dictionary OF_setObject: one - forKey: object - copyKey: NO]; + [mapTable setValue: (void*)1 + forKey: object]; objc_autoreleasePoolPop(pool); } @catch (id e) { [self release]; @throw e; @@ -80,26 +119,33 @@ return self; } - initWithArray: (OFArray*)array { - self = [self init]; + size_t count; if (array == nil) return self; + @try { + count = [array count]; + } @catch (id e) { + [self release]; + @throw e; + } + + self = [self initWithCapacity: count]; + @try { void *pool = objc_autoreleasePoolPush(); - OFNumber *one = [OFNumber numberWithSize: 1]; OFEnumerator *enumerator; id object; enumerator = [array objectEnumerator]; while ((object = [enumerator nextObject]) != nil) - [dictionary OF_setObject: one - forKey: object - copyKey: NO]; + [mapTable setValue: (void*)1 + forKey: object]; objc_autoreleasePoolPop(pool); } @catch (id e) { [self release]; @throw e; @@ -109,23 +155,18 @@ } - initWithObjects: (id const*)objects count: (size_t)count { - self = [self init]; + self = [self initWithCapacity: count]; @try { - void *pool = objc_autoreleasePoolPush(); - OFNumber *one = [OFNumber numberWithSize: 1]; size_t i; for (i = 0; i < count; i++) - [dictionary OF_setObject: one - forKey: objects[i] - copyKey: NO]; - - objc_autoreleasePoolPop(pool); + [mapTable setValue: (void*)1 + forKey: objects[i]]; } @catch (id e) { [self release]; @throw e; } @@ -133,27 +174,32 @@ } - initWithObject: (id)firstObject arguments: (va_list)arguments { - self = [self init]; + self = [super init]; @try { - void *pool = objc_autoreleasePoolPush(); - OFNumber *one = [OFNumber numberWithSize: 1]; id object; + va_list argumentsCopy; + size_t count; + + va_copy(argumentsCopy, arguments); + + for (count = 1; va_arg(argumentsCopy, id) != nil; count++); + + mapTable = [[OFMapTable alloc] + initWithKeyFunctions: keyFunctions + valueFunctions: valueFunctions + capacity: count]; - [dictionary OF_setObject: one - forKey: firstObject - copyKey: NO]; + [mapTable setValue: (void*)1 + forKey: firstObject]; while ((object = va_arg(arguments, id)) != nil) - [dictionary OF_setObject: one - forKey: object - copyKey: NO]; - - objc_autoreleasePoolPop(pool); + [mapTable setValue: (void*)1 + forKey: object]; } @catch (id e) { [self release]; @throw e; } @@ -164,11 +210,10 @@ { self = [self init]; @try { void *pool = objc_autoreleasePoolPush(); - OFNumber *one; OFEnumerator *enumerator; OFXMLElement *child; if ((![[element name] isEqual: @"OFSet"] && ![[element name] isEqual: @"OFMutableSet"]) || @@ -175,20 +220,17 @@ ![[element namespace] isEqual: OF_SERIALIZATION_NS]) @throw [OFInvalidArgumentException exceptionWithClass: [self class] selector: _cmd]; - one = [OFNumber numberWithSize: 1]; - enumerator = [[element elementsForNamespace: OF_SERIALIZATION_NS] objectEnumerator]; while ((child = [enumerator nextObject]) != nil) { void *pool2 = objc_autoreleasePoolPush(); - [dictionary OF_setObject: one - forKey: [child objectByDeserializing] - copyKey: NO]; + [mapTable setValue: (void*)1 + forKey: [child objectByDeserializing]]; objc_autoreleasePoolPop(pool2); } objc_autoreleasePoolPop(pool); @@ -200,26 +242,26 @@ return self; } - (void)dealloc { - [dictionary release]; + [mapTable release]; [super dealloc]; } - (size_t)count { - return [dictionary count]; + return [mapTable count]; } - (BOOL)containsObject: (id)object { if (object == nil) return NO; - return ([dictionary objectForKey: object] != nil); + return ([mapTable valueForKey: object] != nil); } - (BOOL)isEqual: (id)object { OFSet_hashtable *otherSet; @@ -229,32 +271,40 @@ ![object isKindOfClass: [OFCountedSet_hashtable class]]) return [super isEqual: object]; otherSet = object; - return [otherSet->dictionary isEqual: dictionary]; + return [otherSet->mapTable isEqual: mapTable]; } - (OFEnumerator*)objectEnumerator { - return [dictionary keyEnumerator]; + return [[[OFMapTableEnumeratorWrapper alloc] + initWithEnumerator: [mapTable keyEnumerator] + object: self] autorelease]; } - (int)countByEnumeratingWithState: (of_fast_enumeration_state_t*)state objects: (id*)objects count: (int)count { - return [dictionary countByEnumeratingWithState: state - objects: objects - count: count]; + return [mapTable countByEnumeratingWithState: state + objects: objects + count: count]; } #ifdef OF_HAVE_BLOCKS - (void)enumerateObjectsUsingBlock: (of_set_enumeration_block_t)block { - [dictionary enumerateKeysAndObjectsUsingBlock: ^ (id key, id object, - BOOL *stop) { - block(key, stop); - }]; + @try { + [mapTable enumerateKeysAndValuesUsingBlock: + ^ (void *key, void *value, BOOL *stop) { + block(key, stop); + }]; + } @catch (OFEnumerationMutationException *e) { + @throw [OFEnumerationMutationException + exceptionWithClass: [self class] + object: self]; + } } #endif @end Index: tests/OFSet.m ================================================================== --- tests/OFSet.m +++ tests/OFSet.m @@ -49,11 +49,11 @@ TEST(@"-[hash]", [set1 hash] == [set2 hash]) TEST(@"-[description]", [[set1 description] - isEqual: @"{(\n\tfoo,\n\tbaz,\n\tx,\n\tbar\n)}"] && + isEqual: @"{(\n\tx,\n\tbar,\n\tfoo,\n\tbaz\n)}"] && [[set1 description] isEqual: [set2 description]]) TEST(@"-[copy]", [set1 isEqual: [[set1 copy] autorelease]]) TEST(@"-[mutableCopy]", @@ -96,23 +96,23 @@ i = 0; for (OFString *s in set1) { switch (i) { case 0: - if (![s isEqual: @"foo"]) + if (![s isEqual: @"x"]) ok = NO; break; case 1: - if (![s isEqual: @"baz"]) + if (![s isEqual: @"bar"]) ok = NO; break; case 2: - if (![s isEqual: @"x"]) + if (![s isEqual: @"foo"]) ok = NO; break; case 3: - if (![s isEqual: @"bar"]) + if (![s isEqual: @"baz"]) ok = NO; break; } i++; Index: tests/serialization.xml ================================================================== --- tests/serialization.xml +++ tests/serialization.xml @@ -39,23 +39,23 @@ - foo bar + foo + + bar + foo - - bar - list