1 /*
2  * Copyright 2011      INRIA Saclay
3  * Copyright 2013      Ecole Normale Superieure
4  *
5  * Use of this software is governed by the MIT license
6  *
7  * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
8  * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
9  * 91893 Orsay, France
10  * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France
11  */
12 
13 #include <isl/ctx.h>
14 #include <isl/hash.h>
15 
16 #define ISL_xCAT(A,B) A ## B
17 #define ISL_CAT(A,B) ISL_xCAT(A,B)
18 #define ISL_xFN(TYPE,NAME) TYPE ## _ ## NAME
19 #define ISL_FN(TYPE,NAME) ISL_xFN(TYPE,NAME)
20 #define ISL_xS(TYPE1,TYPE2,NAME) struct isl_ ## TYPE1 ## _ ## TYPE2 ## _ ## NAME
21 #define ISL_yS(TYPE1,TYPE2,NAME) ISL_xS(TYPE1,TYPE2,NAME)
22 #define ISL_S(NAME) ISL_yS(ISL_KEY,ISL_VAL,NAME)
23 
24 struct ISL_HMAP {
25 	int ref;
26 	isl_ctx *ctx;
27 	struct isl_hash_table table;
28 };
29 
ISL_S(pair)30 ISL_S(pair) {
31 	ISL_KEY *key;
32 	ISL_VAL *val;
33 };
34 
ISL_FN(ISL_HMAP,alloc)35 __isl_give ISL_HMAP *ISL_FN(ISL_HMAP,alloc)(isl_ctx *ctx, int min_size)
36 {
37 	ISL_HMAP *hmap;
38 
39 	hmap = isl_calloc_type(ctx, ISL_HMAP);
40 	if (!hmap)
41 		return NULL;
42 
43 	hmap->ctx = ctx;
44 	isl_ctx_ref(ctx);
45 	hmap->ref = 1;
46 
47 	if (isl_hash_table_init(ctx, &hmap->table, min_size) < 0)
48 		return ISL_FN(ISL_HMAP,free)(hmap);
49 
50 	return hmap;
51 }
52 
free_pair(void ** entry,void * user)53 static isl_stat free_pair(void **entry, void *user)
54 {
55 	ISL_S(pair) *pair = *entry;
56 	ISL_FN(ISL_KEY,free)(pair->key);
57 	ISL_FN(ISL_VAL,free)(pair->val);
58 	free(pair);
59 	*entry = NULL;
60 	return isl_stat_ok;
61 }
62 
ISL_FN(ISL_HMAP,free)63 __isl_null ISL_HMAP *ISL_FN(ISL_HMAP,free)(__isl_take ISL_HMAP *hmap)
64 {
65 	if (!hmap)
66 		return NULL;
67 	if (--hmap->ref > 0)
68 		return NULL;
69 	isl_hash_table_foreach(hmap->ctx, &hmap->table, &free_pair, NULL);
70 	isl_hash_table_clear(&hmap->table);
71 	isl_ctx_deref(hmap->ctx);
72 	free(hmap);
73 	return NULL;
74 }
75 
ISL_FN(ISL_HMAP,get_ctx)76 isl_ctx *ISL_FN(ISL_HMAP,get_ctx)(__isl_keep ISL_HMAP *hmap)
77 {
78 	return hmap ? hmap->ctx : NULL;
79 }
80 
81 /* Add a mapping from "key" to "val" to the associative array
82  * pointed to by user.
83  */
add_key_val(__isl_take ISL_KEY * key,__isl_take ISL_VAL * val,void * user)84 static isl_stat add_key_val(__isl_take ISL_KEY *key, __isl_take ISL_VAL *val,
85 	void *user)
86 {
87 	ISL_HMAP **hmap = (ISL_HMAP **) user;
88 
89 	*hmap = ISL_FN(ISL_HMAP,set)(*hmap, key, val);
90 
91 	if (!*hmap)
92 		return isl_stat_error;
93 
94 	return isl_stat_ok;
95 }
96 
ISL_FN(ISL_HMAP,dup)97 __isl_give ISL_HMAP *ISL_FN(ISL_HMAP,dup)(__isl_keep ISL_HMAP *hmap)
98 {
99 	ISL_HMAP *dup;
100 
101 	if (!hmap)
102 		return NULL;
103 
104 	dup = ISL_FN(ISL_HMAP,alloc)(hmap->ctx, hmap->table.n);
105 	if (ISL_FN(ISL_HMAP,foreach)(hmap, &add_key_val, &dup) < 0)
106 		return ISL_FN(ISL_HMAP,free)(dup);
107 
108 	return dup;
109 }
110 
ISL_FN(ISL_HMAP,cow)111 __isl_give ISL_HMAP *ISL_FN(ISL_HMAP,cow)(__isl_take ISL_HMAP *hmap)
112 {
113 	if (!hmap)
114 		return NULL;
115 
116 	if (hmap->ref == 1)
117 		return hmap;
118 	hmap->ref--;
119 	return ISL_FN(ISL_HMAP,dup)(hmap);
120 }
121 
ISL_FN(ISL_HMAP,copy)122 __isl_give ISL_HMAP *ISL_FN(ISL_HMAP,copy)(__isl_keep ISL_HMAP *hmap)
123 {
124 	if (!hmap)
125 		return NULL;
126 
127 	hmap->ref++;
128 	return hmap;
129 }
130 
has_key(const void * entry,const void * c_key)131 static isl_bool has_key(const void *entry, const void *c_key)
132 {
133 	const ISL_S(pair) *pair = entry;
134 	ISL_KEY *key = (ISL_KEY *) c_key;
135 
136 	return ISL_KEY_IS_EQUAL(pair->key, key);
137 }
138 
139 /* If "hmap" contains a value associated to "key", then return
140  * (isl_bool_true, copy of value).
141  * Otherwise, return
142  * (isl_bool_false, NULL).
143  * If an error occurs, then return
144  * (isl_bool_error, NULL).
145  */
ISL_MAYBE(ISL_VAL)146 __isl_give ISL_MAYBE(ISL_VAL) ISL_FN(ISL_HMAP,try_get)(
147 	__isl_keep ISL_HMAP *hmap, __isl_keep ISL_KEY *key)
148 {
149 	struct isl_hash_table_entry *entry;
150 	ISL_S(pair) *pair;
151 	uint32_t hash;
152 	ISL_MAYBE(ISL_VAL) res = { isl_bool_false, NULL };
153 
154 	if (!hmap || !key)
155 		goto error;
156 
157 	hash = ISL_FN(ISL_KEY,get_hash)(key);
158 	entry = isl_hash_table_find(hmap->ctx, &hmap->table, hash,
159 					&has_key, key, 0);
160 
161 	if (!entry)
162 		goto error;
163 	if (entry == isl_hash_table_entry_none)
164 		return res;
165 
166 	pair = entry->data;
167 
168 	res.valid = isl_bool_true;
169 	res.value = ISL_FN(ISL_VAL,copy)(pair->val);
170 	if (!res.value)
171 		res.valid = isl_bool_error;
172 	return res;
173 error:
174 	res.valid = isl_bool_error;
175 	res.value = NULL;
176 	return res;
177 }
178 
179 /* If "hmap" contains a value associated to "key", then return
180  * isl_bool_true.  Otherwise, return isl_bool_false.
181  * Return isl_bool_error on error.
182  */
ISL_FN(ISL_HMAP,has)183 isl_bool ISL_FN(ISL_HMAP,has)(__isl_keep ISL_HMAP *hmap,
184 	__isl_keep ISL_KEY *key)
185 {
186 	ISL_MAYBE(ISL_VAL) res;
187 
188 	res = ISL_FN(ISL_HMAP,try_get)(hmap, key);
189 	ISL_FN(ISL_VAL,free)(res.value);
190 
191 	return res.valid;
192 }
193 
194 /* If "hmap" contains a value associated to "key", then return
195  * a copy of that value.  Otherwise, return NULL.
196  * Return NULL on error.
197  */
ISL_FN(ISL_HMAP,get)198 __isl_give ISL_VAL *ISL_FN(ISL_HMAP,get)(__isl_keep ISL_HMAP *hmap,
199 	__isl_take ISL_KEY *key)
200 {
201 	ISL_VAL *res;
202 
203 	res = ISL_FN(ISL_HMAP,try_get)(hmap, key).value;
204 	ISL_FN(ISL_KEY,free)(key);
205 	return res;
206 }
207 
208 /* Remove the mapping between "key" and its associated value (if any)
209  * from "hmap".
210  *
211  * If "key" is not mapped to anything, then we leave "hmap" untouched"
212  */
ISL_FN(ISL_HMAP,drop)213 __isl_give ISL_HMAP *ISL_FN(ISL_HMAP,drop)(__isl_take ISL_HMAP *hmap,
214 	__isl_take ISL_KEY *key)
215 {
216 	struct isl_hash_table_entry *entry;
217 	ISL_S(pair) *pair;
218 	uint32_t hash;
219 
220 	if (!hmap || !key)
221 		goto error;
222 
223 	hash = ISL_FN(ISL_KEY,get_hash)(key);
224 	entry = isl_hash_table_find(hmap->ctx, &hmap->table, hash,
225 					&has_key, key, 0);
226 	if (!entry)
227 		goto error;
228 	if (entry == isl_hash_table_entry_none) {
229 		ISL_FN(ISL_KEY,free)(key);
230 		return hmap;
231 	}
232 
233 	hmap = ISL_FN(ISL_HMAP,cow)(hmap);
234 	if (!hmap)
235 		goto error;
236 	entry = isl_hash_table_find(hmap->ctx, &hmap->table, hash,
237 					&has_key, key, 0);
238 	ISL_FN(ISL_KEY,free)(key);
239 
240 	if (!entry)
241 		return ISL_FN(ISL_HMAP,free)(hmap);
242 	if (entry == isl_hash_table_entry_none)
243 		isl_die(hmap->ctx, isl_error_internal,
244 			"missing entry" , return ISL_FN(ISL_HMAP,free)(hmap));
245 
246 	pair = entry->data;
247 	isl_hash_table_remove(hmap->ctx, &hmap->table, entry);
248 	ISL_FN(ISL_KEY,free)(pair->key);
249 	ISL_FN(ISL_VAL,free)(pair->val);
250 	free(pair);
251 
252 	return hmap;
253 error:
254 	ISL_FN(ISL_KEY,free)(key);
255 	ISL_FN(ISL_HMAP,free)(hmap);
256 	return NULL;
257 }
258 
259 /* Add a mapping from "key" to "val" to "hmap".
260  * If "key" was already mapped to something else, then that mapping
261  * is replaced.
262  * If key happened to be mapped to "val" already, then we leave
263  * "hmap" untouched.
264  */
ISL_FN(ISL_HMAP,set)265 __isl_give ISL_HMAP *ISL_FN(ISL_HMAP,set)(__isl_take ISL_HMAP *hmap,
266 	__isl_take ISL_KEY *key, __isl_take ISL_VAL *val)
267 {
268 	struct isl_hash_table_entry *entry;
269 	ISL_S(pair) *pair;
270 	uint32_t hash;
271 
272 	if (!hmap || !key || !val)
273 		goto error;
274 
275 	hash = ISL_FN(ISL_KEY,get_hash)(key);
276 	entry = isl_hash_table_find(hmap->ctx, &hmap->table, hash,
277 					&has_key, key, 0);
278 	if (!entry)
279 		goto error;
280 	if (entry != isl_hash_table_entry_none) {
281 		isl_bool equal;
282 		pair = entry->data;
283 		equal = ISL_VAL_IS_EQUAL(pair->val, val);
284 		if (equal < 0)
285 			goto error;
286 		if (equal) {
287 			ISL_FN(ISL_KEY,free)(key);
288 			ISL_FN(ISL_VAL,free)(val);
289 			return hmap;
290 		}
291 	}
292 
293 	hmap = ISL_FN(ISL_HMAP,cow)(hmap);
294 	if (!hmap)
295 		goto error;
296 
297 	entry = isl_hash_table_find(hmap->ctx, &hmap->table, hash,
298 					&has_key, key, 1);
299 
300 	if (!entry)
301 		goto error;
302 
303 	if (entry->data) {
304 		pair = entry->data;
305 		ISL_FN(ISL_VAL,free)(pair->val);
306 		pair->val = val;
307 		ISL_FN(ISL_KEY,free)(key);
308 		return hmap;
309 	}
310 
311 	pair = isl_alloc_type(hmap->ctx, ISL_S(pair));
312 	if (!pair)
313 		goto error;
314 
315 	entry->data = pair;
316 	pair->key = key;
317 	pair->val = val;
318 	return hmap;
319 error:
320 	ISL_FN(ISL_KEY,free)(key);
321 	ISL_FN(ISL_VAL,free)(val);
322 	return ISL_FN(ISL_HMAP,free)(hmap);
323 }
324 
325 /* Internal data structure for isl_map_to_basic_set_foreach.
326  *
327  * fn is the function that should be called on each entry.
328  * user is the user-specified final argument to fn.
329  */
ISL_S(foreach_data)330 ISL_S(foreach_data) {
331 	isl_stat (*fn)(__isl_take ISL_KEY *key, __isl_take ISL_VAL *val,
332 		void *user);
333 	void *user;
334 };
335 
336 /* Call data->fn on a copy of the key and value in *entry.
337  */
call_on_copy(void ** entry,void * user)338 static isl_stat call_on_copy(void **entry, void *user)
339 {
340 	ISL_S(pair) *pair = *entry;
341 	ISL_S(foreach_data) *data = (ISL_S(foreach_data) *) user;
342 
343 	return data->fn(ISL_FN(ISL_KEY,copy)(pair->key),
344 			ISL_FN(ISL_VAL,copy)(pair->val), data->user);
345 }
346 
347 /* Call "fn" on each pair of key and value in "hmap".
348  */
ISL_FN(ISL_HMAP,foreach)349 isl_stat ISL_FN(ISL_HMAP,foreach)(__isl_keep ISL_HMAP *hmap,
350 	isl_stat (*fn)(__isl_take ISL_KEY *key, __isl_take ISL_VAL *val,
351 		void *user),
352 	void *user)
353 {
354 	ISL_S(foreach_data) data = { fn, user };
355 
356 	if (!hmap)
357 		return isl_stat_error;
358 
359 	return isl_hash_table_foreach(hmap->ctx, &hmap->table,
360 				      &call_on_copy, &data);
361 }
362 
363 /* Internal data structure for print_pair.
364  *
365  * p is the printer on which the associative array is being printed.
366  * first is set if the current key-value pair is the first to be printed.
367  */
ISL_S(print_data)368 ISL_S(print_data) {
369 	isl_printer *p;
370 	int first;
371 };
372 
373 /* Print the given key-value pair to data->p.
374  */
print_pair(__isl_take ISL_KEY * key,__isl_take ISL_VAL * val,void * user)375 static isl_stat print_pair(__isl_take ISL_KEY *key, __isl_take ISL_VAL *val,
376 	void *user)
377 {
378 	ISL_S(print_data) *data = user;
379 
380 	if (!data->first)
381 		data->p = isl_printer_print_str(data->p, ", ");
382 	data->p = ISL_KEY_PRINT(data->p, key);
383 	data->p = isl_printer_print_str(data->p, ": ");
384 	data->p = ISL_VAL_PRINT(data->p, val);
385 	data->first = 0;
386 
387 	ISL_FN(ISL_KEY,free)(key);
388 	ISL_FN(ISL_VAL,free)(val);
389 	return isl_stat_ok;
390 }
391 
392 /* Print the associative array to "p".
393  */
ISL_FN(isl_printer_print,ISL_HMAP_SUFFIX)394 __isl_give isl_printer *ISL_FN(isl_printer_print,ISL_HMAP_SUFFIX)(
395 	__isl_take isl_printer *p, __isl_keep ISL_HMAP *hmap)
396 {
397 	ISL_S(print_data) data;
398 
399 	if (!p || !hmap)
400 		return isl_printer_free(p);
401 
402 	p = isl_printer_print_str(p, "{");
403 	data.p = p;
404 	data.first = 1;
405 	if (ISL_FN(ISL_HMAP,foreach)(hmap, &print_pair, &data) < 0)
406 		data.p = isl_printer_free(data.p);
407 	p = data.p;
408 	p = isl_printer_print_str(p, "}");
409 
410 	return p;
411 }
412 
ISL_FN(ISL_HMAP,dump)413 void ISL_FN(ISL_HMAP,dump)(__isl_keep ISL_HMAP *hmap)
414 {
415 	isl_printer *printer;
416 
417 	if (!hmap)
418 		return;
419 
420 	printer = isl_printer_to_file(ISL_FN(ISL_HMAP,get_ctx)(hmap), stderr);
421 	printer = ISL_FN(isl_printer_print,ISL_HMAP_SUFFIX)(printer, hmap);
422 	printer = isl_printer_end_line(printer);
423 
424 	isl_printer_free(printer);
425 }
426