/* * Copyright (c) 2008 - 2009 * Jonathan Schleifer <js@webkeks.org> * * All rights reserved. * * This file is part of libobjfw. It may be distributed under the terms of the * Q Public License 1.0, which can be found in the file LICENSE included in * the packaging of this file. */ #include "config.h" #include <string.h> #import "OFDictionary.h" #import "OFIterator.h" #import "OFExceptions.h" /* Reference for static linking */ void _reference_to_OFIterator_in_OFDictionary() { _OFIterator_reference = 1; } @implementation OFDictionary + dictionary; { return [[[self alloc] init] autorelease]; } + dictionaryWithHashSize: (int)hashsize { return [[[self alloc] initWithHashSize: hashsize] autorelease]; } + dictionaryWithKey: (OFObject <OFCopying>*)key andObject: (OFObject*)obj { return [[[self alloc] initWithKey: key andObject: obj] autorelease]; } + dictionaryWithKeys: (OFArray*)keys andObjects: (OFArray*)objs { return [[[self alloc] initWithKeys: keys andObjects: objs] autorelease]; } + dictionaryWithKeysAndObjects: (OFObject <OFCopying>*)first, ... { id ret; va_list args; va_start(args, first); ret = [[[self alloc] initWithKey: first andArgList: args] autorelease]; va_end(args); return ret; } - init { self = [super init]; size = 4096; @try { data = [self allocMemoryForNItems: size withSize: sizeof(OFList*)]; } @catch (OFException *e) { /* * We can't use [super dealloc] on OS X here. Compiler bug? * Anyway, set size to 0 so that [self dealloc] works. */ size = 0; [self dealloc]; @throw e; } memset(data, 0, size * sizeof(OFList*)); return self; } - initWithHashSize: (int)hashsize { self = [super init]; if (hashsize < 8 || hashsize >= 28) { Class c = isa; [super dealloc]; @throw [OFInvalidArgumentException newWithClass: c andSelector: _cmd]; } size = (size_t)1 << hashsize; @try { data = [self allocMemoryForNItems: size withSize: sizeof(OFList*)]; } @catch (OFException *e) { /* * We can't use [super dealloc] on OS X here. Compiler bug? * Anyway, set size to 0 so that [self dealloc] works. */ size = 0; [self dealloc]; @throw e; } memset(data, 0, size * sizeof(OFList*)); return self; } - initWithKey: (OFObject <OFCopying>*)key andObject: (OFObject*)obj { Class c; uint32_t hash; self = [self init]; if (key == nil || obj == nil) { c = isa; [self dealloc]; @throw [OFInvalidArgumentException newWithClass: isa andSelector: _cmd]; } hash = [key hash] & (size - 1); @try { key = [key copy]; } @catch (OFException *e) { [self dealloc]; @throw e; } @try { data[hash] = [[OFList alloc] init]; [data[hash] append: key]; [data[hash] append: obj]; } @catch (OFException *e) { [self dealloc]; @throw e; } @finally { [key release]; } return self; } - initWithKeys: (OFArray*)keys andObjects: (OFArray*)objs { Class c; OFObject <OFCopying> **keys_data; OFObject **objs_data; size_t count, i; self = [self init]; count = [keys count]; if (keys == nil || objs == nil || count != [objs count]) { c = isa; [self dealloc]; @throw [OFInvalidArgumentException newWithClass: isa andSelector: _cmd]; } keys_data = [keys data]; objs_data = [objs data]; for (i = 0; i < count; i++) { uint32_t hash; OFObject <OFCopying> *key; if (keys_data[i] == nil || objs_data[i] == nil) { c = isa; [self dealloc]; @throw [OFInvalidArgumentException newWithClass: isa andSelector: _cmd]; } hash = [keys_data[i] hash] & (size - 1); @try { key = [keys_data[i] copy]; } @catch (OFException *e) { [self dealloc]; @throw e; } @try { if (data[hash] == nil) data[hash] = [[OFList alloc] init]; [data[hash] append: key]; [data[hash] append: objs_data[i]]; } @catch (OFException *e) { [self dealloc]; @throw e; } @finally { [key release]; } } return self; } - initWithKeysAndObjects: (OFObject <OFCopying>*)first, ... { id ret; va_list args; va_start(args, first); ret = [self initWithKey: first andArgList: args]; va_end(args); return ret; } - initWithKey: (OFObject <OFCopying>*)first andArgList: (va_list)args { OFObject <OFCopying> *key; OFObject *obj; Class c; uint32_t hash; self = [self init]; obj = va_arg(args, OFObject*); if (first == nil || obj == nil) { c = isa; [self dealloc]; @throw [OFInvalidArgumentException newWithClass: isa andSelector: _cmd]; } hash = [first hash] & (size - 1); @try { key = [first copy]; } @catch (OFException *e) { [self dealloc]; @throw e; } @try { if (data[hash] == nil) data[hash] = [[OFList alloc] init]; [data[hash] append: key]; [data[hash] append: obj]; } @catch (OFException *e) { [self dealloc]; @throw e; } @finally { [key release]; } while ((key = va_arg(args, OFObject <OFCopying>*)) != nil) { if ((obj = va_arg(args, OFObject*)) == nil) { c = isa; [self dealloc]; @throw [OFInvalidArgumentException newWithClass: isa andSelector: _cmd]; } hash = [key hash] & (size - 1); @try { key = [key copy]; } @catch (OFException *e) { [self dealloc]; @throw e; } @try { if (data[hash] == nil) data[hash] = [[OFList alloc] init]; [data[hash] append: key]; [data[hash] append: obj]; } @catch (OFException *e) { [self dealloc]; @throw e; } @finally { [key release]; } } return self; } - (float)averageItemsPerBucket { size_t items, buckets, i; items = 0; buckets = 0; for (i = 0; i < size; i++) { if (data[i] != nil) { items += [data[i] count] / 2; buckets++; } } return (float)items / buckets; } - (id)objectForKey: (OFObject*)key { uint32_t hash; of_list_object_t *iter; if (key == nil) @throw [OFInvalidArgumentException newWithClass: isa andSelector: _cmd]; hash = [key hash] & (size - 1); if (data[hash] == nil) return nil; for (iter = [data[hash] first]; iter != NULL; iter = iter->next->next) if ([iter->object isEqual: key]) return iter->next->object; return nil; } - setObject: (OFObject*)obj forKey: (OFObject <OFCopying>*)key { @throw [OFNotImplementedException newWithClass: isa andSelector: _cmd]; } - removeObjectForKey: (OFObject*)key { @throw [OFNotImplementedException newWithClass: isa andSelector: _cmd]; } - changeHashSize: (int)hashsize { @throw [OFNotImplementedException newWithClass: isa andSelector: _cmd]; } /* FIXME: Implement this! */ /* - (BOOL)isEqual { } - (id)copy { } - (id)mutableCopy { } */ - (void)dealloc { size_t i; for (i = 0; i < size; i++) if (data[i] != nil) [data[i] release]; [super dealloc]; } @end