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