/**************************************************************************** Copyright (C) 1987-2005 by Jeffery P. Hansen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ****************************************************************************/ #include #include #include #include "hash.h" void *ob_malloc(int,char*); void *ob_calloc(int,int,char*); void ob_free(void*); char *ob_strdup(const char*); void ob_touch(void*); /* Round N up to a power of 2. */ static unsigned int roundup(unsigned int N) { unsigned P = N & (N ^ -N); if (P) { do { N = P; P = N & (N ^ -N); } while (P); N <<= 1; } return N; } static hashcode_t inthash(hashcode_t key) { key += 123456; key += (key << 12); key ^= (key >> 22); key += (key << 4); key ^= (key >> 9); key += (key << 10); key ^= (key >> 2); key += (key << 7); key ^= (key >> 12); return key; } static hashcode_t strhash(const char *s) { hashcode_t N = 0; hashcode_t H = 0; int i; for (i = 0;*s;s++, i++) { N <<= 8; N |= (unsigned char) *s; if ((i & 3) == 3) H = inthash(H + N); } H = inthash(H + N); return H; } static HashElem *new_SHashElem(const char *key,hashcode_t hcode, void *val) { HashElem *E = (HashElem*) ob_malloc(sizeof(HashElem),"HashElem"); E->key.s = ob_strdup(key); E->hashcode = hcode; E->value = val; E->next = 0; return E; } static HashElem *new_NHashElem(int key,hashcode_t hcode,void *val) { HashElem *E = (HashElem*) ob_malloc(sizeof(HashElem),"HashElem"); E->key.d = key; E->hashcode = hcode; E->value = val; E->next = 0; return E; } void SHashElem_uninit(HashElem *E) { ob_free(E->key.s); } Hash *new_Hash() { Hash *H = (Hash*) ob_malloc(sizeof(Hash),"Hash"); Hash_init(H); return H; } void delete_Hash(Hash *H,HashElemDelFunc *hdel) { Hash_uninit(H,hdel); ob_free(H); } void Hash_init(Hash *H) { ob_touch(H); H->size = roundup(INITIAL_HASHSIZE); H->mask = H->size-1; H->num = 0; H->loop_ok = 0; H->elems = (HashElem**) ob_calloc(H->size,sizeof(HashElem*),"HashElem[]"); ob_touch(H->elems); } void Hash_uninit(Hash *H,HashElemDelFunc *hdel) { int i; ob_touch(H); ob_touch(H->elems); for (i = 0;i < H->size;i++) { HashElem *E,*N; for (E = H->elems[i];E;E = N) { N = E->next; if (hdel) (*hdel)(E); ob_free(E); } } ob_free(H->elems); } HashElem *Hash_first(Hash *H) { int i; H->loop_ok = 1; /* This debugging flag does not need an ob_touch */ for (i = 0;i < H->size;i++) if (H->elems[i]) return H->elems[i]; return 0; } HashElem *Hash_next(Hash *H,HashElem *E) { int A; if (!H->loop_ok) { printf("echo Illegal sequential hash table access.\n"); fflush(stdout); } if (E->next) return E->next; for (A = (E->hashcode & H->mask) + 1;A < H->size;A++) if (H->elems[A]) return H->elems[A]; return 0; } /* Double the size of a hash table */ void Hash_grow(Hash *H) { int S = H->size; int i; HashElem **nea; ob_touch(H); ob_touch(H->elems); H->size <<= 1; H->mask = H->size - 1; nea = (HashElem**) ob_calloc(H->size,sizeof(HashElem*),"HashElem[]"); for (i = 0;i < S;i++) { HashElem *E; while ((E = H->elems[i])) { hashcode_t A = E->hashcode & H->mask; H->elems[i] = E->next; ob_touch(E); E->next = nea[A]; nea[A] = E; } } ob_free(H->elems); H->elems = nea; } void Hash_removeElem(Hash *H,int A,HashElem *E) { HashElem *P; ob_touch(H); H->num--; H->loop_ok = 0; if (H->elems[A] == E) { ob_touch(H->elems); H->elems[A] = E->next; return; } for (P = H->elems[A];P->next != E;P = P->next); ob_touch(P); P->next = E->next; } void *SHash_find(Hash *H,const char *key) { hashcode_t HC = strhash(key); hashcode_t A = HC & H->mask; HashElem *E; for (E = H->elems[A];E;E = E->next) if (E->hashcode == HC && strcmp(E->key.s,key) == 0) return E->value; return 0; } int SHash_insert(Hash *H,const char *key,void* val) { hashcode_t HC = strhash(key); hashcode_t A = HC & H->mask; HashElem *E; ob_touch(H); H->loop_ok = 0; for (E = H->elems[A];E;E = E->next) if (E->hashcode == HC && strcmp(E->key.s,key) == 0) return -1; ob_touch(H->elems); E = new_SHashElem(key,HC,val); E->next = H->elems[A]; H->elems[A] = E; H->num++; if (H->size*HASH_MAXLOAD < H->num) Hash_grow(H); return 0; } int SHash_replace(Hash *H,const char *key,void* val) { hashcode_t HC = strhash(key); hashcode_t A = HC & H->mask; HashElem *E; ob_touch(H); ob_touch(H->elems); H->loop_ok = 0; for (E = H->elems[A];E;E = E->next) if (E->hashcode == HC && strcmp(E->key.s,key) == 0) { ob_touch(E); E->value = val; return 1; } E = new_SHashElem(key,HC,val); E->next = H->elems[A]; H->elems[A] = E; H->num++; if (H->size*HASH_MAXLOAD < H->num) Hash_grow(H); return 0; } int SHash_remove(Hash *H,const char *key) { hashcode_t HC = strhash(key); hashcode_t A = HC & H->mask; HashElem *E; ob_touch(H); H->loop_ok = 0; for (E = H->elems[A];E;E = E->next) if (E->hashcode == HC && strcmp(E->key.s,key) == 0) { Hash_removeElem(H,A,E); ob_free(E->key.s); ob_free(E); return 0; } return -1; } void Hash_flush(Hash *H,HashElemDelFunc *hdel) { int i; ob_touch(H); ob_touch(H->elems); H->loop_ok = 0; for (i = 0;i < H->size;i++) { HashElem *E = H->elems[i]; HashElem *N; for (;E;E = N) { N = E->next; if (hdel) (*hdel)(E); ob_free(E); } H->elems[i] = 0; } H->num = 0; } void *NHash_find(Hash *H,nkey_t key) { hashcode_t HC = inthash(key); hashcode_t A = HC & H->mask; HashElem *E; for (E = H->elems[A];E;E = E->next) if (E->key.d == key) return E->value; return 0; } int NHash_insert(Hash *H,nkey_t key,void* val) { hashcode_t HC = inthash(key); hashcode_t A = HC & H->mask; HashElem *E; ob_touch(H); ob_touch(H->elems); H->loop_ok = 0; for (E = H->elems[A];E;E = E->next) if (E->key.d == key) return -1; E = new_NHashElem(key,HC,val); E->next = H->elems[A]; H->elems[A] = E; H->num++; if (H->size*HASH_MAXLOAD < H->num) Hash_grow(H); return 0; } int NHash_remove(Hash *H,nkey_t key) { hashcode_t HC = inthash(key); hashcode_t A = HC & H->mask; HashElem *E; ob_touch(H); H->loop_ok = 0; for (E = H->elems[A];E;E = E->next) if (E->key.d == key) { Hash_removeElem(H,A,E); ob_free(E); return 0; } return -1; }