mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-25 17:36:18 +03:00
478 lines
12 KiB
C
478 lines
12 KiB
C
/*
|
|
* 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"
|
|
#include "QuantDefines.h"
|
|
|
|
typedef struct _IntHashNode {
|
|
struct _IntHashNode *next;
|
|
void *key,*value;
|
|
} IntHashNode;
|
|
|
|
typedef struct _IntHashTable {
|
|
IntHashNode **table;
|
|
unsigned long length;
|
|
unsigned long count;
|
|
HashFunc hashFunc;
|
|
HashCmpFunc cmpFunc;
|
|
DestroyFunc keyDestroyFunc;
|
|
DestroyFunc valDestroyFunc;
|
|
void *userData;
|
|
} IntHashTable;
|
|
|
|
#define MIN_LENGTH 11
|
|
#define RESIZE_FACTOR 3
|
|
|
|
static int _hashtable_insert_node(IntHashTable *,IntHashNode *,int,int,CollisionFunc);
|
|
#if 0
|
|
static int _hashtable_test(IntHashTable *);
|
|
#endif
|
|
|
|
HashTable hashtable_new(HashFunc hf,HashCmpFunc cf) {
|
|
IntHashTable *h;
|
|
h=malloc(sizeof(IntHashTable));
|
|
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;
|
|
h->table=malloc(sizeof(IntHashNode *)*h->length);
|
|
if (!h->table) { free(h); return NULL; }
|
|
memset (h->table,0,sizeof(IntHashNode *)*h->length);
|
|
return (HashTable)h;
|
|
}
|
|
|
|
static void _hashtable_destroy(HashTable H,const void *key,const void *val,void *u) {
|
|
IntHashTable *h=(IntHashTable *)H;
|
|
if (h->keyDestroyFunc&&key) {
|
|
h->keyDestroyFunc((HashTable)h,(void *)key);
|
|
}
|
|
if (h->valDestroyFunc&&val) {
|
|
h->valDestroyFunc((HashTable)h,(void *)val);
|
|
}
|
|
}
|
|
|
|
static unsigned long _findPrime(unsigned long start,int dir) {
|
|
static int unit[]={0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,0};
|
|
unsigned long 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(IntHashTable *h,
|
|
CollisionFunc cf,
|
|
unsigned long newSize) {
|
|
IntHashNode **oldTable=h->table;
|
|
unsigned long i;
|
|
IntHashNode *n,*nn;
|
|
unsigned long oldSize;
|
|
oldSize=h->length;
|
|
h->table=malloc(sizeof(IntHashNode *)*newSize);
|
|
if (!h->table) {
|
|
h->table=oldTable;
|
|
return;
|
|
}
|
|
h->length=newSize;
|
|
h->count=0;
|
|
memset (h->table,0,sizeof(IntHashNode *)*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(IntHashTable *h) {
|
|
unsigned long newSize;
|
|
unsigned long 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);
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
static int _hashtable_test(IntHashTable *h) {
|
|
unsigned long i;
|
|
int j;
|
|
IntHashNode *n;
|
|
for (i=0;i<h->length;i++) {
|
|
for (n=h->table[i];n&&n->next;n=n->next) {
|
|
j=h->cmpFunc((HashTable)h,n->key,n->next->key);
|
|
printf ("%c",j?(j<0?'-':'+'):'=');
|
|
}
|
|
printf ("\n");
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int _hashtable_insert_node(IntHashTable *h,IntHashNode *node,int resize,int update,CollisionFunc cf) {
|
|
unsigned long hash=h->hashFunc((HashTable)h,node->key)%h->length;
|
|
IntHashNode **n,*nv;
|
|
int i;
|
|
|
|
for (n=&(h->table[hash]);*n;n=&((*n)->next)) {
|
|
nv=*n;
|
|
i=h->cmpFunc((HashTable)h,nv->key,node->key);
|
|
if (!i) {
|
|
if (cf) {
|
|
nv->key=node->key;
|
|
cf((HashTable)h,&(nv->key),&(nv->value),node->key,node->value);
|
|
free(node);
|
|
return 1;
|
|
} else {
|
|
if (h->valDestroyFunc) {
|
|
h->valDestroyFunc((HashTable)h,nv->value);
|
|
}
|
|
if (h->keyDestroyFunc) {
|
|
h->keyDestroyFunc((HashTable)h,nv->key);
|
|
}
|
|
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(IntHashTable *h,void *key,void *val,int resize,int update) {
|
|
IntHashNode **n,*nv;
|
|
IntHashNode *t;
|
|
int i;
|
|
unsigned long hash=h->hashFunc((HashTable)h,key)%h->length;
|
|
|
|
for (n=&(h->table[hash]);*n;n=&((*n)->next)) {
|
|
nv=*n;
|
|
i=h->cmpFunc((HashTable)h,nv->key,key);
|
|
if (!i) {
|
|
if (h->valDestroyFunc) { h->valDestroyFunc((HashTable)h,nv->value); }
|
|
nv->value=val;
|
|
return 1;
|
|
} else if (i>0) {
|
|
break;
|
|
}
|
|
}
|
|
if (!update) {
|
|
t=malloc(sizeof(IntHashNode));
|
|
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;
|
|
}
|
|
}
|
|
|
|
static int _hashtable_lookup_or_insert(IntHashTable *h,void *key,void **retVal,void *newVal,int resize) {
|
|
IntHashNode **n,*nv;
|
|
IntHashNode *t;
|
|
int i;
|
|
unsigned long hash=h->hashFunc((HashTable)h,key)%h->length;
|
|
|
|
for (n=&(h->table[hash]);*n;n=&((*n)->next)) {
|
|
nv=*n;
|
|
i=h->cmpFunc((HashTable)h,nv->key,key);
|
|
if (!i) {
|
|
*retVal=nv->value;
|
|
return 1;
|
|
} else if (i>0) {
|
|
break;
|
|
}
|
|
}
|
|
t=malloc(sizeof(IntHashNode));
|
|
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;
|
|
}
|
|
|
|
int hashtable_insert_or_update_computed(HashTable H,
|
|
void *key,
|
|
ComputeFunc newFunc,
|
|
ComputeFunc existsFunc) {
|
|
IntHashTable *h=(IntHashTable *)H;
|
|
IntHashNode **n,*nv;
|
|
IntHashNode *t;
|
|
int i;
|
|
unsigned long hash=h->hashFunc((HashTable)h,key)%h->length;
|
|
|
|
for (n=&(h->table[hash]);*n;n=&((*n)->next)) {
|
|
nv=*n;
|
|
i=h->cmpFunc((HashTable)h,nv->key,key);
|
|
if (!i) {
|
|
void *old=nv->value;
|
|
if (existsFunc) {
|
|
existsFunc(H,nv->key,&(nv->value));
|
|
if (nv->value!=old) {
|
|
if (h->valDestroyFunc) {
|
|
h->valDestroyFunc((HashTable)h,old);
|
|
}
|
|
}
|
|
} else {
|
|
return 0;
|
|
}
|
|
return 1;
|
|
} else if (i>0) {
|
|
break;
|
|
}
|
|
}
|
|
t=malloc(sizeof(IntHashNode));
|
|
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_update(HashTable H,void *key,void *val) {
|
|
IntHashTable *h=(IntHashTable *)H;
|
|
return _hashtable_insert(h,key,val,1,0);
|
|
}
|
|
|
|
int hashtable_insert(HashTable H,void *key,void *val) {
|
|
IntHashTable *h=(IntHashTable *)H;
|
|
return _hashtable_insert(h,key,val,1,0);
|
|
}
|
|
|
|
void hashtable_foreach_update(HashTable H,IteratorUpdateFunc i,void *u) {
|
|
IntHashTable *h=(IntHashTable *)H;
|
|
IntHashNode *n;
|
|
unsigned long x;
|
|
|
|
if (h->table) {
|
|
for (x=0;x<h->length;x++) {
|
|
for (n=h->table[x];n;n=n->next) {
|
|
i((HashTable)h,n->key,(void **)&(n->value),u);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void hashtable_foreach(HashTable H,IteratorFunc i,void *u) {
|
|
IntHashTable *h=(IntHashTable *)H;
|
|
IntHashNode *n;
|
|
unsigned long x;
|
|
|
|
if (h->table) {
|
|
for (x=0;x<h->length;x++) {
|
|
for (n=h->table[x];n;n=n->next) {
|
|
i((HashTable)h,n->key,n->value,u);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void hashtable_free(HashTable H) {
|
|
IntHashTable *h=(IntHashTable *)H;
|
|
IntHashNode *n,*nn;
|
|
unsigned long i;
|
|
|
|
if (h->table) {
|
|
if (h->keyDestroyFunc || h->keyDestroyFunc) {
|
|
hashtable_foreach(H,_hashtable_destroy,NULL);
|
|
}
|
|
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);
|
|
}
|
|
|
|
DestroyFunc hashtable_set_value_destroy_func(HashTable H,DestroyFunc d) {
|
|
IntHashTable *h=(IntHashTable *)H;
|
|
DestroyFunc r=h->valDestroyFunc;
|
|
h->valDestroyFunc=d;
|
|
return r;
|
|
}
|
|
|
|
DestroyFunc hashtable_set_key_destroy_func(HashTable H,DestroyFunc d) {
|
|
IntHashTable *h=(IntHashTable *)H;
|
|
DestroyFunc r=h->keyDestroyFunc;
|
|
h->keyDestroyFunc=d;
|
|
return r;
|
|
}
|
|
|
|
static int _hashtable_remove(IntHashTable *h,
|
|
const void *key,
|
|
void **keyRet,
|
|
void **valRet,
|
|
int resize) {
|
|
unsigned long hash=h->hashFunc((HashTable)h,key)%h->length;
|
|
IntHashNode *n,*p;
|
|
int i;
|
|
|
|
for (p=NULL,n=h->table[hash];n;p=n,n=n->next) {
|
|
i=h->cmpFunc((HashTable)h,n->key,key);
|
|
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;
|
|
}
|
|
|
|
static int _hashtable_delete(IntHashTable *h,const void *key,int resize) {
|
|
unsigned long hash=h->hashFunc((HashTable)h,key)%h->length;
|
|
IntHashNode *n,*p;
|
|
int i;
|
|
|
|
for (p=NULL,n=h->table[hash];n;p=n,n=n->next) {
|
|
i=h->cmpFunc((HashTable)h,n->key,key);
|
|
if (!i) {
|
|
if (p) p=n->next; else h->table[hash]=n->next;
|
|
if (h->valDestroyFunc) { h->valDestroyFunc((HashTable)h,n->value); }
|
|
if (h->keyDestroyFunc) { h->keyDestroyFunc((HashTable)h,n->key); }
|
|
free(n);
|
|
h->count++;
|
|
return 1;
|
|
} else if (i>0) {
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int hashtable_remove(HashTable H,const void *key,void **keyRet,void **valRet) {
|
|
IntHashTable *h=(IntHashTable *)H;
|
|
return _hashtable_remove(h,key,keyRet,valRet,1);
|
|
}
|
|
|
|
int hashtable_delete(HashTable H,const void *key) {
|
|
IntHashTable *h=(IntHashTable *)H;
|
|
return _hashtable_delete(h,key,1);
|
|
}
|
|
|
|
void hashtable_rehash_compute(HashTable H,CollisionFunc cf) {
|
|
IntHashTable *h=(IntHashTable *)H;
|
|
_hashtable_rehash(h,cf,h->length);
|
|
}
|
|
|
|
void hashtable_rehash(HashTable H) {
|
|
IntHashTable *h=(IntHashTable *)H;
|
|
_hashtable_rehash(h,NULL,h->length);
|
|
}
|
|
|
|
int hashtable_lookup_or_insert(HashTable H,void *key,void **valp,void *val) {
|
|
IntHashTable *h=(IntHashTable *)H;
|
|
return _hashtable_lookup_or_insert(h,key,valp,val,1);
|
|
}
|
|
|
|
int hashtable_lookup(const HashTable H,const void *key,void **valp) {
|
|
IntHashTable *h=(IntHashTable *)H;
|
|
unsigned long hash=h->hashFunc((HashTable)h,key)%h->length;
|
|
IntHashNode *n;
|
|
int i;
|
|
|
|
for (n=h->table[hash];n;n=n->next) {
|
|
i=h->cmpFunc((HashTable)h,n->key,key);
|
|
if (!i) {
|
|
*valp=n->value;
|
|
return 1;
|
|
} else if (i>0) {
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
unsigned long hashtable_get_count(const HashTable H) {
|
|
IntHashTable *h=(IntHashTable *)H;
|
|
return h->count;
|
|
}
|
|
|
|
void *hashtable_get_user_data(const HashTable H) {
|
|
IntHashTable *h=(IntHashTable *)H;
|
|
return h->userData;
|
|
}
|
|
|
|
void *hashtable_set_user_data(HashTable H,void *data) {
|
|
IntHashTable *h=(IntHashTable *)H;
|
|
void *r=h->userData;
|
|
h->userData=data;
|
|
return r;
|
|
}
|