ObjFW  Artifact [3ddd956ea4]

Artifact 3ddd956ea435e735fa64ded6c57296b234e0bb393419f2d7e081b8afa79b7ae3:

  • File src/OFDictionary.m — part of check-in [bbf1f79b8f] at 2009-09-08 16:06:10 on branch trunk — New OFDictionary implementation and removal of a hack in OFList.

    The new implementation is easier to use as it does automatic resizing,
    but therefore it's not realtime-capable anymore. The new implementation
    should also be a little bit faster.

    I decided to change the implementation as only very few need a
    realtime-capable dictionary and those few will most likely write their
    own implementation for their specific case anyway.

    As the new implementation no longer uses OFList, this also made it
    possible to remove a hack from OFList. (user: js, size: 5389) [annotate] [blame] [check-ins using]


/*
 * 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 "OFAutoreleasePool.h"
#import "OFExceptions.h"

#define BUCKET_SIZE sizeof(struct of_dictionary_bucket)

/* References for static linking */
void _references_to_categories_of_OFDictionary()
{
	_OFIterator_reference = 1;
}

@implementation OFDictionary
+ dictionary;
{
	return [[[self alloc] init] autorelease];
}

+ dictionaryWithDictionary: (OFDictionary*)dict
{
	return [[[self alloc] initWithDictionary: dict] autorelease];
}

+ dictionaryWithObject: (OFObject*)obj
		forKey: (OFObject <OFCopying>*)key
{
	return [[[self alloc] initWithObject: obj
				      forKey: key] autorelease];
}

+ dictionaryWithObjects: (OFArray*)objs
		forKeys: (OFArray*)keys
{
	return [[[self alloc] initWithObjects: objs
				      forKeys: keys] autorelease];
}

+ dictionaryWithKeysAndObjects: (OFObject <OFCopying>*)first, ...
{
	id ret;
	va_list args;

	va_start(args, first);
	ret = [[[self alloc] initWithKey: first
				 argList: args] autorelease];
	va_end(args);

	return ret;
}

- init
{
	self = [super init];

	size = 1;

	@try {
		data = [self allocMemoryWithSize: BUCKET_SIZE];
	} @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;
	}
	data[0].key = nil;

	return self;
}

- initWithDictionary: (OFDictionary*)dict
{
	size_t i;

	self = [super init];

	if (dict == nil) {
		Class c = isa;
		size = 0;
		[self dealloc];
		@throw [OFInvalidArgumentException newWithClass: c
						       selector: _cmd];
	}

	@try {
		data = [self allocMemoryForNItems: dict->size
					 withSize: BUCKET_SIZE];
	} @catch (OFException *e) {
		/*
		 * We can't use [super dealloc] on OS X here. Compiler bug?
		 * Anyway, we didn't do anything yet anyway, so [self dealloc]
		 * works.
		 */
		[self dealloc];
		@throw e;
	}
	size = dict->size;

	for (i = 0; i < size; i++) {
		if (dict->data[i].key != nil) {
			data[i].key = [dict->data[i].key copy];
			data[i].object = [dict->data[i].object retain];
			data[i].hash = dict->data[i].hash;
		} else
			data[i].key = nil;
	}

	return self;
}

- initWithObject: (OFObject*)obj
	  forKey: (OFObject <OFCopying>*)key
{
	const SEL sel = @selector(setObject:forKey:);
	IMP set = [OFMutableDictionary instanceMethodForSelector: sel];

	self = [self init];

	@try {
		set(self, sel, obj, key);
	} @catch (OFException *e) {
		[self dealloc];
		@throw e;
	}

	return self;
}

- initWithObjects: (OFArray*)objs
	  forKeys: (OFArray*)keys
{
	id *objs_data, *keys_data;
	size_t objs_count, i;
	const SEL sel = @selector(setObject:forKey:);
	IMP set = [OFMutableDictionary instanceMethodForSelector: sel];

	self = [self init];

	objs_data = [objs data];
	keys_data = [keys data];
	objs_count = [objs count];

	if (objs == nil || keys == nil || objs_count != [keys count]) {
		Class c = isa;
		[self dealloc];
		@throw [OFInvalidArgumentException newWithClass: c
						       selector: _cmd];
	}

	@try {
		for (i = 0; i < objs_count; i++)
			set(self, sel, objs_data[i], keys_data[i]);
	} @catch (OFException *e) {
		[self dealloc];
		@throw e;
	}

	return self;
}

- initWithKeysAndObjects: (OFObject <OFCopying>*)first, ...
{
	id ret;
	va_list args;

	va_start(args, first);
	ret = [self initWithKey: first
			argList: args];
	va_end(args);

	return ret;
}

- initWithKey: (OFObject <OFCopying>*)key
      argList: (va_list)args
{
	const SEL sel = @selector(setObject:forKey:);
	IMP set = [OFMutableDictionary instanceMethodForSelector: sel];

	self = [self init];

	@try {
		set(self, sel, va_arg(args, OFObject*), key);
		while ((key = va_arg(args, OFObject <OFCopying>*)) != nil)
			set(self, sel, va_arg(args, OFObject*), key);
	} @catch (OFException *e) {
		[self dealloc];
		@throw e;
	}

	return self;
}

- (id)objectForKey: (OFObject <OFCopying>*)key
{
	uint32_t i, hash;

	if (key == nil)
		@throw [OFInvalidArgumentException newWithClass: isa
						       selector: _cmd];

	hash = [key hash];

	for (i = hash & (size - 1); i < size && data[i].key != nil &&
	    ![data[i].key isEqual: key]; i++);

	/* In case the last bucket is already used */
	if (i >= size)
		for (i = 0; i < size && data[i].key != nil &&
		    ![data[i].key isEqual: key]; i++);

	/* Key not in dictionary */
	if (i >= size || data[i].key == nil)
		return nil;

	return data[i].object;
}

- (size_t)count
{
	return count;
}

- (id)copy
{
	return [self retain];
}

- (id)mutableCopy
{
	return [[OFMutableDictionary alloc] initWithDictionary: self];
}

/* FIXME: Implement this!
- (BOOL)isEqual
{
}
*/

- (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];
}

- setObject: (OFObject*)obj
     forKey: (OFObject <OFCopying>*)key
{
	@throw [OFNotImplementedException newWithClass: isa
					      selector: _cmd];
}

- removeObjectForKey: (OFObject*)key
{
	@throw [OFNotImplementedException newWithClass: isa
					      selector: _cmd];
}
@end