Pillow/libImaging/QuantHash.c

458 lines
10 KiB
C
Raw Normal View History

2010-07-31 06:52:47 +04:00
/*
* 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 <tjs@longford.cs.monash.edu.au>.
*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "QuantHash.h"
2013-04-08 02:52:15 +04:00
typedef struct _HashNode {
struct _HashNode *next;
HashKey_t key;
HashVal_t value;
} HashNode;
2010-07-31 06:52:47 +04:00
struct _HashTable {
2013-04-08 02:52:15 +04:00
HashNode **table;
uint32_t length;
uint32_t count;
2010-07-31 06:52:47 +04:00
HashFunc hashFunc;
HashCmpFunc cmpFunc;
2013-04-08 02:52:15 +04:00
KeyDestroyFunc keyDestroyFunc;
ValDestroyFunc valDestroyFunc;
2010-07-31 06:52:47 +04:00
void *userData;
};
2010-07-31 06:52:47 +04:00
#define MIN_LENGTH 11
#define RESIZE_FACTOR 3
2013-04-08 02:52:15 +04:00
static int _hashtable_insert_node(HashTable *,HashNode *,int,int,CollisionFunc);
2010-07-31 06:52:47 +04:00
#if 0
2013-04-08 02:52:15 +04:00
static int _hashtable_test(HashTable *);
2010-07-31 06:52:47 +04:00
#endif
2013-04-08 02:52:15 +04:00
HashTable *hashtable_new(HashFunc hf,HashCmpFunc cf) {
HashTable *h;
h=malloc(sizeof(HashTable));
2010-07-31 06:52:47 +04:00
if (!h) { return NULL; }
h->hashFunc=hf;
h->cmpFunc=cf;
h->keyDestroyFunc=NULL;
h->valDestroyFunc=NULL;
h->length=MIN_LENGTH;
h->count=0;
h->userData=NULL;
2013-04-08 02:52:15 +04:00
h->table=malloc(sizeof(HashNode *)*h->length);
2010-07-31 06:52:47 +04:00
if (!h->table) { free(h); return NULL; }
2013-04-08 02:52:15 +04:00
memset (h->table,0,sizeof(HashNode *)*h->length);
return h;
2010-07-31 06:52:47 +04:00
}
2013-04-08 02:52:15 +04:00
static void _hashtable_destroy(const HashTable *h,const HashKey_t key,const HashVal_t val,void *u) {
if (h->keyDestroyFunc) {
h->keyDestroyFunc(h,key);
2010-07-31 06:52:47 +04:00
}
2013-04-08 02:52:15 +04:00
if (h->valDestroyFunc) {
h->valDestroyFunc(h,val);
2010-07-31 06:52:47 +04:00
}
}
2013-04-08 02:52:15 +04:00
static uint32_t _findPrime(uint32_t start,int dir) {
2010-07-31 06:52:47 +04:00
static int unit[]={0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,0};
2013-04-08 02:52:15 +04:00
uint32_t t;
2010-07-31 06:52:47 +04:00
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;
}
2013-04-08 02:52:15 +04:00
static void _hashtable_rehash(HashTable *h,CollisionFunc cf,uint32_t newSize) {
HashNode **oldTable=h->table;
uint32_t i;
HashNode *n,*nn;
uint32_t oldSize;
2010-07-31 06:52:47 +04:00
oldSize=h->length;
2013-04-08 02:52:15 +04:00
h->table=malloc(sizeof(HashNode *)*newSize);
2010-07-31 06:52:47 +04:00
if (!h->table) {
h->table=oldTable;
return;
}
h->length=newSize;
h->count=0;
2013-04-08 02:52:15 +04:00
memset (h->table,0,sizeof(HashNode *)*h->length);
2010-07-31 06:52:47 +04:00
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);
}
2013-04-08 02:52:15 +04:00
static void _hashtable_resize(HashTable *h) {
uint32_t newSize;
uint32_t oldSize;
2010-07-31 06:52:47 +04:00
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);
}
}
#if 0
2013-04-08 02:52:15 +04:00
static int _hashtable_test(HashTable *h) {
uint32_t i;
2010-07-31 06:52:47 +04:00
int j;
2013-04-08 02:52:15 +04:00
HashNode *n;
2010-07-31 06:52:47 +04:00
for (i=0;i<h->length;i++) {
for (n=h->table[i];n&&n->next;n=n->next) {
2013-04-08 02:52:15 +04:00
j=h->cmpFunc(h,n->key,n->next->key);
2010-07-31 06:52:47 +04:00
printf ("%c",j?(j<0?'-':'+'):'=');
}
printf ("\n");
}
return 0;
}
#endif
2013-04-08 02:52:15 +04:00
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;
2010-07-31 06:52:47 +04:00
int i;
for (n=&(h->table[hash]);*n;n=&((*n)->next)) {
nv=*n;
2013-04-08 02:52:15 +04:00
i=h->cmpFunc(h,nv->key,node->key);
2010-07-31 06:52:47 +04:00
if (!i) {
if (cf) {
nv->key=node->key;
2013-04-08 02:52:15 +04:00
cf(h,&(nv->key),&(nv->value),node->key,node->value);
2010-07-31 06:52:47 +04:00
free(node);
return 1;
} else {
if (h->valDestroyFunc) {
2013-04-08 02:52:15 +04:00
h->valDestroyFunc(h,nv->value);
2010-07-31 06:52:47 +04:00
}
if (h->keyDestroyFunc) {
2013-04-08 02:52:15 +04:00
h->keyDestroyFunc(h,nv->key);
2010-07-31 06:52:47 +04:00
}
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;
}
}
2013-04-08 02:52:15 +04:00
static int _hashtable_insert(HashTable *h,HashKey_t key,HashVal_t val,int resize,int update) {
HashNode **n,*nv;
HashNode *t;
2010-07-31 06:52:47 +04:00
int i;
2013-04-08 02:52:15 +04:00
uint32_t hash=h->hashFunc(h,key)%h->length;
2010-07-31 06:52:47 +04:00
for (n=&(h->table[hash]);*n;n=&((*n)->next)) {
nv=*n;
2013-04-08 02:52:15 +04:00
i=h->cmpFunc(h,nv->key,key);
2010-07-31 06:52:47 +04:00
if (!i) {
2013-04-08 02:52:15 +04:00
if (h->valDestroyFunc) { h->valDestroyFunc(h,nv->value); }
2010-07-31 06:52:47 +04:00
nv->value=val;
return 1;
} else if (i>0) {
break;
}
}
if (!update) {
2013-04-08 02:52:15 +04:00
t=malloc(sizeof(HashNode));
2010-07-31 06:52:47 +04:00
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;
}
}
2013-04-08 02:52:15 +04:00
static int _hashtable_lookup_or_insert(HashTable *h,HashKey_t key,HashVal_t *retVal,HashVal_t newVal,int resize) {
HashNode **n,*nv;
HashNode *t;
2010-07-31 06:52:47 +04:00
int i;
2013-04-08 02:52:15 +04:00
uint32_t hash=h->hashFunc(h,key)%h->length;
2010-07-31 06:52:47 +04:00
for (n=&(h->table[hash]);*n;n=&((*n)->next)) {
nv=*n;
2013-04-08 02:52:15 +04:00
i=h->cmpFunc(h,nv->key,key);
2010-07-31 06:52:47 +04:00
if (!i) {
*retVal=nv->value;
return 1;
} else if (i>0) {
break;
}
}
2013-04-08 02:52:15 +04:00
t=malloc(sizeof(HashNode));
2010-07-31 06:52:47 +04:00
if (!t) return 0;
t->next=*n;
*n=t;
t->key=key;
t->value=newVal;
*retVal=newVal;
h->count++;
if (resize) _hashtable_resize(h);
return 1;
}
2013-04-08 02:52:15 +04:00
int hashtable_insert_or_update_computed(HashTable *h,
HashKey_t key,
2010-07-31 06:52:47 +04:00
ComputeFunc newFunc,
ComputeFunc existsFunc) {
2013-04-08 02:52:15 +04:00
HashNode **n,*nv;
HashNode *t;
2010-07-31 06:52:47 +04:00
int i;
2013-04-08 02:52:15 +04:00
uint32_t hash=h->hashFunc(h,key)%h->length;
2010-07-31 06:52:47 +04:00
for (n=&(h->table[hash]);*n;n=&((*n)->next)) {
nv=*n;
2013-04-08 02:52:15 +04:00
i=h->cmpFunc(h,nv->key,key);
2010-07-31 06:52:47 +04:00
if (!i) {
2013-04-08 02:52:15 +04:00
HashVal_t old=nv->value;
2010-07-31 06:52:47 +04:00
if (existsFunc) {
2013-04-08 02:52:15 +04:00
existsFunc(h,nv->key,&(nv->value));
2010-07-31 06:52:47 +04:00
if (nv->value!=old) {
if (h->valDestroyFunc) {
2013-04-08 02:52:15 +04:00
h->valDestroyFunc(h,old);
2010-07-31 06:52:47 +04:00
}
}
} else {
return 0;
}
return 1;
} else if (i>0) {
break;
}
}
2013-04-08 02:52:15 +04:00
t=malloc(sizeof(HashNode));
2010-07-31 06:52:47 +04:00
if (!t) return 0;
t->key=key;
t->next=*n;
*n=t;
if (newFunc) {
2013-04-08 02:52:15 +04:00
newFunc(h,t->key,&(t->value));
2010-07-31 06:52:47 +04:00
} else {
free(t);
return 0;
}
h->count++;
_hashtable_resize(h);
return 1;
}
2013-04-08 02:52:15 +04:00
int hashtable_update(HashTable *h,HashKey_t key,HashVal_t val) {
2010-07-31 06:52:47 +04:00
return _hashtable_insert(h,key,val,1,0);
}
2013-04-08 02:52:15 +04:00
int hashtable_insert(HashTable *h,HashKey_t key,HashVal_t val) {
2010-07-31 06:52:47 +04:00
return _hashtable_insert(h,key,val,1,0);
}
2013-04-08 02:52:15 +04:00
void hashtable_foreach_update(HashTable *h,IteratorUpdateFunc i,void *u) {
HashNode *n;
uint32_t x;
2010-07-31 06:52:47 +04:00
if (h->table) {
for (x=0;x<h->length;x++) {
for (n=h->table[x];n;n=n->next) {
2013-04-08 02:52:15 +04:00
i(h,n->key,&(n->value),u);
2010-07-31 06:52:47 +04:00
}
}
}
}
2013-04-08 02:52:15 +04:00
void hashtable_foreach(HashTable *h,IteratorFunc i,void *u) {
HashNode *n;
uint32_t x;
2010-07-31 06:52:47 +04:00
if (h->table) {
for (x=0;x<h->length;x++) {
for (n=h->table[x];n;n=n->next) {
2013-04-08 02:52:15 +04:00
i(h,n->key,n->value,u);
2010-07-31 06:52:47 +04:00
}
}
}
}
2013-04-08 02:52:15 +04:00
void hashtable_free(HashTable *h) {
HashNode *n,*nn;
uint32_t i;
2010-07-31 06:52:47 +04:00
if (h->table) {
if (h->keyDestroyFunc || h->keyDestroyFunc) {
2013-04-08 02:52:15 +04:00
hashtable_foreach(h,_hashtable_destroy,NULL);
2010-07-31 06:52:47 +04:00
}
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);
}
2013-04-08 02:52:15 +04:00
ValDestroyFunc hashtable_set_value_destroy_func(HashTable *h,ValDestroyFunc d) {
ValDestroyFunc r=h->valDestroyFunc;
2010-07-31 06:52:47 +04:00
h->valDestroyFunc=d;
return r;
}
2013-04-08 02:52:15 +04:00
KeyDestroyFunc hashtable_set_key_destroy_func(HashTable *h,KeyDestroyFunc d) {
KeyDestroyFunc r=h->keyDestroyFunc;
2010-07-31 06:52:47 +04:00
h->keyDestroyFunc=d;
return r;
}
2013-04-08 02:52:15 +04:00
static int _hashtable_remove(HashTable *h,
const HashKey_t key,
HashKey_t *keyRet,
HashVal_t *valRet,
2010-07-31 06:52:47 +04:00
int resize) {
2013-04-08 02:52:15 +04:00
uint32_t hash=h->hashFunc(h,key)%h->length;
HashNode *n,*p;
2010-07-31 06:52:47 +04:00
int i;
2010-07-31 06:52:47 +04:00
for (p=NULL,n=h->table[hash];n;p=n,n=n->next) {
2013-04-08 02:52:15 +04:00
i=h->cmpFunc(h,n->key,key);
2010-07-31 06:52:47 +04:00
if (!i) {
if (p) p=n->next; else h->table[hash]=n->next;
*keyRet=n->key;
*valRet=n->value;
free(n);
h->count++;
return 1;
} else if (i>0) {
break;
}
}
return 0;
}
2013-04-08 02:52:15 +04:00
static int _hashtable_delete(HashTable *h,const HashKey_t key,int resize) {
uint32_t hash=h->hashFunc(h,key)%h->length;
HashNode *n,*p;
2010-07-31 06:52:47 +04:00
int i;
2010-07-31 06:52:47 +04:00
for (p=NULL,n=h->table[hash];n;p=n,n=n->next) {
2013-04-08 02:52:15 +04:00
i=h->cmpFunc(h,n->key,key);
2010-07-31 06:52:47 +04:00
if (!i) {
if (p) p=n->next; else h->table[hash]=n->next;
2013-04-08 02:52:15 +04:00
if (h->valDestroyFunc) { h->valDestroyFunc(h,n->value); }
if (h->keyDestroyFunc) { h->keyDestroyFunc(h,n->key); }
2010-07-31 06:52:47 +04:00
free(n);
h->count++;
return 1;
} else if (i>0) {
break;
}
}
return 0;
}
2013-04-08 02:52:15 +04:00
int hashtable_remove(HashTable *h,const HashKey_t key,HashKey_t *keyRet,HashVal_t *valRet) {
2010-07-31 06:52:47 +04:00
return _hashtable_remove(h,key,keyRet,valRet,1);
}
2013-04-08 02:52:15 +04:00
int hashtable_delete(HashTable *h,const HashKey_t key) {
2010-07-31 06:52:47 +04:00
return _hashtable_delete(h,key,1);
}
2013-04-08 02:52:15 +04:00
void hashtable_rehash_compute(HashTable *h,CollisionFunc cf) {
2010-07-31 06:52:47 +04:00
_hashtable_rehash(h,cf,h->length);
}
2013-04-08 02:52:15 +04:00
void hashtable_rehash(HashTable *h) {
2010-07-31 06:52:47 +04:00
_hashtable_rehash(h,NULL,h->length);
}
2013-04-08 02:52:15 +04:00
int hashtable_lookup_or_insert(HashTable *h,HashKey_t key,HashVal_t *valp,HashVal_t val) {
2010-07-31 06:52:47 +04:00
return _hashtable_lookup_or_insert(h,key,valp,val,1);
}
2013-04-08 02:52:15 +04:00
int hashtable_lookup(const HashTable *h,const HashKey_t key,HashVal_t *valp) {
uint32_t hash=h->hashFunc(h,key)%h->length;
HashNode *n;
2010-07-31 06:52:47 +04:00
int i;
2010-07-31 06:52:47 +04:00
for (n=h->table[hash];n;n=n->next) {
2013-04-08 02:52:15 +04:00
i=h->cmpFunc(h,n->key,key);
2010-07-31 06:52:47 +04:00
if (!i) {
*valp=n->value;
return 1;
} else if (i>0) {
break;
}
}
return 0;
}
2013-04-08 02:52:15 +04:00
uint32_t hashtable_get_count(const HashTable *h) {
2010-07-31 06:52:47 +04:00
return h->count;
}
2013-04-08 02:52:15 +04:00
void *hashtable_get_user_data(const HashTable *h) {
2010-07-31 06:52:47 +04:00
return h->userData;
}
2013-04-08 02:52:15 +04:00
void *hashtable_set_user_data(HashTable *h,void *data) {
2010-07-31 06:52:47 +04:00
void *r=h->userData;
h->userData=data;
return r;
}