Index: ObjFW.xcodeproj/project.pbxproj ================================================================== --- ObjFW.xcodeproj/project.pbxproj +++ ObjFW.xcodeproj/project.pbxproj @@ -93,10 +93,15 @@ 4B29BC5C133AC8540004B236 /* OFRenameFileFailedException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B29BC54133AC81B0004B236 /* OFRenameFileFailedException.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B29BC61133AC9CB0004B236 /* OFLinkFailedException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B29BC5D133AC9C40004B236 /* OFLinkFailedException.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B29BC62133AC9CB0004B236 /* OFLinkFailedException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B29BC5E133AC9C40004B236 /* OFLinkFailedException.m */; }; 4B29BC63133AC9CB0004B236 /* OFSymlinkFailedException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B29BC5F133AC9C60004B236 /* OFSymlinkFailedException.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B29BC64133AC9CB0004B236 /* OFSymlinkFailedException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B29BC60133AC9C90004B236 /* OFSymlinkFailedException.m */; }; + 4B39844213D3A24600E6F825 /* OFSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B39844013D3A24600E6F825 /* OFSet.h */; }; + 4B39844313D3A24600E6F825 /* OFSet.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B39844113D3A24600E6F825 /* OFSet.m */; }; + 4B39844713D3AFB400E6F825 /* OFMutableSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B39844513D3AFB400E6F825 /* OFMutableSet.h */; }; + 4B39844813D3AFB400E6F825 /* OFMutableSet.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B39844613D3AFB400E6F825 /* OFMutableSet.m */; }; + 4B39844A13D3D03000E6F825 /* OFSet.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B39844913D3D03000E6F825 /* OFSet.m */; }; 4B3D238B1337FC0D00DD29B8 /* OFApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B175C1E116D130B003C99CB /* OFApplication.m */; }; 4B3D238C1337FC0D00DD29B8 /* OFArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B67995B1099E7C50041064A /* OFArray.m */; }; 4B3D238D1337FC0D00DD29B8 /* OFAutoreleasePool.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B67995D1099E7C50041064A /* OFAutoreleasePool.m */; }; 4B3D238E1337FC0D00DD29B8 /* OFBlock.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BD86D811237A6C600ED9912 /* OFBlock.m */; }; 4B3D238F1337FC0D00DD29B8 /* OFConstantString.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BE5F0D812DF4225005C7A0C /* OFConstantString.m */; }; @@ -406,10 +411,15 @@ 4B29BC55133AC81B0004B236 /* OFRenameFileFailedException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFRenameFileFailedException.m; path = src/exceptions/OFRenameFileFailedException.m; sourceTree = ""; }; 4B29BC5D133AC9C40004B236 /* OFLinkFailedException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFLinkFailedException.h; path = src/exceptions/OFLinkFailedException.h; sourceTree = ""; }; 4B29BC5E133AC9C40004B236 /* OFLinkFailedException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFLinkFailedException.m; path = src/exceptions/OFLinkFailedException.m; sourceTree = ""; }; 4B29BC5F133AC9C60004B236 /* OFSymlinkFailedException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFSymlinkFailedException.h; path = src/exceptions/OFSymlinkFailedException.h; sourceTree = ""; }; 4B29BC60133AC9C90004B236 /* OFSymlinkFailedException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFSymlinkFailedException.m; path = src/exceptions/OFSymlinkFailedException.m; sourceTree = ""; }; + 4B39844013D3A24600E6F825 /* OFSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFSet.h; path = src/OFSet.h; sourceTree = ""; }; + 4B39844113D3A24600E6F825 /* OFSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFSet.m; path = src/OFSet.m; sourceTree = ""; }; + 4B39844513D3AFB400E6F825 /* OFMutableSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFMutableSet.h; path = src/OFMutableSet.h; sourceTree = ""; }; + 4B39844613D3AFB400E6F825 /* OFMutableSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFMutableSet.m; path = src/OFMutableSet.m; sourceTree = ""; }; + 4B39844913D3D03000E6F825 /* OFSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFSet.m; path = tests/OFSet.m; sourceTree = ""; }; 4B3D236D1337FB5800DD29B8 /* base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = base64.h; path = src/base64.h; sourceTree = ""; }; 4B3D236E1337FB5800DD29B8 /* base64.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = base64.m; path = src/base64.m; sourceTree = ""; }; 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; }; @@ -823,10 +833,12 @@ 4BF1BCC311C9663F0025511F /* OFMD5Hash.m */, 4B67996F1099E7C50041064A /* OFMutableArray.h */, 4B6799701099E7C50041064A /* OFMutableArray.m */, 4B6799711099E7C50041064A /* OFMutableDictionary.h */, 4B6799721099E7C50041064A /* OFMutableDictionary.m */, + 4B39844513D3AFB400E6F825 /* OFMutableSet.h */, + 4B39844613D3AFB400E6F825 /* OFMutableSet.m */, 4B6799731099E7C50041064A /* OFMutableString.h */, 4B6799741099E7C50041064A /* OFMutableString.m */, 4B511B7A139C0A34003764A5 /* OFNull.h */, 4B511B7B139C0A34003764A5 /* OFNull.m */, 4B6799751099E7C50041064A /* OFNumber.h */, @@ -838,10 +850,12 @@ 4B6799791099E7C50041064A /* OFPlugin.h */, 4B67997A1099E7C50041064A /* OFPlugin.m */, 4B981CDE116F71DD00294DB7 /* OFSeekableStream.h */, 4B981CDF116F71DD00294DB7 /* OFSeekableStream.m */, 4B989C2E13771A3700109A30 /* OFSerialization.h */, + 4B39844013D3A24600E6F825 /* OFSet.h */, + 4B39844113D3A24600E6F825 /* OFSet.m */, 4BF1BCC411C9663F0025511F /* OFSHA1Hash.h */, 4BF1BCC511C9663F0025511F /* OFSHA1Hash.m */, 4B67997D1099E7C50041064A /* OFStream.h */, 4B67997E1099E7C50041064A /* OFStream.m */, 4BAF5F47123460C900F4E111 /* OFStreamObserver.h */, @@ -914,10 +928,11 @@ 4B6EF6731235358D0076B512 /* OFMD5HashTests.m */, 4B6EF6741235358D0076B512 /* OFNumberTests.m */, 4B6EF6751235358D0076B512 /* OFObjectTests.m */, 4B6EF6761235358D0076B512 /* OFPluginTests.m */, 4B3D5693139A617D0010A78F /* OFSerializationTests.m */, + 4B39844913D3D03000E6F825 /* OFSet.m */, 4B6EF6771235358D0076B512 /* OFSHA1HashTests.m */, 4B6EF6781235358D0076B512 /* OFStreamTests.m */, 4B6EF6791235358D0076B512 /* OFStringTests.m */, 4B6EF67A1235358D0076B512 /* OFTCPSocketTests.m */, 4B6EF67B1235358D0076B512 /* OFThreadTests.m */, @@ -1089,10 +1104,12 @@ 4B55A101133ABEA900B58A93 /* OFThreadStartFailedException.h in Headers */, 4B55A103133ABEA900B58A93 /* OFThreadStillRunningException.h in Headers */, 4B55A116133AC24600B58A93 /* OFWriteFailedException.h in Headers */, 4B55A109133AC05100B58A93 /* common.h in Headers */, 4BDF37B51338055600F9A81A /* config.h in Headers */, + 4B39844213D3A24600E6F825 /* OFSet.h in Headers */, + 4B39844713D3AFB400E6F825 /* OFMutableSet.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ @@ -1339,10 +1356,12 @@ 4B55A104133ABEA900B58A93 /* OFThreadStillRunningException.m in Sources */, 4B17FFAA133A34E7003E6DCD /* OFTruncatedDataException.m in Sources */, 4B17FFB6133A375B003E6DCD /* OFUnboundNamespaceException.m in Sources */, 4B17FFB2133A3664003E6DCD /* OFUnsupportedProtocolException.m in Sources */, 4B55A117133AC24600B58A93 /* OFWriteFailedException.m in Sources */, + 4B39844313D3A24600E6F825 /* OFSet.m in Sources */, + 4B39844813D3AFB400E6F825 /* OFMutableSet.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 4BF33AEC133807310059CEF7 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -1357,10 +1376,12 @@ 4BF33B03133807A20059CEF7 /* OFListTests.m in Sources */, 4BF33B04133807A20059CEF7 /* OFMD5HashTests.m in Sources */, 4BF33B05133807A20059CEF7 /* OFNumberTests.m in Sources */, 4BF33B06133807A20059CEF7 /* OFObjectTests.m in Sources */, 4BF33B07133807A20059CEF7 /* OFPluginTests.m in Sources */, + 4B3D5694139A617D0010A78F /* OFSerializationTests.m in Sources */, + 4B39844A13D3D03000E6F825 /* OFSet.m in Sources */, 4BF33B08133807A20059CEF7 /* OFSHA1HashTests.m in Sources */, 4BF33B09133807A20059CEF7 /* OFStreamTests.m in Sources */, 4BF33B0A133807A20059CEF7 /* OFStringTests.m in Sources */, 4BF33B0B133807A20059CEF7 /* OFTCPSocketTests.m in Sources */, 4BF33B0C133807A20059CEF7 /* OFThreadTests.m in Sources */, @@ -1368,11 +1389,10 @@ 4BF33B0E133807A20059CEF7 /* OFXMLElementBuilderTests.m in Sources */, 4BF33B0F133807A20059CEF7 /* OFXMLElementTests.m in Sources */, 4BF33B10133807A20059CEF7 /* OFXMLParserTests.m in Sources */, 4BF33B11133807A20059CEF7 /* PropertiesTests.m in Sources */, 4BF33B12133807A20059CEF7 /* TestsAppDelegate.m in Sources */, - 4B3D5694139A617D0010A78F /* OFSerializationTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -26,17 +26,19 @@ OFHTTPRequest.m \ OFList.m \ OFMD5Hash.m \ OFMutableArray.m \ OFMutableDictionary.m \ + OFMutableSet.m \ OFMutableString.m \ OFNull.m \ OFNumber.m \ OFObject.m \ OFObject+Serialization.m \ ${OFPLUGIN_M} \ OFSeekableStream.m \ + OFSet.m \ OFSHA1Hash.m \ OFStream.m \ OFStreamObserver.m \ OFStreamSocket.m \ OFString.m \ Index: src/OFDictionary.h ================================================================== --- src/OFDictionary.h +++ src/OFDictionary.h @@ -207,10 +207,15 @@ * \return A new, autoreleased OFDictionary */ - (OFDictionary*)filteredDictionaryUsingBlock: (of_dictionary_filter_block_t)block; #endif + +#ifdef OF_SET_M +- _initWithDictionary: (OFDictionary*)dictionary + copyKeys: (BOOL)copyKeys; +#endif @end @interface OFDictionaryEnumerator: OFEnumerator { OFDictionary *dictionary; Index: src/OFDictionary.m ================================================================== --- src/OFDictionary.m +++ src/OFDictionary.m @@ -89,11 +89,12 @@ } return self; } -- initWithDictionary: (OFDictionary*)dictionary +- _initWithDictionary: (OFDictionary*)dictionary + copyKeys: (BOOL)copyKeys { self = [super init]; @try { uint32_t i; @@ -110,19 +111,21 @@ size = dictionary->size; count = dictionary->count; for (i = 0; i < size; i++) { - id key; + id key; struct of_dictionary_bucket *bucket; if (dictionary->data[i] == NULL || dictionary->data[i] == DELETED) continue; bucket = [self allocMemoryWithSize: sizeof(*bucket)]; - key = [dictionary->data[i]->key copy]; + key = (copyKeys + ? [dictionary->data[i]->key copy] + : [dictionary->data[i]->key retain]); @try { [dictionary->data[i]->object retain]; } @catch (id e) { [key release]; @@ -140,10 +143,16 @@ @throw e; } return self; } + +- initWithDictionary: (OFDictionary*)dictionary +{ + return [self _initWithDictionary: dictionary + copyKeys: YES]; +} - initWithObject: (id)object forKey: (id )key { self = [super init]; @@ -781,11 +790,11 @@ OFEnumerator *keyEnumerator, *objectEnumerator; id key, object; size_t i; if (count == 0) - return @"{}"; + return @"{()}"; ret = [OFMutableString stringWithString: @"{\n"]; pool = [[OFAutoreleasePool alloc] init]; keyEnumerator = [self keyEnumerator]; objectEnumerator = [self objectEnumerator]; Index: src/OFMutableDictionary.h ================================================================== --- src/OFMutableDictionary.h +++ src/OFMutableDictionary.h @@ -52,6 +52,12 @@ * * \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) +- (void)_setObject: (id)object + forKey: (id)key + copyKey: (BOOL)copyKey; +#endif @end Index: src/OFMutableDictionary.m ================================================================== --- src/OFMutableDictionary.m +++ src/OFMutableDictionary.m @@ -85,12 +85,13 @@ [self freeMemory: data]; data = newData; size = newSize; } -- (void)setObject: (id)object - forKey: (id )key +- (void)_setObject: (id)object + forKey: (id)key + copyKey: (BOOL)copyKey { uint32_t i, hash, last; id old; if (key == nil || object == nil) @@ -146,11 +147,11 @@ @throw [OFOutOfRangeException newWithClass: isa]; bucket = [self allocMemoryWithSize: sizeof(*bucket)]; @try { - key = [key copy]; + key = (copyKey ? [key copy] : [key retain]); } @catch (id e) { [self freeMemory: bucket]; @throw e; } @@ -173,10 +174,18 @@ old = data[i]->object; data[i]->object = [object retain]; [old release]; } + +- (void)setObject: (id)object + forKey: (id )key +{ + [self _setObject: object + forKey: key + copyKey: YES]; +} - (void)removeObjectForKey: (id )key { uint32_t i, hash, last; ADDED src/OFMutableSet.h Index: src/OFMutableSet.h ================================================================== --- src/OFMutableSet.h +++ src/OFMutableSet.h @@ -0,0 +1,36 @@ +/* + * 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" + +/** + * \brief An mutable unordered set of unique objects. + */ +@interface OFMutableSet: OFSet +/** + * \brief Adds the specified object to the set. + * + * \param object The object to add to the set + */ +- (void)addObject: (id)object; + +/** + * \brief Removes the specified object from the set. + * + * \param object The object to remove from the set + */ +- (void)removeObject: (id)object; +@end ADDED src/OFMutableSet.m Index: src/OFMutableSet.m ================================================================== --- src/OFMutableSet.m +++ src/OFMutableSet.m @@ -0,0 +1,37 @@ +/* + * 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_MUTABLE_SET_M + +#import "OFMutableSet.h" +#import "OFDictionary.h" +#import "OFNull.h" + +@implementation OFMutableSet +- (void)addObject: (id)object +{ + [dictionary _setObject: [OFNull null] + forKey: object + copyKey: NO]; +} + +- (void)removeObject: (id)object +{ + [dictionary removeObjectForKey: object]; +} +@end ADDED src/OFSet.h Index: src/OFSet.h ================================================================== --- src/OFSet.h +++ src/OFSet.h @@ -0,0 +1,116 @@ +/* + * 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 + +#import "OFObject.h" +#import "OFCollection.h" + +@class OFMutableDictionary; +@class OFArray; + +/** + * \brief An unordered set of unique objects. + */ +@interface OFSet: OFObject +{ + OFMutableDictionary *dictionary; +} + +/** + * \brief Returns a new, autoreleased set. + * + * \return A new, autoreleased set + */ ++ set; + +/** + * \brief Returns a new, autoreleased set with the specified set. + * + * \param set The set to initialize the set with + * \return A new, autoreleased set with the specified set + */ ++ setWithSet: (OFSet*)set; + +/** + * \brief Returns a new, autoreleased set with the specified array. + * + * \param array The array to initialize the set with + * \return A new, autoreleased set with the specified array + */ ++ setWithArray: (OFArray*)array; + +/** + * \brief Returns a new, autoreleased set with the specified objects. + * + * \param firstObject The first object for the set + * \return A new, autoreleased set with the specified objects + */ ++ setWithObjects: (id)firstObject, ...; + +/** + * \brief Initializes an already allocated set with the specified set. + * + * \param set The set to initialize the set with + * \return An initialized set with the specified set + */ +- initWithSet: (OFSet*)set; + +/** + * \brief Initializes an already allocated set with the specified array. + * + * \param array The array to initialize the set with + * \return An initialized set with the specified array + */ +- initWithArray: (OFArray*)array; + +/** + * \brief Initializes an already allocated set with the specified objects. + * + * \param firstObject The first object for the set + * \return An initialized set with the specified objects + */ +- initWithObjects: (id)firstObject, ...; + +/** + * \brief Initializes an already allocated set with the specified object and + * va_list. + * + * \param firstObject The first object for the set + * \param arguments A va_list with the other objects + * \return An initialized set with the specified object and va_list + */ +- initWithObject: (id)firstObject + arguments: (va_list)arguments; + +/** + * \brief Returns whether the receiver is a subset of the specified set. + * + * \return Whether the receiver is a subset of the specified set + */ +- (BOOL)isSubsetOfSet: (OFSet*)set; + +/** + * \brief Returns whether the receiver and the specified set have at least one + * object in common. + * + * \return Whether the receiver and the specified set have at least one object + * in common + */ +- (BOOL)intersectsSet: (OFSet*)set; +@end + +#import "OFMutableSet.h" ADDED src/OFSet.m Index: src/OFSet.m ================================================================== --- src/OFSet.m +++ src/OFSet.m @@ -0,0 +1,271 @@ +/* + * 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_SET_M + +#import "OFSet.h" +#import "OFDictionary.h" +#import "OFArray.h" +#import "OFNull.h" +#import "OFString.h" +#import "OFAutoreleasePool.h" + +@implementation OFSet ++ set +{ + return [[[self alloc] init] autorelease]; +} + ++ setWithSet: (OFSet*)set +{ + return [[[self alloc] initWithSet: set] autorelease]; +} + ++ setWithArray: (OFArray*)array +{ + return [[[self alloc] initWithArray: array] autorelease]; +} + ++ setWithObjects: (id)firstObject, ... +{ + id ret; + va_list arguments; + + va_start(arguments, firstObject); + ret = [[[self alloc] initWithObject: firstObject + arguments: arguments] autorelease]; + va_end(arguments); + + return ret; +} + +- init +{ + self = [super init]; + + @try { + dictionary = [[OFMutableDictionary alloc] init]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- initWithSet: (OFSet*)set +{ + self = [super init]; + + @try { + dictionary = [[OFMutableDictionary alloc] + _initWithDictionary: set->dictionary + copyKeys: NO]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- initWithArray: (OFArray*)array +{ + self = [self init]; + + @try { + id *cArray = [array cArray]; + size_t i, count = [array count]; + OFNull *null = [OFNull null]; + + for (i = 0; i < count; i++) + [dictionary _setObject: null + forKey: cArray[i] + copyKey: NO]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (id)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 +{ + self = [self init]; + + @try { + OFNull *null = [OFNull null]; + id object; + + [dictionary _setObject: null + forKey: firstObject + copyKey: NO]; + + while ((object = va_arg(arguments, id)) != nil) + [dictionary _setObject: null + forKey: object + copyKey: NO]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [dictionary release]; + + [super dealloc]; +} + +- (BOOL)isEqual: (id)object +{ + OFSet *otherSet; + + if (![object isKindOfClass: [OFSet class]]) + return NO; + + otherSet = object; + + return [otherSet->dictionary isEqual: dictionary]; +} + +- (uint32_t)hash +{ + return [dictionary hash]; +} + +- (OFString*)description +{ + OFMutableString *ret; + OFAutoreleasePool *pool, *pool2; + OFEnumerator *enumerator; + size_t i, count = [dictionary count]; + id object; + + if (count == 0) + return @"{()}"; + + ret = [OFMutableString stringWithString: @"{(\n"]; + pool = [[OFAutoreleasePool alloc] init]; + enumerator = [dictionary keyEnumerator]; + + i = 0; + pool2 = [[OFAutoreleasePool alloc] init]; + + while ((object = [enumerator nextObject]) != nil) { + [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 [[OFSet alloc] initWithSet: self]; +} + +- mutableCopy +{ + return [[OFMutableSet alloc] initWithSet: self]; +} + +- (size_t)count +{ + return [dictionary count]; +} + +- (BOOL)containsObject: (id)object +{ + return ([dictionary objectForKey: object] != nil); +} + +- (BOOL)isSubsetOfSet: (OFSet*)set +{ + OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; + OFEnumerator *enumerator = [dictionary keyEnumerator]; + id object; + + while ((object = [enumerator nextObject]) != nil) { + if (![set containsObject: object]) { + [pool release]; + return NO; + } + } + + [pool release]; + + return YES; +} + +- (BOOL)intersectsSet: (OFSet*)set +{ + OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; + OFEnumerator *enumerator = [dictionary keyEnumerator]; + id object; + + while ((object = [enumerator nextObject]) != nil) { + if ([set containsObject: object]) { + [pool release]; + return YES; + } + } + + [pool release]; + + return NO; +} + +- (OFEnumerator*)objectEnumerator +{ + return [dictionary keyEnumerator]; +} +@end Index: tests/Makefile ================================================================== --- tests/Makefile +++ tests/Makefile @@ -13,10 +13,11 @@ OFMD5HashTests.m \ OFNumberTests.m \ OFObjectTests.m \ ${OFPLUGINTESTS_M} \ OFSerializationTests.m \ + OFSet.m \ OFSHA1HashTests.m \ OFStreamTests.m \ OFStringTests.m \ OFTCPSocketTests.m \ ${OFTHREADTESTS_M} \ ADDED tests/OFSet.m Index: tests/OFSet.m ================================================================== --- tests/OFSet.m +++ tests/OFSet.m @@ -0,0 +1,76 @@ +/* + * 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" + +#import "OFSet.h" +#import "OFAutoreleasePool.h" +#import "OFArray.h" + +#import "TestsAppDelegate.h" + +static OFString *module = @"OFSet"; + +@implementation TestsAppDelegate (OFSetTests) +- (void)setTests +{ + OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; + OFSet *set1, *set2; + OFMutableSet *mutableSet; + + TEST(@"+[setWithArray:]", + (set1 = [OFSet setWithArray: [OFArray arrayWithObjects: @"foo", + @"bar", @"baz", @"foo", @"x", nil]])) + + TEST(@"+[setWithObjects:]", + (set2 = [OFSet setWithObjects: @"foo", @"bar", @"baz", @"bar", @"x", + nil])) + + TEST(@"-[isEqual:]", [set1 isEqual: set2]) + + TEST(@"-[hash]", [set1 hash] == [set2 hash]) + + TEST(@"-[description]", + [[set1 description] + isEqual: @"{(\n\tbar,\n\tbaz,\n\tfoo,\n\tx\n)}"] && + [[set1 description] isEqual: [set2 description]]) + + TEST(@"-[copy]", [set1 isEqual: [[set1 copy] autorelease]]) + + TEST(@"-[mutableCopy]", + (mutableSet = [[set1 mutableCopy] autorelease])); + + TEST(@"-[addObject:]", + R([mutableSet addObject: @"baz"]) && [mutableSet isEqual: set2] && + R([mutableSet addObject: @"y"]) && [mutableSet isEqual: + ([OFSet setWithObjects: @"foo", @"bar", @"baz", @"x", @"y", nil])]) + + TEST(@"-[removeObject:]", + R([mutableSet removeObject: @"y"]) && [mutableSet isEqual: set1]) + + TEST(@"-[isSubsetOfSet:]", + R([mutableSet removeObject: @"foo"]) && + [mutableSet isSubsetOfSet: set1] && + ![set1 isSubsetOfSet: mutableSet]); + + TEST(@"-[intersectsSet:]", + [(set2 = [OFSet setWithObjects: @"x", nil]) intersectsSet: set1] && + [set1 intersectsSet: set2] && + ![([OFSet setWithObjects: @"1", nil]) intersectsSet: set1]); + + [pool drain]; +} +@end Index: tests/TestsAppDelegate.h ================================================================== --- tests/TestsAppDelegate.h +++ tests/TestsAppDelegate.h @@ -122,10 +122,14 @@ @end @interface TestsAppDelegate (SerializationTests) - (void)serializationTests; @end + +@interface TestsAppDelegate (OFSetTests) +- (void)setTests; +@end @interface TestsAppDelegate (OFSHA1HashTests) - (void)SHA1HashTests; @end Index: tests/TestsAppDelegate.m ================================================================== --- tests/TestsAppDelegate.m +++ tests/TestsAppDelegate.m @@ -118,17 +118,18 @@ [self objectTests]; #ifdef OF_HAVE_BLOCKS [self blockTests]; #endif - [self stringTests]; [self MD5HashTests]; [self SHA1HashTests]; + [self stringTests]; [self dataArrayTests]; [self arrayTests]; [self dictionaryTests]; [self listTests]; + [self setTests]; [self dateTests]; [self numberTests]; [self streamTests]; [self TCPSocketTests]; #ifdef OF_THREADS