1 /*
2  * This file is part of the Yices SMT Solver.
3  * Copyright (C) 2017 SRI International.
4  *
5  * Yices is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * Yices is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with Yices.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 /*
20  * HASH TABLES OF NON-NEGATIVE INTEGERS FOR HASH CONSING.
21  */
22 
23 #include <stdint.h>
24 #include <assert.h>
25 
26 #include "utils/int_hash_tables.h"
27 #include "utils/memalloc.h"
28 
29 
30 /*
31  * For debugging: check whether n is a power of two
32  */
33 #ifndef NDEBUG
is_power_of_two(uint32_t n)34 static bool is_power_of_two(uint32_t n) {
35   return (n & (n - 1)) == 0;
36 }
37 #endif
38 
39 
40 
41 /*
42  * Initialize table: n = initial size (must be a power of 2)
43  * If n = 0 set size = default value
44  */
init_int_htbl(int_htbl_t * table,uint32_t n)45 void init_int_htbl(int_htbl_t *table, uint32_t n) {
46   uint32_t i;
47   int_hrec_t *tmp;
48 
49   if (n == 0) {
50     n = INT_HTBL_DEFAULT_SIZE;
51   }
52 
53   if (n >= MAX_HTBL_SIZE) {
54     out_of_memory(); // abort
55   }
56 
57   assert(is_power_of_two(n));
58 
59   tmp = (int_hrec_t *) safe_malloc(n * sizeof(int_hrec_t));
60   for (i=0; i<n; i++) {
61     tmp[i].value = NULL_VALUE;
62   }
63 
64   table->records = tmp;
65   table->size = n;
66   table->nelems = 0;
67   table->ndeleted = 0;
68 
69   table->resize_threshold = (uint32_t)(n * RESIZE_RATIO);
70   table->cleanup_threshold = (uint32_t)(n * CLEANUP_RATIO);
71 }
72 
73 
74 /*
75  * Delete table
76  */
delete_int_htbl(int_htbl_t * table)77 void delete_int_htbl(int_htbl_t *table) {
78   safe_free(table->records);
79   table->records = NULL;
80 }
81 
82 
83 /*
84  * Reset table: remove all elements
85  */
reset_int_htbl(int_htbl_t * table)86 void reset_int_htbl(int_htbl_t *table) {
87   uint32_t i, n;
88 
89   n = table->size;
90   for (i=0; i<n; i++) {
91     table->records[i].value = NULL_VALUE;
92   }
93 
94   table->nelems = 0;
95   table->ndeleted = 0;
96 }
97 
98 
99 
100 
101 /*
102  * Copy record <k, v> into a clean record array t.
103  * t contains no DELETED_VALUE and must have at least one empty slot
104  * <k, v> must not be present in t.
105  * mask must be 2^n - 1 and (size of t) = 2^n
106  */
int_htbl_copy_record(int_hrec_t * t,uint32_t k,int32_t v,uint32_t mask)107 static void int_htbl_copy_record(int_hrec_t *t, uint32_t k, int32_t v, uint32_t mask) {
108   uint32_t j;
109   int_hrec_t *r;
110 
111   j = k & mask;
112   for (;;) {
113     r = t + j;
114     if (r->value == NULL_VALUE) {
115       r->value = v;
116       r->key = k;
117       return;
118     }
119     j ++;
120     j &= mask;
121   }
122 }
123 
124 
125 /*
126  * Remove deleted elements
127  */
int_htbl_cleanup(int_htbl_t * table)128 static void int_htbl_cleanup(int_htbl_t *table) {
129   int_hrec_t *tmp;
130   uint32_t j, n;
131   uint32_t mask;
132   int32_t v;
133 
134   n = table->size;
135   tmp = (int_hrec_t *) safe_malloc(n * sizeof(int_hrec_t));
136   for (j=0; j<n; j++) {
137     tmp[j].value = NULL_VALUE;
138   }
139 
140   mask = n - 1;
141   for (j=0; j<n; j++) {
142     v = table->records[j].value;
143     if (v >= 0) {
144       int_htbl_copy_record(tmp, table->records[j].key, v, mask);
145     }
146   }
147 
148   safe_free(table->records);
149   table->records = tmp;
150   table->ndeleted = 0;
151 }
152 
153 
154 
155 /*
156  * Remove deleted elements and make table twice as large
157  */
int_htbl_extend(int_htbl_t * table)158 static void int_htbl_extend(int_htbl_t *table) {
159   int_hrec_t *tmp;
160   uint32_t j, n, n2;
161   uint32_t mask;
162   int32_t v;
163 
164   n = table->size;
165   n2 = n<<1;
166   if (n2 == 0 || n2 >= MAX_HTBL_SIZE) {
167     // overflow or too large
168     out_of_memory();
169   }
170 
171   tmp = (int_hrec_t *) safe_malloc(n2 * sizeof(int_hrec_t));
172   for (j=0; j<n2; j++) {
173     tmp[j].value = NULL_VALUE;
174   }
175   mask = n2 - 1;
176 
177   for (j=0; j<n; j++) {
178     v = table->records[j].value;
179     if (v >= 0) {
180       int_htbl_copy_record(tmp, table->records[j].key, v, mask);
181     }
182   }
183 
184   safe_free(table->records);
185   table->records = tmp;
186   table->ndeleted = 0;
187   table->size = n2;
188 
189   // keep same fill/cleanup ratios
190   table->resize_threshold = (uint32_t) (n2 * RESIZE_RATIO);
191   table->cleanup_threshold = (uint32_t) (n2 * CLEANUP_RATIO);
192 }
193 
194 
195 /*
196  * Erase <k, v>
197  */
int_htbl_erase_record(int_htbl_t * table,uint32_t k,int32_t v)198 void int_htbl_erase_record(int_htbl_t *table, uint32_t k, int32_t v) {
199   uint32_t mask, j;
200   int_hrec_t *r;
201 
202   // table must not be full, otherwise the function loops
203   assert(table->size > table->nelems + table->ndeleted);
204 
205   mask = table->size - 1;
206   j = k & mask;
207   for (;;) {
208     r = table->records + j;
209     if (r->value == v) break;
210     if (r->value == NULL_VALUE) return;
211     j ++;
212     j &= mask;
213   }
214 
215   assert(r->key == k && r->value == v);
216   table->nelems --;
217   table->ndeleted ++;
218   r->value = DELETED_VALUE;
219   if (table->ndeleted > table->cleanup_threshold) {
220     int_htbl_cleanup(table);
221   }
222 }
223 
224 
225 /*
226  * Add record <k, v> to the table
227  * - the record must not be present in the table
228  */
int_htbl_add_record(int_htbl_t * table,uint32_t k,int32_t v)229 void int_htbl_add_record(int_htbl_t *table, uint32_t k, int32_t v) {
230   uint32_t mask, j;
231   int_hrec_t *r;
232 
233   assert(table->size > table->nelems + table->ndeleted);
234 
235   mask = table->size - 1;
236   j = k & mask;
237   for (;;) {
238     r = table->records + j;
239     if (r->value == NULL_VALUE) goto add;
240     if (r->value == DELETED_VALUE) break;
241     assert(r->value != v);
242     j ++;
243     j &= mask;
244   }
245 
246   assert(table->ndeleted > 0);
247   table->ndeleted --;
248 
249  add:
250   // add <k, v> into record r
251   table->nelems ++;
252   r->key = k;
253   r->value = v;
254 
255   if (table->nelems + table->ndeleted > table->resize_threshold) {
256     int_htbl_extend(table);
257   }
258 }
259 
260 
261 /*
262  * Find index of object equal to o or return -1 if no such index is in the hash table.
263  */
int_htbl_find_obj(int_htbl_t * table,int_hobj_t * o)264 int32_t int_htbl_find_obj(int_htbl_t *table, int_hobj_t *o) {
265   uint32_t mask, j, k;
266   int32_t v;
267   int_hrec_t *r;
268 
269   // the table must not be full, otherwise, the function loops
270   assert(table->size > table->nelems + table->ndeleted);
271 
272   mask = table->size - 1;
273   k = o->hash(o);
274   j = k & mask;
275   for (;;) {
276     r = table->records + j;
277     v = r->value;
278     if ((v >= 0 && r->key == k && o->eq(o, v)) || v == NULL_VALUE) {
279       return v;
280     }
281     j ++;
282     j &= mask;
283   }
284 }
285 
286 
287 /*
288  * Allocate an index for o (by calling build) then store this index and k in
289  * record r. k must be the hash code of o.
290  */
int_htbl_store_new_obj(int_htbl_t * table,int_hrec_t * r,uint32_t k,int_hobj_t * o)291 static int32_t int_htbl_store_new_obj(int_htbl_t *table, int_hrec_t *r, uint32_t k, int_hobj_t *o) {
292   int32_t v;
293 
294   v = o->build(o);
295 
296   // error in build is signaled by returning v < 0
297   if (v >= 0) {
298     table->nelems ++;
299     r->key = k;
300     r->value = v;
301 
302     if (table->nelems + table->ndeleted > table->resize_threshold) {
303       int_htbl_extend(table);
304     }
305   }
306 
307   return v;
308 }
309 
310 
311 /*
312  * Get index of an object equal to o if such an index is in the table.
313  * Otherwise, allocate an index by calling o->build(o) then store that index
314  * in the table.
315  */
int_htbl_get_obj(int_htbl_t * table,int_hobj_t * o)316 int32_t int_htbl_get_obj(int_htbl_t *table, int_hobj_t *o) {
317   uint32_t mask, j, k;
318   int32_t v;
319   int_hrec_t *r;
320   int_hrec_t *aux;
321 
322   assert(table->size > table->nelems + table->ndeleted);
323 
324   mask = table->size - 1;
325   k = o->hash(o);
326   j = k & mask;
327 
328   for (;;) {
329     r = table->records + j;
330     v = r->value;
331     if (v == NULL_VALUE) return int_htbl_store_new_obj(table, r, k, o);
332     if (v == DELETED_VALUE) break;
333     if (r->key == k && o->eq(o, v)) return v;
334     j ++;
335     j &= mask;
336   }
337 
338   // aux will be used to store the new index if needed
339   aux = r;
340   for (;;) {
341     j ++;
342     j &= mask;
343     r = table->records + j;
344     v = r->value;
345     if (v == NULL_VALUE) {
346       table->ndeleted --;
347       return int_htbl_store_new_obj(table, aux, k, o);
348     }
349     if (v >= 0 && r->key == k && o->eq(o, v)) return v;
350   }
351 }
352 
353 
354