Line data Source code
1 : /******************************************************************************
2 : * Darmstadtium - a library of data structures
3 : * Ds_hash - a hash table library
4 : * One of the Bohr Game Libraries (see chaoslizard.org/devel/bohr)
5 : * Copyright (C) 2008 Charles Lindsay. Some rights reserved; see COPYING.
6 : * $Id: ds_hash.h 317 2008-01-05 21:45:34Z chaz $
7 : ******************************************************************************/
8 :
9 :
10 : #ifndef __bohr_ds_hash_h__
11 : #define __bohr_ds_hash_h__
12 :
13 : #ifndef __STDC_CONSTANT_MACROS
14 : #define __STDC_CONSTANT_MACROS
15 : #endif
16 : #ifndef __STDC_FORMAT_MACROS
17 : #define __STDC_FORMAT_MACROS
18 : #endif
19 :
20 : #include <inttypes.h>
21 : #include <stddef.h>
22 : #include <stdint.h>
23 : #include <stdlib.h>
24 : #include <string.h>
25 :
26 :
27 : // Controls inlining of this library's functions.
28 : #ifndef Ds_HASH_INLINE
29 : #ifdef Ds_INLINE
30 : #define Ds_HASH_INLINE Ds_INLINE
31 : #else
32 : #define Ds_HASH_INLINE inline static
33 : #endif
34 : #endif
35 :
36 : // Nix non-critical C99 keywords in compilers that don't support them.
37 : #if ((!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L) && !defined(restrict))
38 : #define restrict
39 : #define _Ds_HASH_DEFINED_RESTRICT
40 : #endif
41 :
42 :
43 : // A type to hold a hash value. We also define the standard constant-
44 : // declaration macro, max value, and printf/scanf specifiers.
45 : #ifndef Ds_HASH_T_DEFINED
46 : #define Ds_HASH_T_DEFINED
47 : typedef uint32_t Ds_hash_t;
48 : #define Ds_HASH_C UINT32_C
49 : #define Ds_HASH_MAX UINT32_MAX
50 : #define PRIdDs_HASH PRId32
51 : #define PRIiDs_HASH PRIi32
52 : #define PRIoDs_HASH PRIo32
53 : #define PRIuDs_HASH PRIu32
54 : #define PRIxDs_HASH PRIx32
55 : #define PRIXDs_HASH PRIX32
56 : #define SCNdDs_HASH SCNd32
57 : #define SCNiDs_HASH SCNi32
58 : #define SCNoDs_HASH SCNo32
59 : #define SCNuDs_HASH SCNu32
60 : #define SCNxDs_HASH SCNx32
61 : #endif
62 :
63 :
64 : // A chainable entry in a Ds_hash_table.
65 : typedef struct Ds_hash_entry
66 : {
67 : struct Ds_hash_entry * next; // linked list next pointer
68 :
69 : Ds_hash_t hash; // the item's hash value
70 : size_t bucket; // which bucket it's in
71 : size_t size; // how big the item is
72 : uint8_t item[]; // the item itself (struct hack, valid C99)
73 :
74 : } Ds_hash_entry;
75 :
76 :
77 : // Define a vector that works with our Ds_hash_entries.
78 : #define Ds_VECTOR_BEHAVIOR 2
79 : #define Ds_VECTOR_TYPE Ds_hash_entry *
80 : #define Ds_VECTOR_SUFFIX _Dsh
81 : #include <bohr/ds_vector.h>
82 :
83 :
84 : // A hash table (an alias for a specific kind of Ds_vector).
85 : typedef Ds_vector_Dsh Ds_hash_table;
86 : #define Ds_HASH_TABLE_INIT Ds_VECTOR_INIT // initializer
87 :
88 :
89 : // Function to compare two hash items; return similar to strcmp--declare as:
90 : // int MyCompare(const void * restrict key, size_t key_size,
91 : // const void * restrict item, size_t item_size);
92 : typedef int (*Ds_hash_compare_fn) (const void * key, size_t key_size, const void * item, size_t item_size);
93 :
94 :
95 : /* Reserves initial space in the Ds_hash_table. size must be a power of 2.
96 : */
97 2090 : Ds_HASH_INLINE int Ds_InitHashTable (Ds_hash_table * restrict table, size_t size)
98 : {
99 2090 : if (!Ds_InitVector_Dsh ((Ds_vector_Dsh *) table, size)) return 0;
100 :
101 2090 : memset (table->buf, 0, size * sizeof (Ds_hash_entry *));
102 2090 : return 1;
103 : }
104 :
105 : /* Frees memory associated with the Ds_hash_table.
106 : */
107 2090 : Ds_HASH_INLINE void Ds_FreeHashTable (Ds_hash_table * restrict table)
108 : {
109 69098 : for (size_t i = 0; i < table->cap; ++i)
110 : {
111 : Ds_hash_entry * head;
112 :
113 67008 : head = table->buf[i];
114 135920 : while (head)
115 : {
116 : Ds_hash_entry * t;
117 :
118 1904 : t = head;
119 1904 : head = head->next;
120 1904 : elektraFree (t);
121 : }
122 : }
123 :
124 2090 : Ds_FreeVector_Dsh ((Ds_vector_Dsh *) table);
125 2090 : }
126 :
127 : /* Iterates over all the entries in the Ds_hash_table. Specify NULL for prev to
128 : * return the first Ds_hash_entry. Subsequently pass the previous return value.
129 : * Returns NULL when there are no remaining entries.
130 : */
131 : Ds_HASH_INLINE Ds_hash_entry * Ds_NextHashEntry (const Ds_hash_table * restrict table, const Ds_hash_entry * restrict prev)
132 : {
133 7333 : Ds_hash_entry * n = NULL;
134 7333 : size_t bucket = 0;
135 :
136 7333 : if (prev)
137 : {
138 4277 : n = prev->next;
139 4277 : bucket = prev->bucket + 1;
140 : }
141 :
142 105381 : while (!n && bucket < table->cap)
143 98048 : n = table->buf[bucket++];
144 :
145 : return n;
146 : }
147 :
148 : /* Inserts an item into the Ds_hash_table. The item has the given hash value
149 : * and size. Returns the new Ds_hash_entry or NULL if it fails.
150 : */
151 1904 : Ds_HASH_INLINE Ds_hash_entry * Ds_InsertHashItem (Ds_hash_table * restrict table, const void * restrict item, size_t size, Ds_hash_t hash)
152 : {
153 : Ds_hash_entry * n;
154 :
155 1904 : if ((n = (Ds_hash_entry *) malloc (sizeof (Ds_hash_entry) + size)) != NULL)
156 : {
157 1904 : n->hash = hash;
158 1904 : n->bucket = hash & (table->cap - 1);
159 1904 : n->size = size;
160 1904 : memcpy (n->item, item, size);
161 :
162 1904 : n->next = table->buf[n->bucket];
163 1904 : table->buf[n->bucket] = n;
164 1904 : table->num++;
165 : }
166 :
167 1904 : return n;
168 : }
169 :
170 : /* Removes a Ds_hash_entry from the Ds_hash_table. The entry must belong to the
171 : * table. Returns 0/nonzero on failure/success.
172 : */
173 0 : Ds_HASH_INLINE int Ds_RemoveHashEntry (Ds_hash_table * restrict table, Ds_hash_entry * restrict entry)
174 : {
175 0 : int found = 0;
176 : Ds_hash_entry * t;
177 :
178 0 : if (entry == (t = table->buf[entry->bucket]))
179 : {
180 0 : table->buf[entry->bucket] = entry->next;
181 0 : found = 1;
182 : }
183 0 : else if (t)
184 : {
185 0 : while (t->next)
186 : {
187 0 : if (entry == t->next)
188 : {
189 0 : t->next = entry->next;
190 0 : found = 1;
191 : break;
192 : }
193 : }
194 : }
195 :
196 0 : if (found)
197 : {
198 0 : elektraFree (entry);
199 0 : table->num--;
200 : }
201 :
202 0 : return found;
203 : }
204 :
205 : /* Searches the Ds_hash_table for a given key item that has the given hash
206 : * value. Returns the first entry matching the hash value, or if you specify a
207 : * compare function, returns the first entry the compare function returns 0 on.
208 : * Returns NULL if it's not found. key and size are only used to pass to
209 : * compare().
210 : */
211 : Ds_HASH_INLINE Ds_hash_entry * Ds_SearchHashTable (const Ds_hash_table * restrict table, const void * restrict key, size_t size,
212 : Ds_hash_t hash, Ds_hash_compare_fn compare)
213 : {
214 : Ds_hash_entry * t;
215 :
216 2357 : t = table->buf[hash & (table->cap - 1)];
217 2584 : while (t && hash != t->hash && (!compare || compare (key, size, &t->item, t->size)))
218 : {
219 227 : t = t->next;
220 : }
221 :
222 : return t;
223 : }
224 :
225 : /* Resizes the Ds_hash_table, moving entries around. size must be a power of 2.
226 : */
227 4 : Ds_HASH_INLINE int Ds_ResizeHashTable (Ds_hash_table * restrict table, size_t size)
228 : {
229 4 : if (size > table->cap)
230 : {
231 : size_t old_size;
232 :
233 4 : old_size = table->cap;
234 4 : if (!Ds_ResizeVector_Dsh ((Ds_vector_Dsh *) table, size)) return 0;
235 4 : memset (table->buf + old_size, 0, (size - old_size) * sizeof (Ds_hash_entry *));
236 :
237 132 : for (size_t i = 0; i < old_size; ++i)
238 : {
239 : size_t bucket;
240 : Ds_hash_entry *e, *t;
241 :
242 128 : e = table->buf[i];
243 278 : while (e && (bucket = e->hash & (size - 1)) != i)
244 : {
245 22 : t = e->next;
246 22 : e->next = table->buf[e->bucket = bucket];
247 22 : table->buf[bucket] = e;
248 22 : table->buf[i] = e = t;
249 : }
250 146 : while (e && (t = e->next) != NULL)
251 : {
252 18 : if ((bucket = t->hash & (size - 1)) != i)
253 : {
254 4 : e->next = t->next;
255 4 : t->next = table->buf[t->bucket = bucket];
256 4 : table->buf[bucket] = t;
257 : }
258 : else
259 : e = t;
260 : }
261 : }
262 : }
263 0 : else if (size < table->cap)
264 : {
265 : size_t old_num;
266 :
267 0 : for (size_t i = size; i < table->cap; ++i)
268 : {
269 : Ds_hash_entry * t;
270 :
271 0 : if ((t = table->buf[i]) != NULL)
272 : {
273 : size_t bucket;
274 :
275 0 : bucket = i & (size - 1);
276 0 : while (t->next)
277 : {
278 0 : t->bucket = bucket;
279 0 : t = t->next;
280 : }
281 0 : t->next = table->buf[t->bucket = bucket];
282 0 : table->buf[bucket] = table->buf[i];
283 : }
284 : }
285 :
286 0 : old_num = table->num;
287 0 : if (!Ds_ResizeVector_Dsh ((Ds_vector_Dsh *) table, size)) return 0;
288 0 : table->num = old_num;
289 : }
290 :
291 : return 1;
292 : }
293 :
294 :
295 : #ifdef _Ds_HASH_DEFINED_RESTRICT
296 : #undef _Ds_HASH_DEFINED_RESTRICT
297 : #undef restrict
298 : #endif
299 :
300 : #endif
|