Index: ObjFW.xcodeproj/project.pbxproj ================================================================== --- ObjFW.xcodeproj/project.pbxproj +++ ObjFW.xcodeproj/project.pbxproj @@ -190,10 +190,12 @@ 4B3D23E91337FCB000DD29B8 /* of_asprintf.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BB50DCF12F863C700C9393F /* of_asprintf.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B3D23EA1337FCB000DD29B8 /* threading.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B67998B1099E7C50041064A /* threading.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B3D23EB1337FCB000DD29B8 /* unicode.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B67998C1099E7C50041064A /* unicode.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B3D23EE1337FFD000DD29B8 /* of_asprintf.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BB50DD012F863C700C9393F /* of_asprintf.m */; }; 4B3D5694139A617D0010A78F /* OFSerializationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B3D5693139A617D0010A78F /* OFSerializationTests.m */; }; + 4B45355313DCFE1E0037AB4D /* OFCountedSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B45355113DCFE1E0037AB4D /* OFCountedSet.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4B45355413DCFE1E0037AB4D /* OFCountedSet.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B45355213DCFE1E0037AB4D /* OFCountedSet.m */; }; 4B511B7C139C0A34003764A5 /* OFNull.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B511B7A139C0A34003764A5 /* OFNull.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B511B7D139C0A34003764A5 /* OFNull.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B511B7B139C0A34003764A5 /* OFNull.m */; }; 4B55A0FF133ABEA900B58A93 /* OFThreadJoinFailedException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B55A0F9133ABEA900B58A93 /* OFThreadJoinFailedException.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B55A100133ABEA900B58A93 /* OFThreadJoinFailedException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B55A0FA133ABEA900B58A93 /* OFThreadJoinFailedException.m */; }; 4B55A101133ABEA900B58A93 /* OFThreadStartFailedException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B55A0FB133ABEA900B58A93 /* OFThreadStartFailedException.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -425,10 +427,12 @@ 4B3D23701337FB7500DD29B8 /* OFHTTPRequestTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFHTTPRequestTests.m; path = tests/OFHTTPRequestTests.m; sourceTree = ""; }; 4B3D23761337FBC800DD29B8 /* ObjFW.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ObjFW.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 4B3D23BB1337FC5800DD29B8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = SOURCE_ROOT; }; 4B3D23EF1338008000DD29B8 /* mach_alias_list */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = mach_alias_list; path = src/mach_alias_list; sourceTree = SOURCE_ROOT; }; 4B3D5693139A617D0010A78F /* OFSerializationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFSerializationTests.m; path = tests/OFSerializationTests.m; sourceTree = ""; }; + 4B45355113DCFE1E0037AB4D /* OFCountedSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFCountedSet.h; path = src/OFCountedSet.h; sourceTree = ""; }; + 4B45355213DCFE1E0037AB4D /* OFCountedSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFCountedSet.m; path = src/OFCountedSet.m; sourceTree = ""; }; 4B4986DF1101F64500A2CFDA /* objc_properties.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = objc_properties.m; path = src/objc_properties.m; sourceTree = ""; }; 4B4A61F212DF5EA20048F3F2 /* OFURL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFURL.h; path = src/OFURL.h; sourceTree = SOURCE_ROOT; }; 4B4A61F312DF5EA20048F3F2 /* OFURL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFURL.m; path = src/OFURL.m; sourceTree = SOURCE_ROOT; }; 4B511B7A139C0A34003764A5 /* OFNull.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFNull.h; path = src/OFNull.h; sourceTree = ""; }; 4B511B7B139C0A34003764A5 /* OFNull.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFNull.m; path = src/OFNull.m; sourceTree = ""; }; @@ -805,10 +809,12 @@ 4BD86D801237A6C600ED9912 /* OFBlock.h */, 4BD86D811237A6C600ED9912 /* OFBlock.m */, 4BAF5F46123460C900F4E111 /* OFCollection.h */, 4BE5F0D712DF4225005C7A0C /* OFConstantString.h */, 4BE5F0D812DF4225005C7A0C /* OFConstantString.m */, + 4B45355113DCFE1E0037AB4D /* OFCountedSet.h */, + 4B45355213DCFE1E0037AB4D /* OFCountedSet.m */, 4B6799601099E7C50041064A /* OFDataArray.h */, 4B6799611099E7C50041064A /* OFDataArray.m */, 4BE17AD912FD746D002CEB0B /* OFDataArray+Hashing.h */, 4BE17ADA12FD746D002CEB0B /* OFDataArray+Hashing.m */, 4BE5F0D912DF4225005C7A0C /* OFDate.h */, @@ -1006,10 +1012,11 @@ 4B3D23BD1337FC8300DD29B8 /* OFArray.h in Headers */, 4B3D23BE1337FC8300DD29B8 /* OFAutoreleasePool.h in Headers */, 4B3D23BF1337FC8300DD29B8 /* OFBlock.h in Headers */, 4B3D23C01337FC8300DD29B8 /* OFCollection.h in Headers */, 4B3D23C11337FC8300DD29B8 /* OFConstantString.h in Headers */, + 4B45355313DCFE1E0037AB4D /* OFCountedSet.h in Headers */, 4B3D23C21337FC8300DD29B8 /* OFDataArray.h in Headers */, 4B3D23C31337FC8300DD29B8 /* OFDataArray+Hashing.h in Headers */, 4B3D23C41337FC8300DD29B8 /* OFDate.h in Headers */, 4B3D23C51337FCB000DD29B8 /* OFDictionary.h in Headers */, 4BE852D213B7671200C00856 /* OFDoubleMatrix.h in Headers */, @@ -1265,10 +1272,11 @@ 4B3D238B1337FC0D00DD29B8 /* OFApplication.m in Sources */, 4B3D238C1337FC0D00DD29B8 /* OFArray.m in Sources */, 4B3D238D1337FC0D00DD29B8 /* OFAutoreleasePool.m in Sources */, 4B3D238E1337FC0D00DD29B8 /* OFBlock.m in Sources */, 4B3D238F1337FC0D00DD29B8 /* OFConstantString.m in Sources */, + 4B45355413DCFE1E0037AB4D /* OFCountedSet.m in Sources */, 4B3D23901337FC0D00DD29B8 /* OFDataArray.m in Sources */, 4B3D23911337FC0D00DD29B8 /* OFDataArray+Hashing.m in Sources */, 4B3D23921337FC0D00DD29B8 /* OFDate.m in Sources */, 4B3D23931337FC0D00DD29B8 /* OFDictionary.m in Sources */, 4BE852D313B7671200C00856 /* OFDoubleMatrix.m in Sources */, Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -10,10 +10,11 @@ SRCS = OFApplication.m \ OFArray.m \ OFAutoreleasePool.m \ OFBlock.m \ OFConstantString.m \ + OFCountedSet.m \ OFDataArray.m \ OFDataArray+Hashing.m \ OFDate.m \ OFDictionary.m \ OFDoubleMatrix.m \ ADDED src/OFCountedSet.h Index: src/OFCountedSet.h ================================================================== --- src/OFCountedSet.h +++ src/OFCountedSet.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011 + * 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 + * 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. + */ + +#import "OFSet.h" + +#ifdef OF_HAVE_BLOCKS +typedef void (^of_counted_set_enumeration_block_t)(id object, size_t count, + BOOL *stop); +#endif + +/** + * \brief An unordered set of objects, counting how often it contains an object. + */ +@interface OFCountedSet: OFMutableSet +/** + * \brief Returns how often the object is in the set. + * + * \return How often the object is in the set + */ +- (size_t)countForObject: (id)object; + +#ifdef OF_HAVE_BLOCKS +/** + * \brief Executes a block for each object in the set. + * + * \param block The block to execute for each object in the set + */ +- (void)enumerateObjectsAndCountUsingBlock: + (of_counted_set_enumeration_block_t)block; +#endif +@end ADDED src/OFCountedSet.m Index: src/OFCountedSet.m ================================================================== --- src/OFCountedSet.m +++ src/OFCountedSet.m @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011 + * 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 + * 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" + +#define OF_COUNTED_SET_M + +#import "OFCountedSet.h" +#import "OFDictionary.h" +#import "OFNumber.h" +#import "OFArray.h" +#import "OFString.h" +#import "OFAutoreleasePool.h" + +@implementation OFCountedSet +- initWithSet: (OFSet*)set +{ + self = [super init]; + + @try { + [dictionary release]; + dictionary = nil; + dictionary = [[OFMutableDictionary alloc] + _initWithDictionary: set->dictionary + copyKeys: NO]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- initWithArray: (OFArray*)array +{ + self = [super init]; + + @try { + id *cArray = [array cArray]; + size_t i, count = [array count]; + + for (i = 0; i < count; i++) + [self addObject: cArray[i]]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- initWithObject: (id)firstObject + arguments: (va_list)arguments +{ + self = [super init]; + + @try { + id object; + + [self addObject: firstObject]; + + while ((object = va_arg(arguments, id)) != nil) + [self addObject: object]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (OFString*)description +{ + OFMutableString *ret; + OFAutoreleasePool *pool, *pool2; + OFEnumerator *keyEnumerator, *objectEnumerator; + size_t i, count = [dictionary count]; + id key, object; + + if (count == 0) + return @"{()}"; + + ret = [OFMutableString stringWithString: @"{(\n"]; + pool = [[OFAutoreleasePool alloc] init]; + keyEnumerator = [dictionary keyEnumerator]; + objectEnumerator = [dictionary objectEnumerator]; + + i = 0; + pool2 = [[OFAutoreleasePool alloc] init]; + + while ((key = [keyEnumerator nextObject]) != nil && + (object = [objectEnumerator nextObject]) != nil) { + [ret appendString: [key description]]; + [ret appendString: @": "]; + [ret appendString: [object description]]; + + if (++i < count) + [ret appendString: @",\n"]; + + [pool2 releaseObjects]; + } + [ret replaceOccurrencesOfString: @"\n" + withString: @"\n\t"]; + [ret appendString: @"\n)}"]; + + [pool release]; + + /* + * Class swizzle the string to be immutable. We declared the return type + * to be OFString*, so it can't be modified anyway. But not swizzling it + * would create a real copy each time -[copy] is called. + */ + ret->isa = [OFString class]; + return ret; +} + +- copy +{ + return [[OFCountedSet alloc] initWithSet: self]; +} + +- mutableCopy +{ + return [[OFCountedSet alloc] initWithSet: self]; +} + +- (size_t)countForObject: (id)object +{ + return [[dictionary objectForKey: object] sizeValue]; +} + +#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); + }]; +} +#endif + +- (void)addObject: (id)object +{ + OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; + OFNumber *count; + + count = [[dictionary objectForKey: object] numberByIncreasing]; + + if (count == nil) + count = [OFNumber numberWithSize: 1]; + + [dictionary _setObject: count + forKey: object + copyKey: NO]; + + mutations++; + + [pool release]; +} + +- (void)removeObject: (id)object +{ + OFNumber *count = [dictionary objectForKey: object]; + OFAutoreleasePool *pool; + + if (count == nil) + return; + + pool = [[OFAutoreleasePool alloc] init]; + count = [count numberByDecreasing]; + + if ([count sizeValue] > 0) + [dictionary _setObject: count + forKey: object + copyKey: NO]; + else + [dictionary removeObjectForKey: object]; + + mutations++; + + [pool release]; +} + +- (void)minusSet: (OFSet*)set +{ + OFCountedSet *countedSet; + OFAutoreleasePool *pool; + OFEnumerator *enumerator; + id object; + + if (![set isKindOfClass: [OFCountedSet class]]) { + [super minusSet: set]; + return; + } + + countedSet = (OFCountedSet*)set; + + pool = [[OFAutoreleasePool alloc] init]; + enumerator = [countedSet objectEnumerator]; + + while ((object = [enumerator nextObject]) != nil) { + size_t i, count = [countedSet countForObject: object]; + + for (i = 0; i < count; i++) + [self removeObject: object]; + } + + [pool release]; +} + +- (void)unionSet: (OFSet*)set +{ + OFCountedSet *countedSet; + OFAutoreleasePool *pool; + OFEnumerator *enumerator; + id object; + + if (![set isKindOfClass: [OFCountedSet class]]) { + [super unionSet: set]; + return; + } + + countedSet = (OFCountedSet*)set; + + pool = [[OFAutoreleasePool alloc] init]; + enumerator = [countedSet objectEnumerator]; + + while ((object = [enumerator nextObject]) != nil) { + size_t i, count = [countedSet countForObject: object]; + + for (i = 0; i < count; i++) + [self addObject: object]; + } + + [pool release]; +} +@end Index: src/OFDictionary.h ================================================================== --- src/OFDictionary.h +++ src/OFDictionary.h @@ -222,11 +222,11 @@ */ - (OFDictionary*)filteredDictionaryUsingBlock: (of_dictionary_filter_block_t)block; #endif -#ifdef OF_SET_M +#if defined(OF_SET_M) || defined(OF_COUNTED_SET_M) - _initWithDictionary: (OFDictionary*)dictionary copyKeys: (BOOL)copyKeys; #endif @end Index: src/OFMutableDictionary.h ================================================================== --- src/OFMutableDictionary.h +++ src/OFMutableDictionary.h @@ -53,11 +53,11 @@ * \param block The block which returns a new object for each object */ - (void)replaceObjectsUsingBlock: (of_dictionary_replace_block_t)block; #endif -#if defined(OF_SET_M) || defined(OF_MUTABLE_SET_M) +#if defined(OF_SET_M) || defined(OF_MUTABLE_SET_M) || defined(OF_COUNTED_SET_M) - (void)_setObject: (id)object forKey: (id)key copyKey: (BOOL)copyKey; #endif @end Index: src/OFMutableSet.m ================================================================== --- src/OFMutableSet.m +++ src/OFMutableSet.m @@ -61,11 +61,11 @@ size_t count = [objects count]; size_t i; for (i = 0; i < count; i++) if (![set containsObject: cArray[i]]) - [self removeObject: cArray[i]]; + [dictionary removeObjectForKey: cArray[i]]; [pool release]; } - (void)unionSet: (OFSet*)set Index: src/OFSet.h ================================================================== --- src/OFSet.h +++ src/OFSet.h @@ -117,13 +117,13 @@ */ - (BOOL)intersectsSet: (OFSet*)set; #ifdef OF_HAVE_BLOCKS /** - * \brief Executes a block for each object. + * \brief Executes a block for each object in the set. * - * \param block The block to execute for each object + * \param block The block to execute for each object in the set */ - (void)enumerateObjectsUsingBlock: (of_set_enumeration_block_t)block; /** * \brief Returns a new set, only containing the objects for which the block Index: src/ObjFW.h ================================================================== --- src/ObjFW.h +++ src/ObjFW.h @@ -28,10 +28,11 @@ #import "OFList.h" #import "OFDictionary.h" #import "OFSet.h" +#import "OFCountedSet.h" #import "OFEnumerator.h" #import "OFNull.h"