• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*  Copyright 1996,1997,2001,2002,2009 Alain Knaff.
2  *  This file is part of mtools.
3  *
4  *  Mtools is free software: you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation, either version 3 of the License, or
7  *  (at your option) any later version.
8  *
9  *  Mtools is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with Mtools.  If not, see <http://www.gnu.org/licenses/>.
16  *
17  * hash.c - hash table.
18  */
19 
20 #include "sysincludes.h"
21 #include "htable.h"
22 #include "mtools.h"
23 
24 struct hashtable {
25   T_HashFunc f1,f2;
26   T_ComparFunc compar;
27   size_t size;  /* actual size of the array */
28   size_t fill;  /* number of deleted or in use slots */
29   size_t inuse; /* number of slots in use */
30   size_t max;   /* maximal number of elements to keep efficient */
31   T_HashTableEl *entries;
32 };
33 
34 static size_t sizes[]=
35 	{ 5, 11, 23, 47, 97, 197, 397, 797, 1597, 3203, 6421, 12853,
36 	  25717, 51437, 102877, 205759, 411527, 823117, 1646237,
37 	  3292489, 6584983, 13169977, 26339969, 52679969, 105359939,
38 	  210719881, 421439783, 842879579, 1685759167, 0 };
39 static int deleted=0;
40 static int unallocated=0;
41 
alloc_ht(T_HashTable * H,size_t size)42 static int alloc_ht(T_HashTable *H, size_t size)
43 {
44   int i;
45   size_t ii;
46 
47   for(i=0; sizes[i]; i++)
48     if (sizes[i] > size*4 )
49       break;
50   if (!sizes[i])
51     for(i=0; sizes[i]; i++)
52       if (sizes[i] > size*2 )
53 	break;
54   if (!sizes[i])
55     for(i=0; sizes[i]; i++)
56       if (sizes[i] > size)
57 	break;
58   if(!sizes[i])
59     return -1;
60   size = sizes[i];
61   if(size < H->size)
62 	  size = H->size; /* never shrink the table */
63   H->max = size * 4 / 5 - 2;
64   H->size = size;
65   H->fill = 0;
66   H->inuse = 0;
67   H->entries = NewArray(size, T_HashTableEl);
68   if (H->entries == NULL)
69     return -1; /* out of memory error */
70 
71   for(ii=0; ii < size; ii++)
72     H->entries[ii] = &unallocated;
73   return 0;
74 }
75 
make_ht(T_HashFunc f1,T_HashFunc f2,T_ComparFunc c,size_t size,T_HashTable ** H)76 int make_ht(T_HashFunc f1, T_HashFunc f2, T_ComparFunc c, size_t size,
77 	    T_HashTable **H)
78 {
79   *H = New(T_HashTable);
80   if (*H == NULL){
81     return -1; /* out of memory error */
82   }
83 
84   (*H)->f1 = f1;
85   (*H)->f2 = f2;
86   (*H)->compar = c;
87   (*H)->size = 0;
88   if(alloc_ht(*H,size))
89     return -1;
90   return 0;
91 }
92 
free_ht(T_HashTable * H,T_HashFunc entry_free)93 int free_ht(T_HashTable *H, T_HashFunc entry_free)
94 {
95   size_t i;
96   if(entry_free)
97     for(i=0; i< H->size; i++)
98       if (H->entries[i] != &unallocated &&
99 	  H->entries[i] != &deleted)
100 	entry_free(H->entries[i]);
101   Free(H->entries);
102   Free(H);
103   return 0;
104 }
105 
106 /* add into hash table without checking for repeats */
_hash_add(T_HashTable * H,T_HashTableEl * E,size_t * hint)107 static int _hash_add(T_HashTable *H,T_HashTableEl *E, size_t *hint)
108 {
109   size_t f2, pos;
110   int ctr;
111 
112   pos = H->f1(E) % H->size;
113   f2 = (size_t) -1;
114   ctr = 0;
115   while(H->entries[pos] != &unallocated &&
116 	H->entries[pos] != &deleted){
117     if (f2 == (size_t) -1)
118       f2 = H->f2(E) % (H->size - 1);
119     pos = (pos+f2+1) % H->size;
120     ctr++;
121   }
122   if(H->entries[pos] == &unallocated)
123      H->fill++; /* only increase fill if the previous element was not yet
124 		 * counted, i.e. unallocated */
125   H->inuse++;
126   H->entries[pos] = E;
127   if(hint)
128 	  *hint = pos;
129   return 0;
130 }
131 
rehash(T_HashTable * H)132 static int rehash(T_HashTable *H)
133 {
134   size_t size,i;
135   T_HashTableEl *oldentries;
136   /* resize the table */
137 
138   size = H->size;
139   oldentries = H->entries;
140   if(alloc_ht(H,((H->inuse+1)*4+H->fill)/5))
141 	  return -1;
142 
143   for(i=0; i < size; i++){
144     if(oldentries[i] != &unallocated && oldentries[i] != &deleted)
145       _hash_add(H, oldentries[i], 0);
146   }
147   Free(oldentries);
148   return 0;
149 }
150 
hash_add(T_HashTable * H,T_HashTableEl * E,size_t * hint)151 int hash_add(T_HashTable *H, T_HashTableEl *E, size_t *hint)
152 {
153   if (H->fill >= H->max)
154     rehash(H);
155   if (H->fill == H->size)
156     return -1; /*out of memory error */
157   return _hash_add(H,E, hint);
158 }
159 
160 
161 /* add into hash table without checking for repeats */
_hash_lookup(T_HashTable * H,T_HashTableEl * E,T_HashTableEl ** E2,size_t * hint,int isIdentity)162 static int _hash_lookup(T_HashTable *H,T_HashTableEl *E, T_HashTableEl **E2,
163 			size_t *hint, int isIdentity)
164 {
165 	size_t f2, pos, upos, ttl;
166 
167   pos = H->f1(E) % H->size;
168   ttl = H->size;
169   f2 = (size_t) -1;
170   upos = (size_t) -1;
171   while(ttl &&
172 	H->entries[pos] != &unallocated &&
173 	(H->entries[pos] == &deleted ||
174 	 ((isIdentity || H->compar(H->entries[pos], E) != 0) &&
175 	  (!isIdentity || H->entries[pos] != E)))){
176     if (f2 == (size_t) -1)
177       f2 = H->f2(E) % (H->size - 1);
178     if (upos == (size_t) -1 && H->entries[pos] == &deleted)
179       upos = pos;
180     pos = (pos+f2+1) % H->size;
181     ttl--;
182   }
183   if(H->entries[pos] == &unallocated || !ttl)
184     return -1;
185   if (upos != (size_t) -1){
186     H->entries[upos] = H->entries[pos];
187     H->entries[pos] = &deleted;
188     pos = upos;
189   }
190   if(hint)
191     *hint = pos;
192   *E2= H->entries[pos];
193   return 0;
194 }
195 
196 
hash_lookup(T_HashTable * H,T_HashTableEl * E,T_HashTableEl ** E2,size_t * hint)197 int hash_lookup(T_HashTable *H,T_HashTableEl *E, T_HashTableEl **E2,
198 		size_t *hint)
199 {
200 	return _hash_lookup(H, E, E2, hint, 0);
201 }
202 
203 /* add into hash table without checking for repeats */
hash_remove(T_HashTable * H,T_HashTableEl * E,size_t hint)204 int hash_remove(T_HashTable *H,T_HashTableEl *E, size_t hint)
205 {
206   T_HashTableEl *E2;
207 
208   if (hint >=0 && hint < H->size &&
209       H->entries[hint] == E){
210     H->inuse--;
211     H->entries[hint] = &deleted;
212     return 0;
213   }
214 
215   if(_hash_lookup(H, E, &E2, &hint, 1)) {
216 	  fprintf(stderr, "Removing non-existent entry\n");
217 	  exit(1);
218   }
219   H->inuse--;
220   H->entries[hint] = &deleted;
221   return 0;
222 }
223