2 ** Splint - annotation-assisted static program checker
3 ** Copyright (C) 1994-2003 University of Virginia,
4 ** Massachusetts Institute of Technology
6 ** This program is free software; you can redistribute it and/or modify it
7 ** under the terms of the GNU General Public License as published by the
8 ** Free Software Foundation; either version 2 of the License, or (at your
9 ** option) any later version.
11 ** This program is distributed in the hope that it will be useful, but
12 ** WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 ** General Public License for more details.
16 ** The GNU General Public License is available from http://www.gnu.org/ or
17 ** the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
18 ** MA 02111-1307, USA.
20 ** For information on splint: info@splint.org
21 ** To report a bug: splint-bug@splint.org
22 ** For more information: http://www.splint.org
27 ** A genericTable is a mapping from string keys to void * objects.
28 ** We sacrific type checking here for code reuse.
31 # include "splintMacros.nf"
33 # include "cstringHash.h"
35 /*@constant null ghbucket ghbucket_undefined; @*/
36 # define ghbucket_undefined 0
38 /*@constant int GHBUCKET_BASESIZE; @*/
39 # define GHBUCKET_BASESIZE 2
41 static /*@nullwhentrue@*/ bool ghbucket_isNull (/*@null@*/ ghbucket h
)
43 return (h
== ghbucket_undefined
);
47 ghentry_create (/*@keep@*/ cstring key
, /*@keep@*/ void *val
)
49 ghentry h
= (ghentry
) dmalloc (sizeof (*h
));
53 llassert (val
!= NULL
);
60 ghentry_free (/*@only@*/ ghentry ghe
)
62 cstring_free (ghe
->key
);
63 /* can't free val contents */
70 ghbucket_isEmpty (ghbucket h
)
72 return (h
== ghbucket_undefined
|| h
->size
== 0);
76 int genericTable_size (genericTable h
)
78 if (genericTable_isDefined (h
)) {
86 static /*@unused@*/ cstring
87 ghbucket_unparse (ghbucket h
)
89 cstring s
= cstring_undefined
;
91 if (!ghbucket_isNull (h
))
95 for (i
= 0; i
< h
->size
; i
++)
97 s
= message ("%q %s", s
, h
->entries
[i
]->key
);
105 static ghbucket
ghbucket_single (/*@keep@*/ ghentry e
)
107 ghbucket h
= (ghbucket
) dmalloc (sizeof (*h
));
110 h
->nspace
= GHBUCKET_BASESIZE
- 1;
111 h
->entries
= (ghentry
*) dmalloc (GHBUCKET_BASESIZE
* sizeof (*h
->entries
));
118 ghbucket_grow (/*@notnull@*/ ghbucket h
)
123 h
->nspace
+= GHBUCKET_BASESIZE
;
125 newentries
= (ghentry
*)
126 dmalloc ((h
->size
+ GHBUCKET_BASESIZE
) * sizeof (*newentries
));
128 for (i
= 0; i
< h
->size
; i
++)
130 newentries
[i
] = h
->entries
[i
];
134 h
->entries
= newentries
;
136 } /*@=compmempass*/ /* Spurious warnings reported for h->entries */
138 static /*@null@*/ /*@exposed@*/ void *ghbucket_lookup (ghbucket p_h
, cstring p_key
);
141 ** bizarre duplicate behaviour
142 ** required for duplicate entries
146 ghbucket_add (/*@notnull@*/ ghbucket h
, /*@only@*/ ghentry e
)
148 void *exloc
= ghbucket_lookup (h
, e
->key
);
151 llcontbug (message ("ghbucket_add: adding duplicate entry: %s",
157 if (h
->nspace
== 0) {
161 h
->entries
[h
->size
] = e
;
168 ghbucket_ncollisions (ghbucket h
)
170 if (!ghbucket_isNull (h
) && (h
->size
> 1))
171 return (h
->size
- 1);
177 /*@exposed@*/ /*@null@*/ void *
178 ghbucket_lookup (ghbucket h
, cstring key
)
180 if (!ghbucket_isNull (h
))
184 for (i
= 0; i
< h
->size
; i
++)
186 if (cstring_equal (h
->entries
[i
]->key
, key
))
188 return h
->entries
[i
]->val
;
197 void ghbucket_free (/*@only@*/ ghbucket h
)
199 if (!ghbucket_isNull (h
))
203 for (i
= 0; i
< h
->size
; i
++)
205 ghentry_free (h
->entries
[i
]);
214 genericTable_free (/*@only@*/ genericTable h
)
216 if (genericTable_isDefined (h
))
220 for (i
= 0; i
< h
->size
; i
++)
222 ghbucket_free (h
->buckets
[i
]);
232 genericTable_countCollisions (genericTable h
)
237 llassert (genericTable_isDefined (h
));
239 for (i
= 0; i
< h
->size
; i
++)
241 nc
+= ghbucket_ncollisions (h
->buckets
[i
]);
249 genericTable_countEmpty (genericTable h
)
254 llassert (genericTable_isDefined (h
));
256 for (i
= 0; i
< h
->size
; i
++)
258 if (ghbucket_isEmpty (h
->buckets
[i
]))
269 genericTable_hashValue (/*@notnull@*/ genericTable h
, cstring key
)
271 llassert (h
->size
!= 0);
272 return (cstring_hashValue(key
) % h
->size
);
275 static /*@exposed@*/ ghbucket
276 genericTable_hash (/*@notnull@*/ genericTable h
, cstring key
)
278 return h
->buckets
[genericTable_hashValue (h
, key
)];
282 /*@only@*/ genericTable
283 genericTable_create (int size
)
286 genericTable h
= (genericTable
) dmalloc (sizeof (*h
));
291 h
->buckets
= (ghbucket
*) dmalloc (sizeof (*h
->buckets
) * size
);
294 for (i
= 0; i
< size
; i
++)
296 h
->buckets
[i
] = ghbucket_undefined
;
304 static /*@unused@*/ void
305 genericTable_print (genericTable h
)
309 if (genericTable_isDefined (h
)) {
310 for (i
= 0; i
< h
->size
; i
++)
312 ghbucket hb
= h
->buckets
[i
];
316 llmsg (message ("%d. %s\n", i
, ghbucket_unparse (hb
)));
320 llmsg (message ("size: %d, collisions: %d, empty: %d",
322 genericTable_countCollisions (h
),
323 genericTable_countEmpty (h
)));
325 llmsglit ("Empty hash table.");
333 genericTable_stats (genericTable h
)
335 llassert (genericTable_isDefined (h
));
336 return (message ("size: %d, collisions: %d, empty: %d\n",
337 h
->size
, genericTable_countCollisions (h
),
338 genericTable_countEmpty (h
)));
340 # endif /* DEADCODE */
343 genericTable_addEntry (/*@notnull@*/ genericTable h
, /*@only@*/ ghentry e
)
345 unsigned int hindex
= genericTable_hashValue (h
, e
->key
);
348 ** ghbucket hb = h->buckets[hindex];
349 ** instead reveals a bug I don't want to deal with right now!
354 if (ghbucket_isNull (h
->buckets
[hindex
]))
356 h
->buckets
[hindex
] = ghbucket_single (e
);
360 ghbucket_add (h
->buckets
[hindex
], e
);
365 genericTable_insert (genericTable h
, cstring key
, void *value
)
371 llassert (genericTable_isDefined (h
));
374 ** rehashing based (loosely) on code by Steve Harrison
377 if (h
->nentries
* 162 > h
->size
* 100)
380 int oldsize
= h
->size
;
381 int newsize
= 1 + ((oldsize
* 26244) / 10000); /* 26244 = 162^2 */
382 ghbucket
*oldbuckets
= h
->buckets
;
384 DPRINTF (("Rehashing..."));
387 h
->buckets
= (ghbucket
*) dmalloc (sizeof (*h
->buckets
) * newsize
);
390 for (i
= 0; i
< newsize
; i
++)
392 h
->buckets
[i
] = ghbucket_undefined
;
396 for (i
= 0; i
< oldsize
; i
++)
398 ghbucket bucket
= oldbuckets
[i
];
400 oldbuckets
[i
] = NULL
;
402 if (!ghbucket_isNull (bucket
))
406 for (j
= 0; j
< bucket
->size
; j
++)
408 genericTable_addEntry (h
, bucket
->entries
[j
]);
411 sfree (bucket
->entries
); /* evans 2001-03-24: Splint caught this */
419 /* evans 2000-12-22: this was before the rehash! Lost an entry size! */
421 e
= ghentry_create (key
, value
);
422 hindex
= genericTable_hashValue (h
, key
);
423 hb
= h
->buckets
[hindex
];
425 if (ghbucket_isNull (hb
))
427 h
->buckets
[hindex
] = ghbucket_single (e
);
431 ghbucket_add (hb
, e
);
435 /*@null@*/ /*@exposed@*/ void *
436 genericTable_lookup (genericTable h
, cstring key
)
440 llassert (genericTable_isDefined (h
));
442 hb
= genericTable_hash (h
, key
);
443 res
= ghbucket_lookup (hb
, key
);
445 /* if (res == NULL) { DPRINTF (("Lookup not found: %s", key)); } */
450 genericTable_update (genericTable h
, cstring key
, /*@only@*/ void *newval
)
454 llassert (genericTable_isDefined (h
));
456 hb
= genericTable_hash (h
, key
);
458 if (!ghbucket_isNull (hb
))
462 for (i
= 0; i
< hb
->size
; i
++)
464 if (cstring_equal (hb
->entries
[i
]->key
, key
))
466 llassert (newval
!= NULL
);
467 hb
->entries
[i
]->val
= newval
;
473 llbug (message ("genericTable_update: %s not found", key
));
478 genericTable_remove (genericTable h
, cstring key
)
482 llassert (genericTable_isDefined (h
));
483 hb
= genericTable_hash (h
, key
);
485 if (!ghbucket_isNull (hb
))
489 for (i
= 0; i
< hb
->size
; i
++)
491 if (cstring_equal (hb
->entries
[i
]->key
, key
))
493 if (i
< hb
->size
- 1)
495 hb
->entries
[i
] = hb
->entries
[hb
->size
- 1];
504 llbug (message ("genericTable_removeKey: %s not found", key
));
506 # endif /* DEADCODE */
508 bool genericTable_contains (genericTable h
, cstring key
)
510 return (genericTable_lookup (h
, key
) != NULL
);