blob: 7e9fcfadab180033b1d307ed91ac15de88c2f681 [file] [log] [blame]
Petr Machataa11cbed2012-05-05 00:07:38 +02001/*
2 * This file is part of ltrace.
Petr Machatad7e4ca82012-11-28 03:38:47 +01003 * Copyright (C) 2012 Petr Machata, Red Hat Inc.
Petr Machataa11cbed2012-05-05 00:07:38 +02004 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18 * 02110-1301 USA
19 */
20
Juan Cespedes7186e2a2003-01-31 19:56:34 +010021#include <string.h>
Petr Machatad7e4ca82012-11-28 03:38:47 +010022#include <stdlib.h>
23#include <stdio.h>
24#include "dict.h"
Juan Cespedescac15c32003-01-31 18:58:58 +010025
Petr Machatad7e4ca82012-11-28 03:38:47 +010026struct status_bits {
27 unsigned char taken : 1;
28 unsigned char erased : 1;
Juan Cespedescac15c32003-01-31 18:58:58 +010029};
30
Petr Machatad7e4ca82012-11-28 03:38:47 +010031static struct status_bits *
32bitp(struct dict *dict, size_t n)
Petr Machata345c9b52012-02-10 13:10:03 +010033{
Petr Machatad7e4ca82012-11-28 03:38:47 +010034 return VECT_ELEMENT(&dict->status, struct status_bits, n);
Juan Cespedescac15c32003-01-31 18:58:58 +010035}
36
Juan Cespedesf1350522008-12-16 18:19:58 +010037void
Petr Machatad7e4ca82012-11-28 03:38:47 +010038dict_init(struct dict *dict,
39 size_t key_size, size_t value_size,
40 size_t (*hash1)(const void *),
41 int (*eq)(const void *, const void *),
42 size_t (*hash2)(size_t))
43{
44 assert(hash1 != NULL);
45 assert(eq != NULL);
Juan Cespedescac15c32003-01-31 18:58:58 +010046
Petr Machatad7e4ca82012-11-28 03:38:47 +010047 vect_init(&dict->keys, key_size);
48 vect_init(&dict->values, value_size);
49 VECT_INIT(&dict->status, struct status_bits);
50 dict->size = 0;
51
52 dict->hash1 = hash1;
53 dict->hash2 = hash2;
54 dict->eq = eq;
55}
56
57struct clone_data {
58 struct dict *target;
59 int (*clone_key)(void *tgt, const void *src, void *data);
60 int (*clone_value)(void *tgt, const void *src, void *data);
61 void (*dtor_key)(void *tgt, void *data);
62 void (*dtor_value)(void *tgt, void *data);
63 void *data;
64};
65
Petr Machata82fa4c42012-12-05 01:19:42 +010066static enum callback_status
Petr Machatad7e4ca82012-11-28 03:38:47 +010067clone_cb(void *key, void *value, void *data)
68{
69 struct clone_data *clone_data = data;
70
71 char nkey[clone_data->target->keys.elt_size];
72 if (clone_data->clone_key == NULL)
73 memmove(nkey, key, sizeof(nkey));
74 else if (clone_data->clone_key(&nkey, key, clone_data->data) < 0)
75 return CBS_STOP;
76
77 char nvalue[clone_data->target->values.elt_size];
78 if (clone_data->clone_value == NULL) {
79 memmove(nvalue, value, sizeof(nvalue));
80 } else if (clone_data->clone_value(&nvalue, value,
81 clone_data->data) < 0) {
82 fail:
83 if (clone_data->clone_key != NULL)
84 clone_data->dtor_key(&nkey, clone_data->data);
85 return CBS_STOP;
Juan Cespedescac15c32003-01-31 18:58:58 +010086 }
Petr Machatad7e4ca82012-11-28 03:38:47 +010087
88 if (dict_insert(clone_data->target, nkey, nvalue) < 0) {
89 if (clone_data->clone_value != NULL)
90 clone_data->dtor_value(&nvalue, clone_data->data);
91 goto fail;
92 }
93
94 return CBS_CONT;
Juan Cespedescac15c32003-01-31 18:58:58 +010095}
96
Juan Cespedesf1350522008-12-16 18:19:58 +010097int
Petr Machatad7e4ca82012-11-28 03:38:47 +010098dict_clone(struct dict *target, const struct dict *source,
99 int (*clone_key)(void *tgt, const void *src, void *data),
100 void (*dtor_key)(void *tgt, void *data),
101 int (*clone_value)(void *tgt, const void *src, void *data),
102 void (*dtor_value)(void *tgt, void *data),
103 void *data)
104{
105 assert((clone_key != NULL) == (dtor_key != NULL));
106 assert((clone_value != NULL) == (dtor_value != NULL));
Juan Cespedescd8976d2009-05-14 13:47:58 +0200107
Petr Machatad7e4ca82012-11-28 03:38:47 +0100108 dict_init(target, source->keys.elt_size, source->values.elt_size,
109 source->hash1, source->eq, source->hash2);
110 struct clone_data clone_data = {
111 target, clone_key, clone_value, dtor_key, dtor_value, data
112 };
113 if (dict_each((struct dict *)source, NULL,
114 clone_cb, &clone_data) != NULL) {
115 dict_destroy(target, dtor_key, dtor_value, data);
116 return -1;
117 }
118 return 0;
119}
Juan Cespedescd8976d2009-05-14 13:47:58 +0200120
Petr Machatad7e4ca82012-11-28 03:38:47 +0100121size_t
122dict_size(const struct dict *dict)
123{
124 return dict->size;
125}
Juan Cespedescac15c32003-01-31 18:58:58 +0100126
Petr Machatad7e4ca82012-11-28 03:38:47 +0100127int
128dict_empty(const struct dict *dict)
129{
130 return dict->size == 0;
131}
132
133struct destroy_data {
134 void (*dtor_key)(void *tgt, void *data);
135 void (*dtor_value)(void *tgt, void *data);
136 void *data;
137};
138
Petr Machata82fa4c42012-12-05 01:19:42 +0100139static enum callback_status
Petr Machatad7e4ca82012-11-28 03:38:47 +0100140destroy_cb(void *key, void *value, void *data)
141{
142 struct destroy_data *destroy_data = data;
143 if (destroy_data->dtor_key)
144 destroy_data->dtor_key(key, destroy_data->data);
145 if (destroy_data->dtor_value)
146 destroy_data->dtor_value(value, destroy_data->data);
147 return CBS_CONT;
148}
149
150void
151dict_destroy(struct dict *dict,
152 void (*dtor_key)(void *tgt, void *data),
153 void (*dtor_value)(void *tgt, void *data),
154 void *data)
155{
156 /* Some keys and values are not initialized, so we can't call
157 * dtors for them. Iterate DICT instead. */
158 if (dtor_key != NULL || dtor_value != NULL) {
159 struct destroy_data destroy_data = {
160 dtor_key, dtor_value, data
161 };
162 dict_each(dict, NULL, destroy_cb, &destroy_data);
Juan Cespedescac15c32003-01-31 18:58:58 +0100163 }
164
Petr Machatad7e4ca82012-11-28 03:38:47 +0100165 vect_destroy(&dict->keys, NULL, NULL);
166 vect_destroy(&dict->values, NULL, NULL);
167 vect_destroy(&dict->status, NULL, NULL);
168}
Juan Cespedescac15c32003-01-31 18:58:58 +0100169
Petr Machatad7e4ca82012-11-28 03:38:47 +0100170static size_t
171default_secondary_hash(size_t pos)
172{
173 return pos % 97 + 1;
174}
Juan Cespedescac15c32003-01-31 18:58:58 +0100175
Petr Machata2718e3f2012-11-28 20:48:11 +0100176static size_t
177small_secondary_hash(size_t pos)
178{
179 return 1;
180}
181
Petr Machatad7e4ca82012-11-28 03:38:47 +0100182static inline size_t
183n(struct dict *dict)
184{
185 return vect_size(&dict->keys);
186}
187
188static inline size_t (*
189hash2(struct dict *dict))(size_t)
190{
191 if (dict->hash2 != NULL)
192 return dict->hash2;
Petr Machata2718e3f2012-11-28 20:48:11 +0100193 else if (n(dict) < 100)
194 return small_secondary_hash;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100195 else
Petr Machatad7e4ca82012-11-28 03:38:47 +0100196 return default_secondary_hash;
197}
Juan Cespedescac15c32003-01-31 18:58:58 +0100198
Petr Machatad7e4ca82012-11-28 03:38:47 +0100199static void *
200getkey(struct dict *dict, size_t pos)
201{
202 return ((unsigned char *)dict->keys.data)
203 + dict->keys.elt_size * pos;
204}
205
206static void *
207getvalue(struct dict *dict, size_t pos)
208{
209 return ((unsigned char *)dict->values.data)
210 + dict->values.elt_size * pos;
211}
212
213static size_t
214find_slot(struct dict *dict, const void *key,
215 int *foundp, int *should_rehash, size_t *pi)
216{
Petr Machata82fa4c42012-12-05 01:19:42 +0100217 assert(n(dict) > 0);
Petr Machatad7e4ca82012-11-28 03:38:47 +0100218 size_t pos = dict->hash1(key) % n(dict);
219 size_t pos0 = -1;
220 size_t d = hash2(dict)(pos);
221 size_t i = 0;
222 *foundp = 0;
223
224 /* We skip over any taken or erased slots. But we remember
225 * the first erased that we find, and if we don't find the key
226 * later, we return that position. */
227 for (; bitp(dict, pos)->taken || bitp(dict, pos)->erased;
228 pos = (pos + d) % n(dict)) {
Petr Machatad7e4ca82012-11-28 03:38:47 +0100229 if (pos0 == (size_t)-1 && bitp(dict, pos)->erased)
230 pos0 = pos;
231
Petr Machata2718e3f2012-11-28 20:48:11 +0100232 /* If there is a loop, but we've seen an erased
233 * element, take that one. Otherwise give up. */
234 if (++i > dict->size) {
235 if (pos0 != (size_t)-1)
236 break;
237 return (size_t)-1;
238 }
Petr Machatad7e4ca82012-11-28 03:38:47 +0100239
240 if (bitp(dict, pos)->taken
241 && dict->eq(getkey(dict, pos), key)) {
242 *foundp = 1;
243 break;
244 }
245 }
246
247 if (!*foundp && pos0 != (size_t)-1)
248 pos = pos0;
249
250 /* If the hash table degraded into a linked list, request a
251 * rehash. */
252 if (should_rehash != NULL)
253 *should_rehash = i > 10 && i > n(dict) / 10;
254
255 if (pi != NULL)
256 *pi = i;
257 return pos;
258}
259
Petr Machata82fa4c42012-12-05 01:19:42 +0100260static enum callback_status
Petr Machatad7e4ca82012-11-28 03:38:47 +0100261rehash_move(void *key, void *value, void *data)
262{
263 if (dict_insert(data, key, value) < 0)
264 return CBS_STOP;
265 else
266 return CBS_CONT;
267}
268
Petr Machata82fa4c42012-12-05 01:19:42 +0100269static int
Petr Machatad7e4ca82012-11-28 03:38:47 +0100270rehash(struct dict *dict, size_t nn)
271{
Petr Machata2718e3f2012-11-28 20:48:11 +0100272 assert(nn != n(dict));
Petr Machatad7e4ca82012-11-28 03:38:47 +0100273 int ret = -1;
274
275 struct dict tmp;
276 dict_init(&tmp, dict->keys.elt_size, dict->values.elt_size,
277 dict->hash1, dict->eq, dict->hash2);
278
279 /* To honor all invariants (so that we can safely call
280 * dict_destroy), we first make a request to _reserve_ enough
281 * room in all vectors. This has no observable effect on
282 * contents of vectors. */
283 if (vect_reserve(&tmp.keys, nn) < 0
284 || vect_reserve(&tmp.values, nn) < 0
285 || vect_reserve(&tmp.status, nn) < 0)
286 goto done;
287
288 /* Now that we know that there is enough size in vectors, we
289 * simply bump the size. */
290 tmp.keys.size = nn;
291 tmp.values.size = nn;
292 size_t old_size = tmp.status.size;
293 tmp.status.size = nn;
294 memset(VECT_ELEMENT(&tmp.status, struct status_bits, old_size),
295 0, (tmp.status.size - old_size) * tmp.status.elt_size);
296
297 /* At this point, TMP is once more an empty dictionary with NN
298 * slots. Now move stuff from DICT to TMP. */
299 if (dict_each(dict, NULL, rehash_move, &tmp) != NULL)
300 goto done;
301
302 /* And now swap contents of DICT and TMP, and we are done. */
303 {
304 struct dict tmp2 = *dict;
305 *dict = tmp;
306 tmp = tmp2;
307 }
308
309 ret = 0;
310
311done:
312 /* We only want to release the containers, not the actual data
313 * that they hold, so it's fine if we don't pass any dtor. */
314 dict_destroy(&tmp, NULL, NULL, NULL);
315 return ret;
316
317}
318
319static const size_t primes[] = {
320 13, 31, 61, 127, 251, 509, 1021, 2039, 4093,
321 8191, 16381, 32749, 65521, 130981, 0
322};
323
324static size_t
325larger_size(size_t current)
326{
327 if (current == 0)
328 return primes[0];
329
330 if (current < primes[sizeof(primes)/sizeof(*primes) - 2]) {
331 size_t i;
332 for (i = 0; primes[i] != 0; ++i)
333 if (primes[i] > current)
334 return primes[i];
335 abort();
336 }
337
338 /* We ran out of primes, so invent a new one. The following
339 * gives primes until about 17M elements (and then some more
340 * later). */
341 return 2 * current + 6585;
342}
343
344static size_t
345smaller_size(size_t current)
346{
347 if (current <= primes[0])
348 return primes[0];
349
350 if (current <= primes[sizeof(primes)/sizeof(*primes) - 2]) {
351 size_t i;
352 size_t prev = 0;
353 for (i = 0; primes[i] != 0; ++i) {
354 if (primes[i] >= current)
355 return prev;
356 prev = primes[i];
357 }
358 abort();
359 }
360
361 return (current - 6585) / 2;
362}
363
364int
365dict_insert(struct dict *dict, void *key, void *value)
366{
367 if (n(dict) == 0 || dict->size > 0.7 * n(dict))
368 rehash:
369 if (rehash(dict, larger_size(n(dict))) < 0)
370 return -1;
371
372 int found;
373 int should_rehash;
374 size_t slot_n = find_slot(dict, key, &found, &should_rehash, NULL);
Petr Machata2718e3f2012-11-28 20:48:11 +0100375 if (slot_n == (size_t)-1)
376 return -1;
Petr Machatad7e4ca82012-11-28 03:38:47 +0100377 if (found)
378 return 1;
Petr Machata2718e3f2012-11-28 20:48:11 +0100379 assert(!bitp(dict, slot_n)->taken);
Petr Machatad7e4ca82012-11-28 03:38:47 +0100380
381 /* If rehash was requested, do that, and retry. But just live
382 * with it for apparently sparse tables. No resizing can fix
383 * a rubbish hash. */
384 if (should_rehash && dict->size > 0.3 * n(dict))
385 goto rehash;
386
387 memmove(getkey(dict, slot_n), key, dict->keys.elt_size);
388 memmove(getvalue(dict, slot_n), value, dict->values.elt_size);
389
390 bitp(dict, slot_n)->taken = 1;
391 bitp(dict, slot_n)->erased = 0;
392 ++dict->size;
393
Juan Cespedescac15c32003-01-31 18:58:58 +0100394 return 0;
395}
396
Juan Cespedesf1350522008-12-16 18:19:58 +0100397void *
Petr Machatad7e4ca82012-11-28 03:38:47 +0100398dict_find(struct dict *dict, const void *key)
Petr Machata77fbb8f2012-04-19 16:57:57 +0200399{
Petr Machatad7e4ca82012-11-28 03:38:47 +0100400 if (dict->size == 0)
401 return NULL;
Petr Machata82fa4c42012-12-05 01:19:42 +0100402 assert(n(dict) > 0);
Petr Machata77fbb8f2012-04-19 16:57:57 +0200403
Petr Machatad7e4ca82012-11-28 03:38:47 +0100404 int found;
405 size_t slot_n = find_slot(dict, key, &found, NULL, NULL);
406 if (found)
407 return getvalue(dict, slot_n);
408 else
409 return NULL;
410}
Petr Machata77fbb8f2012-04-19 16:57:57 +0200411
Petr Machatad7e4ca82012-11-28 03:38:47 +0100412int
413dict_erase(struct dict *dict, const void *key,
414 void (*dtor_key)(void *tgt, void *data),
415 void (*dtor_value)(void *tgt, void *data),
416 void *data)
417{
418 int found;
419 size_t i;
420 size_t slot_n = find_slot(dict, key, &found, NULL, &i);
421 if (!found)
422 return -1;
423
424 if (dtor_key != NULL)
425 dtor_key(getkey(dict, slot_n), data);
426 if (dtor_value != NULL)
427 dtor_value(getvalue(dict, slot_n), data);
428
429 bitp(dict, slot_n)->taken = 0;
430 bitp(dict, slot_n)->erased = 1;
431 --dict->size;
432
433 if (dict->size < 0.3 * n(dict)) {
Petr Machata2718e3f2012-11-28 20:48:11 +0100434 size_t smaller = smaller_size(n(dict));
435 if (smaller != n(dict))
436 /* Don't mind if it fails when shrinking. */
437 rehash(dict, smaller);
Petr Machata77fbb8f2012-04-19 16:57:57 +0200438 }
Petr Machatad7e4ca82012-11-28 03:38:47 +0100439
440 return 0;
Petr Machata77fbb8f2012-04-19 16:57:57 +0200441}
442
443void *
Petr Machatad7e4ca82012-11-28 03:38:47 +0100444dict_each(struct dict *dict, void *start_after,
445 enum callback_status (*cb)(void *, void *, void *), void *data)
Petr Machata345c9b52012-02-10 13:10:03 +0100446{
Petr Machatad7e4ca82012-11-28 03:38:47 +0100447 size_t i;
448 if (start_after != NULL)
449 i = ((start_after - dict->keys.data) / dict->keys.elt_size) + 1;
450 else
451 i = 0;
Juan Cespedescac15c32003-01-31 18:58:58 +0100452
Petr Machatad7e4ca82012-11-28 03:38:47 +0100453 for (; i < dict->keys.size; ++i)
454 if (bitp(dict, i)->taken && !bitp(dict, i)->erased) {
455 void *key = getkey(dict, i);
456 if (cb(key, getvalue(dict, i), data) != CBS_CONT)
457 return key;
Juan Cespedescac15c32003-01-31 18:58:58 +0100458 }
Petr Machatad7e4ca82012-11-28 03:38:47 +0100459
460 return NULL;
Juan Cespedescac15c32003-01-31 18:58:58 +0100461}
462
Petr Machatad7e4ca82012-11-28 03:38:47 +0100463size_t
464dict_hash_int(const int *key)
Petr Machata345c9b52012-02-10 13:10:03 +0100465{
Petr Machatad7e4ca82012-11-28 03:38:47 +0100466 return (size_t)(*key * 2654435761);
Juan Cespedescac15c32003-01-31 18:58:58 +0100467}
468
Juan Cespedesf1350522008-12-16 18:19:58 +0100469int
Petr Machatad7e4ca82012-11-28 03:38:47 +0100470dict_eq_int(const int *key1, const int *key2)
Petr Machata345c9b52012-02-10 13:10:03 +0100471{
Petr Machatad7e4ca82012-11-28 03:38:47 +0100472 return *key1 == *key2;
Juan Cespedescac15c32003-01-31 18:58:58 +0100473}
474
Petr Machatad7e4ca82012-11-28 03:38:47 +0100475size_t
476dict_hash_string(const char **key)
Petr Machata345c9b52012-02-10 13:10:03 +0100477{
Petr Machatad7e4ca82012-11-28 03:38:47 +0100478 size_t h = 5381;
479 const char *str = *key;
480 while (*str != 0)
481 h = h * 33 ^ *str++;
482 return h;
Juan Cespedescac15c32003-01-31 18:58:58 +0100483}
484
Juan Cespedesf1350522008-12-16 18:19:58 +0100485int
Petr Machatad7e4ca82012-11-28 03:38:47 +0100486dict_eq_string(const char **key1, const char **key2)
Petr Machata345c9b52012-02-10 13:10:03 +0100487{
Petr Machatad7e4ca82012-11-28 03:38:47 +0100488 return strcmp(*key1, *key2) == 0;
Juan Cespedescac15c32003-01-31 18:58:58 +0100489}
Juan Cespedesbc8caf02009-05-07 19:38:38 +0200490
Petr Machata3e865762012-11-28 22:06:17 +0100491void
492dict_dtor_string(const char **key, void *data)
493{
494 free((char *)*key);
495}
496
497int
498dict_clone_string(const char **tgt, const char **src, void *data)
499{
500 *tgt = strdup(*src);
501 return *tgt != NULL ? 0 : -1;
502}
503
Petr Machatad7e4ca82012-11-28 03:38:47 +0100504#ifdef TEST
505static enum callback_status
506dump(int *key, int *value, void *data)
Petr Machata534e00f2011-09-27 17:58:38 +0200507{
Petr Machatad7e4ca82012-11-28 03:38:47 +0100508 char *seen = data;
509 assert(seen[*key] == 0);
510 seen[*key] = 1;
511 assert(*value == *key * 2 + 1);
512 return CBS_STOP;
513}
514
515static size_t
516dict_hash_int_silly(const int *key)
517{
518 return *key % 10;
519}
520
521static void
522verify(struct dict *di, size_t len, char *seen)
523{
524 size_t ct = 0;
525 int *it;
526 for (it = NULL; (it = DICT_EACH(di, int, int, it, dump, seen)) != NULL;)
527 ct++;
528 assert(ct == len);
529 memset(seen, 0, len);
530}
531
532static enum callback_status
533fill_keys(int *key, int *value, void *data)
534{
535 int *array = data;
536 array[++array[0]] = *key;
537 return CBS_CONT;
538}
539
540static void
541test1(void)
542{
543 struct dict di;
544 DICT_INIT(&di, int, int, dict_hash_int, dict_eq_int, NULL);
545
546 char seen[100000] = {};
547 size_t i;
548 for (i = 0; i < sizeof(seen); ++i) {
549 int key = i;
550 int value = 2 * i + 1;
551 DICT_INSERT(&di, &key, &value);
552 int *valp = DICT_FIND(&di, &key, int);
553 assert(valp != NULL);
554 assert(*valp == value);
555 assert(dict_size(&di) == i + 1);
556 }
557
558 verify(&di, sizeof(seen), seen);
559
560 struct dict d2;
561 DICT_CLONE(&d2, &di, int, int, NULL, NULL, NULL, NULL, NULL);
562 DICT_DESTROY(&di, int, int, NULL, NULL, NULL);
563 verify(&d2, sizeof(seen), seen);
564
565 /* Now we try to gradually erase all elements. We can't erase
566 * inside a DICT_EACH call, so copy first keys to a separate
567 * memory area first. */
568 int keys[d2.size + 1];
569 size_t ct = 0;
570 keys[0] = 0;
571 DICT_EACH(&d2, int, int, NULL, fill_keys, keys);
572 for (i = 0; i < (size_t)keys[0]; ++i) {
573 assert(DICT_ERASE(&d2, &keys[i + 1], int,
574 NULL, NULL, NULL) == 0);
575 ++ct;
576 }
577 assert(ct == sizeof(seen));
578 DICT_DESTROY(&d2, int, int, NULL, NULL, NULL);
579}
580
581static void
582test_erase(void)
583{
Juan Cespedesbc8caf02009-05-07 19:38:38 +0200584 int i;
585
Petr Machatad7e4ca82012-11-28 03:38:47 +0100586 /* To test erase, we need a relatively bad hash function, so
587 * that there are some overlapping chains in the table. */
588 struct dict d2;
589 DICT_INIT(&d2, int, int, dict_hash_int_silly, dict_eq_int, NULL);
590 const int limit = 500;
591 for (i = 0; i < limit; ++i) {
592 int key = 2 * i + 1;
593 int value = 2 * key + 1;
594 DICT_INSERT(&d2, &key, &value);
Juan Cespedesbc8caf02009-05-07 19:38:38 +0200595 }
Juan Cespedesbc8caf02009-05-07 19:38:38 +0200596
Petr Machatad7e4ca82012-11-28 03:38:47 +0100597 /* Now we try to delete each of the keys, and verify that none
598 * of the chains was broken. */
599 for (i = 0; i < limit; ++i) {
600 struct dict copy;
601 DICT_CLONE(&copy, &d2, int, int, NULL, NULL, NULL, NULL, NULL);
602 int key = 2 * i + 1;
603 DICT_ERASE(&copy, &key, int, NULL, NULL, NULL);
604 assert(dict_size(&copy) == dict_size(&d2) - 1);
605
606 int j;
607 for (j = 0; j < limit; ++j) {
608 key = 2 * j + 1;
609 int *valp = DICT_FIND(&copy, &key, int);
610 if (i != j) {
611 assert(valp != NULL);
612 assert(*valp == 2 * key + 1);
613 } else {
614 assert(valp == NULL);
Juan Cespedesbc8caf02009-05-07 19:38:38 +0200615 }
Juan Cespedesbc8caf02009-05-07 19:38:38 +0200616 }
Petr Machatad7e4ca82012-11-28 03:38:47 +0100617
618 DICT_DESTROY(&copy, int, int, NULL, NULL, NULL);
Juan Cespedesbc8caf02009-05-07 19:38:38 +0200619 }
Petr Machatad7e4ca82012-11-28 03:38:47 +0100620 DICT_DESTROY(&d2, int, int, NULL, NULL, NULL);
Juan Cespedesbc8caf02009-05-07 19:38:38 +0200621}
Petr Machata534e00f2011-09-27 17:58:38 +0200622
Petr Machatad7e4ca82012-11-28 03:38:47 +0100623int main(int argc, char *argv[])
Petr Machata534e00f2011-09-27 17:58:38 +0200624{
Petr Machatad7e4ca82012-11-28 03:38:47 +0100625 test1();
626 test_erase();
627 return 0;
Petr Machata534e00f2011-09-27 17:58:38 +0200628}
629
Petr Machatad7e4ca82012-11-28 03:38:47 +0100630#endif