/* Copyright 1996,1997,2001,2002,2009 Alain Knaff. * This file is part of mtools. * * Mtools 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 3 of the License, or * (at your option) any later version. * * Mtools 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 Mtools. If not, see . * * hash.c - hash table. */ #include "sysincludes.h" #include "htable.h" #include "mtools.h" struct hashtable { T_HashFunc f1,f2; T_ComparFunc compar; size_t size; /* actual size of the array */ size_t fill; /* number of deleted or in use slots */ size_t inuse; /* number of slots in use */ size_t max; /* maximal number of elements to keep efficient */ T_HashTableEl *entries; }; static size_t sizes[]= { 5, 11, 23, 47, 97, 197, 397, 797, 1597, 3203, 6421, 12853, 25717, 51437, 102877, 205759, 411527, 823117, 1646237, 3292489, 6584983, 13169977, 26339969, 52679969, 105359939, 210719881, 421439783, 842879579, 1685759167, 0 }; static int deleted=0; static int unallocated=0; static int alloc_ht(T_HashTable *H, size_t size) { int i; size_t ii; for(i=0; sizes[i]; i++) if (sizes[i] > size*4 ) break; if (!sizes[i]) for(i=0; sizes[i]; i++) if (sizes[i] > size*2 ) break; if (!sizes[i]) for(i=0; sizes[i]; i++) if (sizes[i] > size) break; if(!sizes[i]) return -1; size = sizes[i]; if(size < H->size) size = H->size; /* never shrink the table */ H->max = size * 4 / 5 - 2; H->size = size; H->fill = 0; H->inuse = 0; H->entries = NewArray(size, T_HashTableEl); if (H->entries == NULL) return -1; /* out of memory error */ for(ii=0; ii < size; ii++) H->entries[ii] = &unallocated; return 0; } int make_ht(T_HashFunc f1, T_HashFunc f2, T_ComparFunc c, size_t size, T_HashTable **H) { *H = New(T_HashTable); if (*H == NULL){ return -1; /* out of memory error */ } (*H)->f1 = f1; (*H)->f2 = f2; (*H)->compar = c; (*H)->size = 0; if(alloc_ht(*H,size)) return -1; return 0; } int free_ht(T_HashTable *H, T_HashFunc entry_free) { size_t i; if(entry_free) for(i=0; i< H->size; i++) if (H->entries[i] != &unallocated && H->entries[i] != &deleted) entry_free(H->entries[i]); Free(H->entries); Free(H); return 0; } /* add into hash table without checking for repeats */ static int _hash_add(T_HashTable *H,T_HashTableEl *E, size_t *hint) { size_t f2, pos; int ctr; pos = H->f1(E) % H->size; f2 = (size_t) -1; ctr = 0; while(H->entries[pos] != &unallocated && H->entries[pos] != &deleted){ if (f2 == (size_t) -1) f2 = H->f2(E) % (H->size - 1); pos = (pos+f2+1) % H->size; ctr++; } if(H->entries[pos] == &unallocated) H->fill++; /* only increase fill if the previous element was not yet * counted, i.e. unallocated */ H->inuse++; H->entries[pos] = E; if(hint) *hint = pos; return 0; } static int rehash(T_HashTable *H) { size_t size,i; T_HashTableEl *oldentries; /* resize the table */ size = H->size; oldentries = H->entries; if(alloc_ht(H,((H->inuse+1)*4+H->fill)/5)) return -1; for(i=0; i < size; i++){ if(oldentries[i] != &unallocated && oldentries[i] != &deleted) _hash_add(H, oldentries[i], 0); } Free(oldentries); return 0; } int hash_add(T_HashTable *H, T_HashTableEl *E, size_t *hint) { if (H->fill >= H->max) rehash(H); if (H->fill == H->size) return -1; /*out of memory error */ return _hash_add(H,E, hint); } /* add into hash table without checking for repeats */ static int _hash_lookup(T_HashTable *H,T_HashTableEl *E, T_HashTableEl **E2, size_t *hint, int isIdentity) { size_t f2, pos, upos, ttl; pos = H->f1(E) % H->size; ttl = H->size; f2 = (size_t) -1; upos = (size_t) -1; while(ttl && H->entries[pos] != &unallocated && (H->entries[pos] == &deleted || ((isIdentity || H->compar(H->entries[pos], E) != 0) && (!isIdentity || H->entries[pos] != E)))){ if (f2 == (size_t) -1) f2 = H->f2(E) % (H->size - 1); if (upos == (size_t) -1 && H->entries[pos] == &deleted) upos = pos; pos = (pos+f2+1) % H->size; ttl--; } if(H->entries[pos] == &unallocated || !ttl) return -1; if (upos != (size_t) -1){ H->entries[upos] = H->entries[pos]; H->entries[pos] = &deleted; pos = upos; } if(hint) *hint = pos; *E2= H->entries[pos]; return 0; } int hash_lookup(T_HashTable *H,T_HashTableEl *E, T_HashTableEl **E2, size_t *hint) { return _hash_lookup(H, E, E2, hint, 0); } /* add into hash table without checking for repeats */ int hash_remove(T_HashTable *H,T_HashTableEl *E, size_t hint) { T_HashTableEl *E2; if (hint >=0 && hint < H->size && H->entries[hint] == E){ H->inuse--; H->entries[hint] = &deleted; return 0; } if(_hash_lookup(H, E, &E2, &hint, 1)) { fprintf(stderr, "Removing non-existent entry\n"); exit(1); } H->inuse--; H->entries[hint] = &deleted; return 0; }