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  * MAPS INTEGERS TO INTEGERS
21  */
22 
23 /*
24  * Keys are 32bit non-negative integers. Values are signed 32bit integers.
25  */
26 
27 #include <assert.h>
28 #include <stdbool.h>
29 
30 #include "utils/memalloc.h"
31 #include "utils/pair_hash_map.h"
32 
33 
34 /*
35  * For debugging: check whether n is a power of two
36  */
37 #ifndef NDEBUG
is_power_of_two(uint32_t n)38 static bool is_power_of_two(uint32_t n) {
39   return (n & (n - 1)) == 0;
40 }
41 #endif
42 
43 
44 /*
45  * Initialization:
46  * - n = initial size, must be a power of 2
47  * - if n = 0, the default size is used
48  */
init_pmap(pmap_t * hmap,uint32_t n)49 void init_pmap(pmap_t *hmap, uint32_t n) {
50   uint32_t i;
51   pmap_rec_t *tmp;
52 
53   if (n == 0) {
54     n = PMAP_DEFAULT_SIZE;
55   }
56 
57   if (n >= PMAP_MAX_SIZE) {
58     out_of_memory();
59   }
60 
61   assert(is_power_of_two(n));
62   tmp = (pmap_rec_t *) safe_malloc(n * sizeof(pmap_rec_t));
63   for (i=0; i<n; i++) {
64     tmp[i].val = NULL;
65   }
66 
67   hmap->data = tmp;
68   hmap->size = n;
69   hmap->nelems = 0;
70   hmap->ndeleted = 0;
71 
72   hmap->resize_threshold = (uint32_t)(n * PMAP_RESIZE_RATIO);
73   hmap->cleanup_threshold = (uint32_t) (n * PMAP_CLEANUP_RATIO);
74 }
75 
76 
77 /*
78  * Free memory
79  */
delete_pmap(pmap_t * hmap)80 void delete_pmap(pmap_t *hmap) {
81   safe_free(hmap->data);
82   hmap->data = NULL;
83 }
84 
85 
86 /*
87  * Hash of a pair k0, k1 based on Jenkins lookup3 code.
88  * (public domain code, see http://www.burtleburtle.net)
89  */
90 #define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
91 
92 #define final(x,y,z)      \
93 {                         \
94   z ^= y; z -= rot(y,14); \
95   x ^= z; x -= rot(z,11); \
96   y ^= x; y -= rot(x,25); \
97   z ^= y; z -= rot(y,16); \
98   x ^= z; x -= rot(z,4);  \
99   y ^= x; y -= rot(x,14); \
100   z ^= y; z -= rot(y,24); \
101 }
102 
hash_pair(int32_t k0,int32_t k1)103 static uint32_t hash_pair(int32_t k0, int32_t k1) {
104   uint32_t x, y, z;
105 
106   x = (uint32_t) k0;
107   y = (uint32_t) k1;
108   z = 0xdeadbeef;
109   final(x, y, z);
110 
111   return z;
112 }
113 
114 
115 /*
116  * Check whether d is a valid record: non-null, not deleted
117  */
valid_record(pmap_rec_t * r)118 static inline bool valid_record(pmap_rec_t *r) {
119   return r->val != NULL && r->val != DELETED_PTR;
120 }
121 
122 
123 /*
124  * Make a copy of record d in a clean array data
125  * - mask = size of data - 1 (size must be a power of 2)
126  */
pmap_clean_copy(pmap_rec_t * data,pmap_rec_t * d,uint32_t mask)127 static void pmap_clean_copy(pmap_rec_t *data, pmap_rec_t *d, uint32_t mask) {
128   uint32_t j;
129 
130   j = hash_pair(d->k0, d->k1) & mask;
131   while (data[j].val != NULL) {
132     j ++;
133     j &= mask;
134   }
135 
136   data[j].k0 = d->k0;
137   data[j].k1 = d->k1;
138   data[j].val = d->val;
139 }
140 
141 
142 /*
143  * Remove deleted records
144  */
pmap_cleanup(pmap_t * hmap)145 static void pmap_cleanup(pmap_t *hmap) {
146   pmap_rec_t *tmp, *d;
147   uint32_t j, n, mask;
148 
149   n = hmap->size;
150   tmp = (pmap_rec_t *) safe_malloc(n * sizeof(pmap_rec_t));
151   for (j=0; j<n; j++) {
152     tmp[j].val = NULL;
153   }
154 
155   mask = n - 1;
156   d = hmap->data;
157   for (j=0; j<n; j++) {
158     if (valid_record(d)) {
159       pmap_clean_copy(tmp, d, mask);
160     }
161     d++;
162   }
163 
164   safe_free(hmap->data);
165   hmap->data = tmp;
166   hmap->ndeleted = 0;
167 }
168 
169 
170 /*
171  * Remove deleted records and make the table twice as large
172  */
pmap_extend(pmap_t * hmap)173 static void pmap_extend(pmap_t *hmap) {
174   pmap_rec_t *tmp, *d;
175   uint32_t j, n, n2, mask;
176 
177   n = hmap->size;
178   n2 = n << 1;
179   if (n2 >= PMAP_MAX_SIZE) {
180     out_of_memory();
181   }
182 
183   tmp = (pmap_rec_t *) safe_malloc(n2 * sizeof(pmap_rec_t));
184   for (j=0; j<n2; j++) {
185     tmp[j].val = NULL;
186   }
187 
188   mask = n2 - 1;
189   d = hmap->data;
190   for (j=0; j<n; j++) {
191     if (valid_record(d)) {
192       pmap_clean_copy(tmp, d, mask);
193     }
194     d ++;
195   }
196 
197   safe_free(hmap->data);
198   hmap->data = tmp;
199   hmap->size = n2;
200   hmap->ndeleted = 0;
201 
202   hmap->resize_threshold = (uint32_t)(n2 * PMAP_RESIZE_RATIO);
203   hmap->cleanup_threshold = (uint32_t)(n2 * PMAP_CLEANUP_RATIO);
204 }
205 
206 
207 /*
208  * Find record with key (k0, k1)
209  * - return NULL if that key is not in the table
210  */
pmap_find(pmap_t * hmap,int32_t k0,int32_t k1)211 pmap_rec_t *pmap_find(pmap_t *hmap, int32_t k0, int32_t k1) {
212   uint32_t mask, j;
213   pmap_rec_t *d;
214 
215   mask = hmap->size - 1;
216   j = hash_pair(k0, k1) & mask;
217   for (;;) {
218     d = hmap->data + j;
219     if (d->val == NULL) return NULL;
220     if (d->val != DELETED_PTR && d->k0 == k0 && d->k1 == k1) {
221       return d;
222     }
223     j ++;
224     j &= mask;
225   }
226 }
227 
228 
229 /*
230  * Add a fresh record with key (k0, k1) after hmap was extended
231  * - there are no record with this key in hmap
232  * - there are no deleted record
233  */
pmap_get_clean(pmap_t * hmap,int32_t k0,int32_t k1)234 static pmap_rec_t *pmap_get_clean(pmap_t *hmap, int32_t k0, int32_t k1) {
235   uint32_t j, mask;
236   pmap_rec_t *d;
237 
238   mask = hmap->size - 1;
239   j = hash_pair(k0, k1) & mask;
240   for (;;) {
241     d = hmap->data + j;
242     if (d->val == NULL) {
243       hmap->nelems ++;
244       d->k0 = k0;
245       d->k1 = k1;
246       d->val = DEFAULT_PTR;
247       return d;
248     }
249     j ++;
250     j &= mask;
251   }
252 }
253 
254 
255 /*
256  * Find or add record with key (k0, k1)
257  */
pmap_get(pmap_t * hmap,int32_t k0,int32_t k1)258 pmap_rec_t *pmap_get(pmap_t *hmap, int32_t k0, int32_t k1) {
259   uint32_t mask, j;
260   pmap_rec_t *d, *aux;
261 
262   assert(hmap->size > hmap->ndeleted + hmap->nelems);
263 
264   mask = hmap->size - 1;
265   j = hash_pair(k0, k1) & mask;
266 
267   for (;;) {
268     d = hmap->data + j;
269     if (d->val == NULL || d->val == DELETED_PTR) break;
270     if (d->k0 == k0 && d->k1 == k1) return d;
271     j ++;
272     j &= mask;
273   }
274 
275   aux = d; // new record, if needed, will be aux
276   while (d->val != NULL) {
277     j ++;
278     j &= mask;
279     if (d->val != DELETED_PTR && d->k0 == k0 && d->k1 == k1) return d;
280   }
281 
282   if (aux->val == DELETED_PTR){
283     assert(hmap->ndeleted > 0);
284     hmap->ndeleted --;
285   }
286 
287   if (hmap->nelems + hmap->ndeleted >= hmap->resize_threshold) {
288     pmap_extend(hmap);
289     aux = pmap_get_clean(hmap, k0, k1);
290   } else {
291     aux->k0 = k0;
292     aux->k1 = k1;
293     aux->val = DEFAULT_PTR;
294     hmap->nelems ++;
295   }
296 
297   return aux;
298 }
299 
300 
301 /*
302  * Erase record r
303  */
pmap_erase(pmap_t * hmap,pmap_rec_t * r)304 void pmap_erase(pmap_t *hmap, pmap_rec_t *r) {
305   assert(pmap_find(hmap, r->k0, r->k1) == r);
306 
307   r->val = DELETED_PTR;
308   hmap->nelems --;
309   hmap->ndeleted ++;
310   if (hmap->ndeleted > hmap->cleanup_threshold) {
311     pmap_cleanup(hmap);
312   }
313 }
314 
315 
316 /*
317  * Empty the map
318  */
pmap_reset(pmap_t * hmap)319 void pmap_reset(pmap_t *hmap) {
320   uint32_t i, n;
321   pmap_rec_t *d;
322 
323   n = hmap->size;
324   d = hmap->data;
325   for (i=0; i<n; i++) {
326     d->val = NULL;
327     d ++;
328   }
329 
330   hmap->nelems = 0;
331   hmap->ndeleted = 0;
332 }
333 
334 /*
335  * First non-empty record in the table, starting from p
336  */
pmap_get_next(pmap_t * hmap,pmap_rec_t * p)337 static pmap_rec_t *pmap_get_next(pmap_t *hmap, pmap_rec_t *p) {
338   pmap_rec_t *end;
339 
340   end = hmap->data + hmap->size;
341   while (p < end) {
342     if (valid_record(p)) return p;
343     p ++;
344   }
345 
346   return NULL;
347 }
348 
349 
350 /*
351  * Get the first non-empty record or NULL if the table is empty
352  */
pmap_first_record(pmap_t * hmap)353 pmap_rec_t *pmap_first_record(pmap_t *hmap) {
354   return pmap_get_next(hmap, hmap->data);
355 }
356 
357 
358 /*
359  * Next record after p or NULL
360  */
pmap_next_record(pmap_t * hmap,pmap_rec_t * p)361 pmap_rec_t *pmap_next_record(pmap_t *hmap, pmap_rec_t *p) {
362   assert(p != NULL && p<hmap->data + hmap->size && valid_record(p));
363   return pmap_get_next(hmap, p+1);
364 }
365 
366 
367