/* * The Python Imaging Library * $Id$ * * hash tables used by the image quantizer * * history: * 98-09-10 tjs Contributed * 98-12-29 fl Added to PIL 1.0b1 * * Written by Toby J Sargeant . * * Copyright (c) 1998 by Toby J Sargeant * Copyright (c) 1998 by Secret Labs AB * * See the README file for information on usage and redistribution. */ #include #include #include #include #include "QuantHash.h" typedef struct _HashNode { struct _HashNode *next; HashKey_t key; HashVal_t value; } HashNode; struct _HashTable { HashNode **table; uint32_t length; uint32_t count; HashFunc hashFunc; HashCmpFunc cmpFunc; void *userData; }; #define MIN_LENGTH 11 #define RESIZE_FACTOR 3 static int _hashtable_insert_node(HashTable *, HashNode *, int, int, CollisionFunc); HashTable * hashtable_new(HashFunc hf, HashCmpFunc cf) { HashTable *h; h = malloc(sizeof(HashTable)); if (!h) { return NULL; } h->hashFunc = hf; h->cmpFunc = cf; h->length = MIN_LENGTH; h->count = 0; h->userData = NULL; h->table = malloc(sizeof(HashNode *) * h->length); if (!h->table) { free(h); return NULL; } memset(h->table, 0, sizeof(HashNode *) * h->length); return h; } static uint32_t _findPrime(uint32_t start, int dir) { static int unit[] = {0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0}; uint32_t t; while (start > 1) { if (!unit[start & 0x0f]) { start += dir; continue; } for (t = 2; t < sqrt((double)start); t++) { if (!start % t) { break; } } if (t >= sqrt((double)start)) { break; } start += dir; } return start; } static void _hashtable_rehash(HashTable *h, CollisionFunc cf, uint32_t newSize) { HashNode **oldTable = h->table; uint32_t i; HashNode *n, *nn; uint32_t oldSize; oldSize = h->length; h->table = malloc(sizeof(HashNode *) * newSize); if (!h->table) { h->table = oldTable; return; } h->length = newSize; h->count = 0; memset(h->table, 0, sizeof(HashNode *) * h->length); for (i = 0; i < oldSize; i++) { for (n = oldTable[i]; n; n = nn) { nn = n->next; _hashtable_insert_node(h, n, 0, 0, cf); } } free(oldTable); } static void _hashtable_resize(HashTable *h) { uint32_t newSize; uint32_t oldSize; oldSize = h->length; newSize = oldSize; if (h->count * RESIZE_FACTOR < h->length) { newSize = _findPrime(h->length / 2 - 1, -1); } else if (h->length * RESIZE_FACTOR < h->count) { newSize = _findPrime(h->length * 2 + 1, +1); } if (newSize < MIN_LENGTH) { newSize = oldSize; } if (newSize != oldSize) { _hashtable_rehash(h, NULL, newSize); } } static int _hashtable_insert_node( HashTable *h, HashNode *node, int resize, int update, CollisionFunc cf) { uint32_t hash = h->hashFunc(h, node->key) % h->length; HashNode **n, *nv; int i; for (n = &(h->table[hash]); *n; n = &((*n)->next)) { nv = *n; i = h->cmpFunc(h, nv->key, node->key); if (!i) { if (cf) { nv->key = node->key; cf(h, &(nv->key), &(nv->value), node->key, node->value); free(node); return 1; } else { nv->key = node->key; nv->value = node->value; free(node); return 1; } } else if (i > 0) { break; } } if (!update) { node->next = *n; *n = node; h->count++; if (resize) { _hashtable_resize(h); } return 1; } else { return 0; } } static int _hashtable_insert(HashTable *h, HashKey_t key, HashVal_t val, int resize, int update) { HashNode **n, *nv; HashNode *t; int i; uint32_t hash = h->hashFunc(h, key) % h->length; for (n = &(h->table[hash]); *n; n = &((*n)->next)) { nv = *n; i = h->cmpFunc(h, nv->key, key); if (!i) { nv->value = val; return 1; } else if (i > 0) { break; } } if (!update) { t = malloc(sizeof(HashNode)); if (!t) { return 0; } t->next = *n; *n = t; t->key = key; t->value = val; h->count++; if (resize) { _hashtable_resize(h); } return 1; } else { return 0; } } int hashtable_insert_or_update_computed( HashTable *h, HashKey_t key, ComputeFunc newFunc, ComputeFunc existsFunc) { HashNode **n, *nv; HashNode *t; int i; uint32_t hash = h->hashFunc(h, key) % h->length; for (n = &(h->table[hash]); *n; n = &((*n)->next)) { nv = *n; i = h->cmpFunc(h, nv->key, key); if (!i) { if (existsFunc) { existsFunc(h, nv->key, &(nv->value)); } else { return 0; } return 1; } else if (i > 0) { break; } } t = malloc(sizeof(HashNode)); if (!t) { return 0; } t->key = key; t->next = *n; *n = t; if (newFunc) { newFunc(h, t->key, &(t->value)); } else { free(t); return 0; } h->count++; _hashtable_resize(h); return 1; } int hashtable_insert(HashTable *h, HashKey_t key, HashVal_t val) { return _hashtable_insert(h, key, val, 1, 0); } void hashtable_foreach_update(HashTable *h, IteratorUpdateFunc i, void *u) { HashNode *n; uint32_t x; if (h->table) { for (x = 0; x < h->length; x++) { for (n = h->table[x]; n; n = n->next) { i(h, n->key, &(n->value), u); } } } } void hashtable_foreach(HashTable *h, IteratorFunc i, void *u) { HashNode *n; uint32_t x; if (h->table) { for (x = 0; x < h->length; x++) { for (n = h->table[x]; n; n = n->next) { i(h, n->key, n->value, u); } } } } void hashtable_free(HashTable *h) { HashNode *n, *nn; uint32_t i; if (h->table) { for (i = 0; i < h->length; i++) { for (n = h->table[i]; n; n = nn) { nn = n->next; free(n); } } free(h->table); } free(h); } void hashtable_rehash_compute(HashTable *h, CollisionFunc cf) { _hashtable_rehash(h, cf, h->length); } int hashtable_lookup(const HashTable *h, const HashKey_t key, HashVal_t *valp) { uint32_t hash = h->hashFunc(h, key) % h->length; HashNode *n; int i; for (n = h->table[hash]; n; n = n->next) { i = h->cmpFunc(h, n->key, key); if (!i) { *valp = n->value; return 1; } else if (i > 0) { break; } } return 0; } uint32_t hashtable_get_count(const HashTable *h) { return h->count; } void * hashtable_get_user_data(const HashTable *h) { return h->userData; } void * hashtable_set_user_data(HashTable *h, void *data) { void *r = h->userData; h->userData = data; return r; }