/* 
   NSHashMap.m

   Copyright (C) 1995, 1996 Ovidiu Predescu and Mircea Oancea.
   All rights reserved.

   Author: Ovidiu Predescu <ovidiu@bx.logicnet.ro>
	   Mircea Oancea <mircea@jupiter.elcom.pub.ro>

   This file is part of the mySTEP Library and is provided under the 
   terms of the libFoundation BSD type license (See the Readme file).
*/

#import <Foundation/NSHashTable.h>
#import <Foundation/NSMapTable.h>
#import <Foundation/NSString.h>
#import <Foundation/NSException.h>
#import <Foundation/NSArray.h>

static void __NSHashGrow(NSHashTable *table, unsigned newSize);
static void __NSMapGrow(NSMapTable *table, unsigned newSize);

// FIXME: speed up by building a cache for smaller values
// e.g. char isprime[2048] - 0=unknown 1=no 2=yes

// #define is_prime(n) (n>=sizeof(isprime)?_is_prime(n):(isprime[n] == 0?(isprime[n]=_is_prime(n)+1)-1:isprime[n]-1))

static BOOL													// Hash and Map table utilities
is_prime(unsigned n)
{
	unsigned divisor, maxDivisor, n2;
#if 0
	fprintf(stderr, "is_prime(%u)?\n", n);	
#endif
	if((n & 1) == 0)
		return NO;	// even number
    for(maxDivisor = 2, n2 = 4; n2 <= n; maxDivisor <<= 1, n2 <<= 2)
		; // ceil(sqrt(n))
#if 0
	fprintf(stderr, "n=%u maxdivisor=%u n2=%u\n", n, maxDivisor, n2);
#endif
	for(divisor = 3; divisor <= maxDivisor; divisor+=2)
        if(n % divisor == 0)
            return NO;
#if 0
	fprintf(stderr, "is_prime(%u)!\n", n);	
#endif
    return YES;
}

static unsigned 
nextPrime(unsigned old_value)
{
	unsigned i, new_value = old_value | 1;	// make odd
    for(i = new_value; i >= new_value; i += 2)
        if(is_prime(i))
            return i;
	return old_value;
}

static void 								
__NSCheckHashTableFull(NSHashTable* table)					// Check if node 
{															// table is full.
    if( ++(table->itemsCount) >= ((table->hashSize * 3) / 4))
		{
		unsigned newSize = nextPrime((table->hashSize * 4) / 3);
		if(newSize != table->hashSize)
			__NSHashGrow(table, newSize);
		}
}

static void 
__NSCheckMapTableFull(NSMapTable* table)
{
    if( ++(table->itemsCount) >= ((table->hashSize * 3) / 4))
		{
		unsigned newSize = nextPrime((table->hashSize * 4) / 3);
		if(newSize != table->hashSize)
			__NSMapGrow(table, newSize);
		}
}

//*****************************************************************************
//
// 		NSHashTable functions 
//
//*****************************************************************************

NSHashTable *
NSCreateHashTable(NSHashTableCallBacks callBacks, unsigned capacity)
{
	NSHashTable *table = objc_malloc(sizeof(NSHashTable));

    capacity = capacity ? capacity : 13;
    if (!is_prime(capacity))
		capacity = nextPrime(capacity);

    table->hashSize = capacity;
    table->nodes = objc_calloc(table->hashSize, sizeof(void*));
    table->itemsCount = 0;
    table->callbacks = callBacks;
    if (table->callbacks.hash == NULL)
	table->callbacks.hash = 
	    (unsigned(*)(NSHashTable*, const void*))__NSHashPointer;
    if (table->callbacks.isEqual == NULL)
	table->callbacks.isEqual = 
	(BOOL(*)(NSHashTable*, const void*, const void*)) __NSComparePointers;
    if (table->callbacks.retain == NULL)
	table->callbacks.retain = 
	    (void(*)(NSHashTable*, const void*))__NSRetainNothing;
    if (table->callbacks.release == NULL)
	table->callbacks.release = 
	    (void(*)(NSHashTable*, void*))__NSReleaseNothing;
    if (table->callbacks.describe == NULL)
	table->callbacks.describe = 
	    (NSString*(*)(NSHashTable*, const void*))__NSDescribePointers;
    return table;
}

NSHashTable *
NSCopyHashTable(NSHashTable *table)
{
NSHashTable *new;
struct _NSHashNode *oldnode, *newnode;
unsigned i;
    
    new = objc_malloc(sizeof(NSHashTable));
    new->hashSize = table->hashSize;
    new->itemsCount = table->itemsCount;
    new->callbacks = table->callbacks;
    new->nodes = objc_calloc(new->hashSize, sizeof(void*));
    
    for (i = 0; i < new->hashSize; i++) 
		{
		for (oldnode = table->nodes[i]; oldnode; oldnode = oldnode->next) 
			{
			newnode = objc_malloc(sizeof(struct _NSHashNode));
			newnode->key = oldnode->key;
			newnode->next = new->nodes[i];
			new->nodes[i] = newnode;
			table->callbacks.retain(new, oldnode->key);
		}	}
    
    return new;
}

void 
NSFreeHashTable(NSHashTable *table)
{																// Free a Table
    NSResetHashTable(table);
    objc_free(table->nodes);
    objc_free(table);
}

void 
NSResetHashTable(NSHashTable *table)
{
unsigned i;

    for(i = 0; i < table->hashSize; i++) 
		{
		struct _NSHashNode *next, *node;
		
		node = table->nodes[i];
		table->nodes[i] = NULL;		
		while (node) 
			{
			table->callbacks.release(table, node->key);
			next = node->next;
			objc_free(node);
			node = next;
		}	}

    table->itemsCount = 0;
}

BOOL 
NSCompareHashTables(NSHashTable *table1, NSHashTable *table2)
{
unsigned i;												// Compare Two Tables
struct _NSHashNode *node1;
    
    if (table1->hashSize != table2->hashSize)
		return NO;
    for (i = 0; i < table1->hashSize; i++)
		{ 
		for (node1 = table1->nodes[i]; node1; node1 = node1->next) 
	    	if (NSHashGet(table2, node1->key) == NULL)
				return NO;
		}

    return YES;;
}	

unsigned 											// return Number of Items
NSCountHashTable(NSHashTable *table)			{ return table->itemsCount;	}

void *
NSHashGet(NSHashTable *table, const void *pointer)
{															// Retrieve Items
struct _NSHashNode *node;

	node =table->nodes[table->callbacks.hash(table,pointer) % table->hashSize];
    for(; node; node = node->next)
        if(table->callbacks.isEqual(table, pointer, node->key))
            return node->key;

    return NULL;
}

NSArray *NSAllHashTableObjects(NSHashTable *table)
{
	if(table)
		{
		id array = [NSMutableArray arrayWithCapacity:table->itemsCount];
		struct _NSHashNode *node;
		unsigned i;
		for(i = 0; i < table->hashSize; i++)
			for(node = table->nodes[i]; node; node = node->next)
				[array addObject:(NSObject*)(node->key)];	// this will retain
		return array;
		}
	else
		return [NSArray array];
}

NSHashEnumerator 
NSEnumerateHashTable(NSHashTable *table)
{
NSHashEnumerator en;

    en.table = table;
    en.node = NULL;
    en.bucket = -1;

    return en;
}

void *
NSNextHashEnumeratorItem(NSHashEnumerator *en)
{
    if(en->node)
		en->node = en->node->next;
    if(en->node == NULL) {
	for(en->bucket++; ((unsigned)en->bucket)<en->table->hashSize; en->bucket++)
	    if (en->table->nodes[en->bucket]) {
		    en->node = en->table->nodes[en->bucket];
		    break;
	    };
	if (((unsigned)en->bucket) >= en->table->hashSize) {
	    en->node = NULL;
	    en->bucket = en->table->hashSize-1;
	    return NULL;
	}
    }
    return en->node->key;
}

static void 
__NSHashGrow(NSHashTable *table, unsigned newSize)		// Add / Remove an Item
{
unsigned i;
struct _NSHashNode** newNodeTable =objc_calloc(newSize,sizeof(struct _NSHashNode*));
    
    for(i = 0; i < table->hashSize; i++) 
		{
		struct _NSHashNode *next, *node;
		unsigned int h;

		node = table->nodes[i];
		while(node) 
			{
			next = node->next;
			h = table->callbacks.hash(table, node->key) % newSize;
			node->next = newNodeTable[h];
			newNodeTable[h] = node;
			node = next;
		}	}

    objc_free(table->nodes);
	table->nodes = newNodeTable;
    table->hashSize = newSize;
}

void 
NSHashInsert(NSHashTable *table, const void *pointer)
{
	unsigned int h;
	struct _NSHashNode *node;

    if (pointer == nil)
		[NSException raise: NSInvalidArgumentException
        			 format: @"Nil object to be added in NSHashTable."];

    h = table->callbacks.hash(table, pointer) % table->hashSize;
    for(node = table->nodes[h]; node; node = node->next)
        if(table->callbacks.isEqual(table, pointer, node->key))
            break;

    /* Check if an entry for key exist in nodeTable. */
    if(node) {
        /* key exist. Set for it new value and return the old value of it. */
	if (pointer != node->key) {
	    table->callbacks.retain(table, pointer);
	    table->callbacks.release(table, node->key);
	}
	node->key = (void*)pointer;
        return;
    }
				// key not found. Allocate a new bucket and initialize it.
    node = objc_malloc(sizeof(struct _NSHashNode));
	table->callbacks.retain(table, pointer);
    node->key = (void*)pointer;
    node->next = table->nodes[h];
    table->nodes[h] = node;

    __NSCheckHashTableFull(table);
}

void 
NSHashInsertKnownAbsent(NSHashTable *table, const void *pointer)
{
unsigned int h;
struct _NSHashNode *node;

    if (pointer == nil)
		[NSException raise: NSInvalidArgumentException
        			 format: @"Nil object to be added in NSHashTable."];

    h = table->callbacks.hash(table, pointer) % table->hashSize;
    for(node = table->nodes[h]; node; node = node->next)
        if(table->callbacks.isEqual(table, pointer, node->key))
            break;

    /* Check if an entry for key exist in nodeTable. */
    if(node) 
		[NSException raise: NSInvalidArgumentException
        			 format: @"Nil object already existing in NSHashTable."];

					// key not found. Allocate a new bucket and initialize it.
    node = objc_malloc(sizeof(struct _NSHashNode));
	table->callbacks.retain(table, pointer);
    node->key = (void*)pointer;
    node->next = table->nodes[h];
    table->nodes[h] = node;

    __NSCheckHashTableFull(table);
}

void *
NSHashInsertIfAbsent(NSHashTable *table, const void *pointer)
{
unsigned int h;
struct _NSHashNode *node;

    if (pointer == nil)
		[NSException raise: NSInvalidArgumentException
        			 format: @"Nil object to be added in NSHashTable."];

    h = table->callbacks.hash(table, pointer) % table->hashSize;
    for(node = table->nodes[h]; node; node = node->next)
        if(table->callbacks.isEqual(table, pointer, node->key))
            break;

    if(node)				// Check if an entry for key exist in nodeTable.
		return node->key;
	
					// key not found. Allocate a new bucket and initialize it.
    node = objc_malloc(sizeof(struct _NSHashNode));
    table->callbacks.retain(table, pointer);
    node->key = (void*)pointer;
    node->next = table->nodes[h];
    table->nodes[h] = node;

    __NSCheckHashTableFull(table);
    
    return NULL;
}

void 
NSHashRemove(NSHashTable *table, const void *pointer)
{
unsigned int h;
struct _NSHashNode *node, *node1 = NULL;

    if (pointer == nil)
	    return;

    h = table->callbacks.hash(table, pointer) % table->hashSize;

    			// node point to current bucket, and node1 to previous bucket 
				// or to NULL if current node is the first node in the list 
    for(node = table->nodes[h]; node; node1 = node, node = node->next)
        if(table->callbacks.isEqual(table, pointer, node->key)) {
	    table->callbacks.release(table, node->key);
            if(!node1)
                table->nodes[h] = node->next;
            else
                node1->next = node->next;
	    objc_free(node);
	    (table->itemsCount)--;
	    return;
        }
}

NSString *
NSStringFromHashTable(NSHashTable *table)
{												// Get a String Representation
id ret = [NSMutableString new];
unsigned i;
struct _NSHashNode *node;
    
    for (i = 0; i < table->hashSize; i++)
		for (node = table->nodes[i]; node; node = node->next) 
			{
	    	[ret appendString:table->callbacks.describe(table, node->key)];
	    	[ret appendString:@" "];
			}
    
    return ret;
}

//*****************************************************************************
//
// 		Map Table Functions 
//
//*****************************************************************************

NSMapTable *
NSCreateMapTable(NSMapTableKeyCallBacks keyCallbacks, 
				 NSMapTableValueCallBacks valueCallbacks, 
				 unsigned capacity)
{
NSMapTable *table = objc_malloc(sizeof(NSMapTable));
    
    capacity = capacity ? capacity : 13;
    if (!is_prime(capacity))
		capacity = nextPrime(capacity);

    table->hashSize = capacity;
    table->nodes = objc_calloc(table->hashSize, sizeof(void*));
    table->itemsCount = 0;
    table->keyCallbacks = keyCallbacks;
    table->valueCallbacks = valueCallbacks;
    if (table->keyCallbacks.hash == NULL)
	table->keyCallbacks.hash = 
		(unsigned(*)(NSMapTable*, const void*))__NSHashPointer;
    if (table->keyCallbacks.isEqual == NULL)
	table->keyCallbacks.isEqual = 
	(BOOL(*)(NSMapTable*, const void*, const void*)) __NSComparePointers;
    if (table->keyCallbacks.retain == NULL)
	table->keyCallbacks.retain = 
	    (void(*)(NSMapTable*, const void*))__NSRetainNothing;
    if (table->keyCallbacks.release == NULL)
	table->keyCallbacks.release = 
	    (void(*)(NSMapTable*, void*))__NSReleaseNothing;
    if (table->keyCallbacks.describe == NULL)
	table->keyCallbacks.describe = 
	    (NSString*(*)(NSMapTable*, const void*))__NSDescribePointers;
    if (table->valueCallbacks.retain == NULL)
	table->valueCallbacks.retain = 
	    (void(*)(NSMapTable*, const void*))__NSRetainNothing;
    if (table->valueCallbacks.release == NULL)
	table->valueCallbacks.release = 
	    (void(*)(NSMapTable*, void*))__NSReleaseNothing;
    if (table->valueCallbacks.describe == NULL)
	table->valueCallbacks.describe = 
	    (NSString*(*)(NSMapTable*, const void*))__NSDescribePointers;
    return table;
}

NSMapTable *
NSCopyMapTable(NSMapTable *table)
{
NSMapTable *new;
struct _NSMapNode *oldnode, *newnode;
unsigned i;
    
    new = objc_malloc(sizeof(NSMapTable));
    new->hashSize = table->hashSize;
    new->itemsCount = table->itemsCount;
    new->keyCallbacks = table->keyCallbacks;
    new->valueCallbacks = table->valueCallbacks;
    new->nodes = objc_calloc(new->hashSize, sizeof(void*));
    
    for (i = 0; i < new->hashSize; i++) 
		{
		for (oldnode = table->nodes[i]; oldnode; oldnode = oldnode->next) 
			{
			newnode = objc_malloc(sizeof(struct _NSMapNode));
			newnode->key = oldnode->key;
			newnode->value = oldnode->value;
			newnode->next = new->nodes[i];
			new->nodes[i] = newnode;
			table->keyCallbacks.retain(new, oldnode->key);
			table->valueCallbacks.retain(new, oldnode->value);
		}	}
    
    return new;
}

void 
NSFreeMapTable(NSMapTable *table)
{																// Free a Table
    NSResetMapTable(table);
    objc_free(table->nodes);
    objc_free(table);
}

void 
NSResetMapTable(NSMapTable *table)
{
unsigned i;

    for(i = 0; i < table->hashSize; i++) 
		{
		struct _NSMapNode *next, *node;
	
		node = table->nodes[i];
		table->nodes[i] = NULL;		
		while (node) 
			{
			table->keyCallbacks.release(table, node->key);
			table->valueCallbacks.release(table, node->value);
			next = node->next;
			objc_free(node);
			node = next;
		}	}

    table->itemsCount = 0;
}

BOOL 
NSCompareMapTables(NSMapTable *table1, NSMapTable *table2)
{
unsigned i;												// Compare Two Tables
struct _NSMapNode *node1;
    
    if (table1->hashSize != table2->hashSize)
		return NO;
    for (i = 0; i < table1->hashSize; i++) 
		for (node1 = table1->nodes[i]; node1; node1 = node1->next)
			if (NSMapGet(table2, node1->key) != node1->value)
				return NO;

    return YES;
}
													// Return Number of Items 
unsigned 
NSCountMapTable(NSMapTable *table)			{ return table->itemsCount; }

BOOL 
NSMapMember(NSMapTable *table, const void *key,void **originalKey,void **value)
{
struct _NSMapNode *node;

	node = table->nodes[table->keyCallbacks.hash(table,key) % table->hashSize];
    for(; node; node = node->next)
        if(table->keyCallbacks.isEqual(table, key, node->key)) 
			{
            *originalKey = node->key;
			*value = node->value;
			return YES;
			}

    return NO;
}

void *
NSMapGet(NSMapTable *table, const void *key)
{
struct _NSMapNode *node;

	node = table->nodes[table->keyCallbacks.hash(table,key) % table->hashSize];
    for(; node; node = node->next)
        if(table->keyCallbacks.isEqual(table, key, node->key))
            return node->value;

    return NULL;
}

NSMapEnumerator 
NSEnumerateMapTable(NSMapTable *table)
{
NSMapEnumerator en;

    en.table = table;
    en.node = NULL;
    en.bucket = -1;

    return en;
}

BOOL 
NSNextMapEnumeratorPair(NSMapEnumerator *en, void **key, void **value)
{
    if(en->node)
		en->node = en->node->next;
    if(en->node == NULL) {
	for(en->bucket++; ((unsigned)en->bucket)<en->table->hashSize; en->bucket++)
	    if (en->table->nodes[en->bucket]) {
		    en->node = en->table->nodes[en->bucket];
		    break;
	    }
	if (((unsigned)en->bucket) >= en->table->hashSize) {
	    en->node = NULL;
	    en->bucket = en->table->hashSize-1;
	    return NO;
	}
    }
    *key = en->node->key;
    *value = en->node->value;
    return YES;
}

NSArray *
NSAllMapTableKeys(NSMapTable *table)
{
	id array = [NSMutableArray arrayWithCapacity:table->itemsCount];
	struct _NSMapNode *node;
	unsigned i;
	if(table)
		{
		for(i = 0; i < table->hashSize; i++)
			for(node = table->nodes[i]; node; node=node->next)
				[array addObject:(NSObject*)(node->key)];
		}
    return array;
}

NSArray *
NSAllMapTableValues(NSMapTable *table)
{
	id array = [NSMutableArray arrayWithCapacity:table->itemsCount];
	struct _NSMapNode *node;
	unsigned i;
	if(table)
		{
		for(i = 0; i < table->hashSize; i++)
			for(node = table->nodes[i]; node; node = node->next)
				[array addObject:(NSObject*)(node->value)];
		}
    return array;
}

static void 
__NSMapGrow(NSMapTable *table, unsigned newSize)		// Add / Remove an Item
{
unsigned i;
struct _NSMapNode **newNodeTable = objc_calloc(newSize, sizeof(struct _NSMapNode*));
    
    for(i = 0; i < table->hashSize; i++) 
		{
		struct _NSMapNode *next, *node;
		unsigned int h;

		node = table->nodes[i];
		while(node) 
			{
			next = node->next;
			h = table->keyCallbacks.hash(table, node->key) % newSize;
			node->next = newNodeTable[h];
			newNodeTable[h] = node;
			node = next;
		}	}

    objc_free(table->nodes);
    table->nodes = newNodeTable;
    table->hashSize = newSize;
}

void 
NSMapInsert(NSMapTable *table, const void *key, const void *value)
{
unsigned int h;
struct _NSMapNode *node;

	if (key == table->keyCallbacks.notAKeyMarker)
		[NSException raise: NSInvalidArgumentException
        			 format: @"Invalid key to be added in NSMapTable."];

    h = table->keyCallbacks.hash(table, key) % table->hashSize;
    for(node = table->nodes[h]; node; node = node->next)
        if(table->keyCallbacks.isEqual(table, key, node->key))
            break;
											// Check if an entry for key exists
    if(node) 								// in nodeTable.
		{									
		if (key != node->key) 				// key exists.  Set it's new value
			{								// and release the old value.
			table->keyCallbacks.retain(table, key);
			table->keyCallbacks.release(table, node->key);
			}
		if (value != node->value) 
			{
			table->valueCallbacks.retain(table, value);
			table->valueCallbacks.release(table, node->value);
			}
		node->key = (void*)key;
		node->value = (void*)value;

		return;
		}

    node = objc_malloc(sizeof(struct _NSMapNode));	// key not found so allocate a
    table->keyCallbacks.retain(table, key);		// new bucket for the key
	table->valueCallbacks.retain(table, value);
    node->key = (void*)key;
    node->value = (void*)value;
    node->next = table->nodes[h];
    table->nodes[h] = node;

    __NSCheckMapTableFull(table);
}

void *
NSMapInsertIfAbsent(NSMapTable *table, const void *key,const void *value)
{
unsigned int h;
struct _NSMapNode *node;

    if (key == table->keyCallbacks.notAKeyMarker)
		[NSException raise: NSInvalidArgumentException
        			 format: @"Invalid key to be added in NSMapTable."];

    h = table->keyCallbacks.hash(table, key) % table->hashSize;
    for(node = table->nodes[h]; node; node = node->next)
        if(table->keyCallbacks.isEqual(table, key, node->key))
            break;								// Check if key already exists
    if(node)									// in the nodeTable and return
        return node->key;						// it if it does.

    node = objc_malloc(sizeof(struct _NSMapNode));	// key not found, alloc a new
    table->keyCallbacks.retain(table, key);		// bucket for the key
    table->valueCallbacks.retain(table, value);
    node->key = (void*)key;
    node->value = (void*)value;
    node->next = table->nodes[h];
    table->nodes[h] = node;

    __NSCheckMapTableFull(table);

    return NULL;
}

void 
NSMapInsertKnownAbsent(NSMapTable *table, const void *key, const void *value)
{
unsigned int h;
struct _NSMapNode *node;

    if (key == table->keyCallbacks.notAKeyMarker)
		[NSException raise: NSInvalidArgumentException
        			 format: @"Invalid key to be added in NSMapTable."];

    h = table->keyCallbacks.hash(table, key) % table->hashSize;
    for(node = table->nodes[h]; node; node = node->next)
        if(table->keyCallbacks.isEqual(table, key, node->key))
            break;

    if(node) 				// Check if an entry for key exists in nodeTable
		[NSException raise: NSInvalidArgumentException
        			 format: @"Nil object already existing in NSMapTable."];

    node = objc_malloc(sizeof(struct _NSMapNode));	// key not found, alloc a new
    table->keyCallbacks.retain(table, key);		// bucket for the key
    table->valueCallbacks.retain(table, value);
    node->key = (void*)key;
    node->value = (void*)value;
    node->next = table->nodes[h];
    table->nodes[h] = node;

    __NSCheckMapTableFull(table);
}

void 
NSMapRemove(NSMapTable *table, const void *key)
{
unsigned int h;
struct _NSMapNode *node, *node1 = NULL;

    if (key == nil)
	    return;

    h = table->keyCallbacks.hash(table, key) % table->hashSize;

    			// node points to current bucket, and node1 to previous bucket 
				// or to NULL if current node is the first node in the list 
    for(node = table->nodes[h]; node; node1 = node, node = node->next)
        if(table->keyCallbacks.isEqual(table, key, node->key)) 
			{
	    	table->keyCallbacks.release(table, node->key);
	    	table->valueCallbacks.release(table, node->value);
            if(!node1)
                table->nodes[h] = node->next;
            else
                node1->next = node->next;
	    	objc_free(node);
	    	(table->itemsCount)--;

	    	return;
        	}
}

NSString *
NSStringFromMapTable(NSMapTable *table)
{
id ret = [NSMutableString new];
unsigned i;
struct _NSMapNode *node;
    
    for (i = 0; i < table->hashSize; i++)
	  for (node = table->nodes[i]; node; node = node->next) 
		{
	    [ret appendString:table->keyCallbacks.describe(table, node->key)];
	    [ret appendString:@"="];
	    [ret appendString:table->valueCallbacks.describe(table, node->value)];
	    [ret appendString:@"\n"];
		}
    
    return ret;
}

//*****************************************************************************
//
// 		Convenience functions 
//
//*****************************************************************************

unsigned 
__NSHashObject(void *table, const void *anObject)
{
    return (unsigned)[(id)anObject hash];
}

unsigned 
__NSHashPointer(void *table, const void *anObject)
{
    return (unsigned)((long)anObject / 4);
}

unsigned 
__NSHashInteger(void *table, const void *anObject)
{
    return (unsigned)(long)anObject;
}

unsigned 
__NSHashCString(void *table, const void *aString)
{
register const char *p = (char*)aString;
register unsigned hash = 0, hash2;
register int i, n = strlen((char*)aString);

    for(i = 0; i < n; i++) 
		{
        hash <<= 4;
        hash += *p++;
        if((hash2 = hash & 0xf0000000))
            hash ^= (hash2 >> 24) ^ hash2;
		}

    return hash;
}

BOOL 
__NSCompareObjects(void *table, const void *anObject1, const void *anObject2)
{
    return [(NSObject*)anObject1 isEqual:(NSObject*)anObject2];
}

BOOL 
__NSComparePointers(void *table, const void *anObject1, const void *anObject2)
{
    return anObject1 == anObject2;
}

BOOL 
__NSCompareInts(void *table, const void *anObject1, const void *anObject2)
{
    return anObject1 == anObject2;
}

BOOL 
__NSCompareCString(void *table, const void *anObject1, const void *anObject2)
{
    return strcmp((char*)anObject1, (char*)anObject2) == 0;
}

void 
__NSRetainObjects(void *table, const void *anObject)
{
    [(NSObject*)anObject retain];
}

void 
__NSRetainNothing(void *table, const void *anObject)		{}

void 
__NSReleaseNothing(void *table, void *anObject)				{}

void 
__NSReleasePointers(void *table, void *anObject)			{ objc_free(anObject); }

void 
__NSReleaseObjects(void *table, void *anObject)
{
    [(NSObject*)anObject release];
}

NSString *
__NSDescribeObjects(void *table, const void *anObject)
{
    return [(NSObject*)anObject description];
}

NSString *
__NSDescribePointers(void *table, const void *anObject)
{
    return [NSString stringWithFormat:@"%p", anObject];
}

NSString *
__NSDescribeInts(void *table, const void *anObject)
{
    return [NSString stringWithFormat:@"%ld", (long)anObject];
}

											// NSHashTable predefined callbacks
const NSHashTableCallBacks NSIntHashCallBacks = { 
    (unsigned(*)(NSHashTable*, const void*))__NSHashInteger, 
    (BOOL(*)(NSHashTable*, const void*, const void*))__NSCompareInts, 
    (void(*)(NSHashTable*, const void*))__NSRetainNothing, 
    (void(*)(NSHashTable*, void*))__NSReleaseNothing, 
    (NSString*(*)(NSHashTable*, const void*))__NSDescribeInts 
};

const NSHashTableCallBacks NSNonOwnedPointerHashCallBacks = { 
    (unsigned(*)(NSHashTable*, const void*))__NSHashPointer, 
    (BOOL(*)(NSHashTable*, const void*, const void*))__NSComparePointers, 
    (void(*)(NSHashTable*, const void*))__NSRetainNothing, 
    (void(*)(NSHashTable*, void*))__NSReleaseNothing, 
    (NSString*(*)(NSHashTable*, const void*))__NSDescribePointers 
};

const NSHashTableCallBacks NSNonRetainedObjectHashCallBacks = { 
    (unsigned(*)(NSHashTable*, const void*))__NSHashObject, 
    (BOOL(*)(NSHashTable*, const void*, const void*))__NSCompareObjects, 
    (void(*)(NSHashTable*, const void*))__NSRetainNothing, 
    (void(*)(NSHashTable*, void*))__NSReleaseNothing, 
    (NSString*(*)(NSHashTable*, const void*))__NSDescribeObjects 
};
 
const NSHashTableCallBacks NSObjectHashCallBacks = { 
    (unsigned(*)(NSHashTable*, const void*))__NSHashObject, 
    (BOOL(*)(NSHashTable*, const void*, const void*))__NSCompareObjects, 
    (void(*)(NSHashTable*, const void*))__NSRetainObjects, 
    (void(*)(NSHashTable*, void*))__NSReleaseObjects, 
    (NSString*(*)(NSHashTable*, const void*))__NSDescribeObjects 
};

const NSHashTableCallBacks NSOwnedObjectIdentityHashCallBacks = { 
    (unsigned(*)(NSHashTable*, const void*))__NSHashPointer, 
    (BOOL(*)(NSHashTable*, const void*, const void*))__NSComparePointers, 
    (void(*)(NSHashTable*, const void*))__NSRetainObjects, 
    (void(*)(NSHashTable*, void*))__NSReleaseObjects, 
    (NSString*(*)(NSHashTable*, const void*))__NSDescribeObjects 
};

const NSHashTableCallBacks NSOwnedPointerHashCallBacks = { 
    (unsigned(*)(NSHashTable*, const void*))__NSHashObject, 
    (BOOL(*)(NSHashTable*, const void*, const void*))__NSCompareObjects, 
    (void(*)(NSHashTable*, const void*))__NSRetainNothing, 
    (void(*)(NSHashTable*, void*))__NSReleasePointers, 
    (NSString*(*)(NSHashTable*, const void*))__NSDescribePointers 
};

const NSHashTableCallBacks NSPointerToStructHashCallBacks = { 
    (unsigned(*)(NSHashTable*, const void*))__NSHashPointer, 
    (BOOL(*)(NSHashTable*, const void*, const void*))__NSComparePointers, 
    (void(*)(NSHashTable*, const void*))__NSRetainNothing, 
    (void(*)(NSHashTable*, void*))__NSReleasePointers, 
    (NSString*(*)(NSHashTable*, const void*))__NSDescribePointers 
};
											// NSMapTable predefined callbacks 
const NSMapTableKeyCallBacks NSIntMapKeyCallBacks = {
    (unsigned(*)(NSMapTable *, const void *))__NSHashInteger,
    (BOOL(*)(NSMapTable *, const void *, const void *))__NSCompareInts,
    (void (*)(NSMapTable *, const void *anObject))__NSRetainNothing,
    (void (*)(NSMapTable *, void *anObject))__NSReleaseNothing,
    (NSString *(*)(NSMapTable *, const void *))__NSDescribeInts,
    (const void *)NSNotAnIntMapKey
};

const NSMapTableValueCallBacks NSIntMapValueCallBacks = {
    (void (*)(NSMapTable *, const void *))__NSRetainNothing,
    (void (*)(NSMapTable *, void *))__NSReleaseNothing,
    (NSString *(*)(NSMapTable *, const void *))__NSDescribeInts
};

const NSMapTableKeyCallBacks NSNonOwnedPointerMapKeyCallBacks = {
    (unsigned(*)(NSMapTable *, const void *))__NSHashPointer,
    (BOOL(*)(NSMapTable *, const void *, const void *))__NSComparePointers,
    (void (*)(NSMapTable *, const void *anObject))__NSRetainNothing,
    (void (*)(NSMapTable *, void *anObject))__NSReleaseNothing,
    (NSString *(*)(NSMapTable *, const void *))__NSDescribePointers,
    (const void *)NULL
}; 

const NSMapTableKeyCallBacks NSNonOwnedCStringMapKeyCallBacks = {
    (unsigned(*)(NSMapTable *, const void *))__NSHashCString,
    (BOOL(*)(NSMapTable *, const void *, const void *))__NSCompareCString,
    (void (*)(NSMapTable *, const void *anObject))__NSRetainNothing,
    (void (*)(NSMapTable *, void *anObject))__NSReleaseNothing,
    (NSString *(*)(NSMapTable *, const void *))__NSDescribePointers,
    (const void *)NULL
}; 

const NSMapTableKeyCallBacks NSOwnedPointerMapKeyCallBacks = {
    (unsigned(*)(NSMapTable *, const void *))__NSHashPointer,
    (BOOL(*)(NSMapTable *, const void *, const void *))__NSComparePointers,
    (void (*)(NSMapTable *, const void *anObject))__NSRetainNothing,
    (void (*)(NSMapTable *, void *anObject))__NSReleasePointers,
    (NSString *(*)(NSMapTable *, const void *))__NSDescribePointers,
    (const void *)NULL
};

const NSMapTableValueCallBacks NSNonOwnedPointerMapValueCallBacks = {
    (void (*)(NSMapTable *, const void *))__NSRetainNothing,
    (void (*)(NSMapTable *, void *))__NSReleaseNothing,
    (NSString *(*)(NSMapTable *, const void *))__NSDescribePointers
};

const NSMapTableKeyCallBacks NSNonOwnedPointerOrNullMapKeyCallBacks = {
    (unsigned(*)(NSMapTable *, const void *))__NSHashPointer,
    (BOOL(*)(NSMapTable *, const void *, const void *))__NSComparePointers,
    (void (*)(NSMapTable *, const void *anObject))__NSRetainNothing,
    (void (*)(NSMapTable *, void *anObject))__NSReleaseNothing,
    (NSString *(*)(NSMapTable *, const void *))__NSDescribePointers,
    (const void *)NSNotAPointerMapKey
};

const NSMapTableKeyCallBacks NSNonRetainedObjectMapKeyCallBacks = {
    (unsigned(*)(NSMapTable *, const void *))__NSHashObject,
    (BOOL(*)(NSMapTable *, const void *, const void *))__NSCompareObjects,
    (void (*)(NSMapTable *, const void *anObject))__NSRetainNothing,
    (void (*)(NSMapTable *, void *anObject))__NSReleaseNothing,
    (NSString *(*)(NSMapTable *, const void *))__NSDescribeObjects,
    (const void *)NULL
};

const NSMapTableValueCallBacks NSNonRetainedObjectMapValueCallBacks = {
    (void (*)(NSMapTable *, const void *))__NSRetainNothing,
    (void (*)(NSMapTable *, void *))__NSReleaseNothing,
    (NSString *(*)(NSMapTable *, const void *))__NSDescribeObjects
}; 

const NSMapTableKeyCallBacks NSObjectMapKeyCallBacks = {
    (unsigned(*)(NSMapTable *, const void *))__NSHashObject,
    (BOOL(*)(NSMapTable *, const void *, const void *))__NSCompareObjects,
    (void (*)(NSMapTable *, const void *anObject))__NSRetainObjects,
    (void (*)(NSMapTable *, void *anObject))__NSReleaseObjects,
    (NSString *(*)(NSMapTable *, const void *))__NSDescribeObjects,
    (const void *)NULL
}; 

const NSMapTableValueCallBacks NSObjectMapValueCallBacks = {
    (void (*)(NSMapTable *, const void *))__NSRetainObjects,
    (void (*)(NSMapTable *, void *))__NSReleaseObjects,
    (NSString *(*)(NSMapTable *, const void *))__NSDescribeObjects
}; 

const NSMapTableKeyCallBacks GSOwnedCStringMapKeyCallBacks = {
    (unsigned(*)(NSMapTable *, const void *))__NSHashCString,
    (BOOL(*)(NSMapTable *, const void *, const void *))__NSCompareCString,
    (void (*)(NSMapTable *, const void *anObject))__NSRetainNothing,
    (void (*)(NSMapTable *, void *anObject))__NSReleasePointers,
    (NSString *(*)(NSMapTable *, const void *))__NSDescribePointers,
    (const void *)NULL
}; 

const NSMapTableValueCallBacks NSOwnedPointerMapValueCallBacks = {
    (void (*)(NSMapTable *, const void *))__NSRetainNothing,
    (void (*)(NSMapTable *, void *))__NSReleasePointers,
    (NSString *(*)(NSMapTable *, const void *))__NSDescribePointers
}; 

