source: mainline/uspace/lib/c/generic/adt/hash_set.c@ e2c50e1

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since e2c50e1 was e2c50e1, checked in by Martin Decky <martin@…>, 14 years ago

cherrypick hashset implementation from lp:~helenos-nicf/helenos/nicf

  • Property mode set to 100644
File size: 9.0 KB
Line 
1/*
2 * Copyright (c) 2008 Jakub Jermar
3 * Copyright (c) 2011 Radim Vansa
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup libc
31 * @{
32 */
33/** @file
34 */
35
36#include <adt/hash_set.h>
37#include <adt/list.h>
38#include <unistd.h>
39#include <malloc.h>
40#include <assert.h>
41#include <str.h>
42
43/** Create chained hash set
44 *
45 * @param h Hash set structure to be initialized.
46 * @param[in] hash Hash function
47 * @param[in] equals Equals function
48 * @param[in] init_size Initial hash set size
49 *
50 * @return True on success
51 *
52 */
53int hash_set_init(hash_set_t *h, hash_set_hash hash, hash_set_equals equals,
54 size_t init_size)
55{
56 assert(h);
57 assert(hash);
58 assert(equals);
59
60 if (init_size < HASH_SET_MIN_SIZE)
61 init_size = HASH_SET_MIN_SIZE;
62
63 h->table = malloc(init_size * sizeof(link_t));
64 if (!h->table)
65 return false;
66
67 for (size_t i = 0; i < init_size; i++)
68 list_initialize(&h->table[i]);
69
70 h->size = init_size;
71 h->count = 0;
72 h->hash = hash;
73 h->equals = equals;
74
75 return true;
76}
77
78/** Destroy a hash table instance.
79 *
80 * @param h Hash table to be destroyed.
81 *
82 */
83void hash_set_destroy(hash_set_t *h)
84{
85 assert(h);
86 free(h->table);
87}
88
89/** Rehash the internal table to new table
90 *
91 * @param h Original hash set
92 * @param new_table Memory for the new table
93 * @param new_size Size of the new table
94 */
95static void hash_set_rehash(hash_set_t *h, list_t *new_table,
96 size_t new_size)
97{
98 assert(new_size >= HASH_SET_MIN_SIZE);
99
100 for (size_t bucket = 0; bucket < new_size; bucket++)
101 list_initialize(&new_table[bucket]);
102
103 for (size_t bucket = 0; bucket < h->size; bucket++) {
104 link_t *cur;
105 link_t *next;
106
107 for (cur = h->table[bucket].head.next;
108 cur != &h->table[bucket].head;
109 cur = next) {
110 next = cur->next;
111 list_append(cur, &new_table[h->hash(cur) % new_size]);
112 }
113 }
114
115 list_t *old_table = h->table;
116 h->table = new_table;
117 free(old_table);
118 h->size = new_size;
119}
120
121/** Insert item into the set.
122 *
123 * If the set already contains equivalent object,
124 * the function fails.
125 *
126 * @param h Hash table.
127 * @param key Array of all keys necessary to compute hash index.
128 * @param item Item to be inserted into the hash table.
129 *
130 * @return True if the object was inserted
131 * @return Ffalse if the set already contained equivalent object.
132 *
133 */
134int hash_set_insert(hash_set_t *h, link_t *item)
135{
136 assert(item);
137 assert(h);
138 assert(h->hash);
139 assert(h->equals);
140
141 unsigned long hash = h->hash(item);
142 unsigned long chain = hash % h->size;
143
144 list_foreach(h->table[chain], cur) {
145 if (h->equals(cur, item))
146 return false;
147 }
148
149 if (h->count + 1 > h->size) {
150 size_t new_size = h->size * 2;
151 list_t *temp = malloc(new_size * sizeof(list_t));
152 if (temp != NULL)
153 hash_set_rehash(h, temp, new_size);
154
155 /*
156 * If the allocation fails, just use the same
157 * old table and try to rehash next time.
158 */
159 chain = hash % h->size;
160 }
161
162 h->count++;
163 list_append(item, &h->table[chain]);
164
165 return true;
166}
167
168/** Search the hash set for a matching object and return it
169 *
170 * @param h Hash set
171 * @param item The item that should equal to the matched object
172 *
173 * @return Matching item on success, NULL if there is no such item.
174 *
175 */
176link_t *hash_set_find(hash_set_t *h, const link_t *item)
177{
178 assert(h);
179 assert(h->hash);
180 assert(h->equals);
181
182 unsigned long chain = h->hash(item) % h->size;
183
184 list_foreach(h->table[chain], cur) {
185 if (h->equals(cur, item))
186 return cur;
187 }
188
189 return NULL;
190}
191
192/** Remove first matching object from the hash set and return it
193 *
194 * @param h Hash set.
195 * @param item The item that should be equal to the matched object
196 *
197 * @return The removed item or NULL if this is not found.
198 *
199 */
200link_t *hash_set_remove(hash_set_t *h, const link_t *item)
201{
202 assert(h);
203 assert(h->hash);
204 assert(h->equals);
205
206 link_t *cur = hash_set_find(h, item);
207 if (cur) {
208 list_remove(cur);
209
210 h->count--;
211 if (4 * h->count < h->size && h->size > HASH_SET_MIN_SIZE) {
212 size_t new_size = h->size / 2;
213 if (new_size < HASH_SET_MIN_SIZE)
214 /* possible e.g. if init_size == HASH_SET_MIN_SIZE + 1 */
215 new_size = HASH_SET_MIN_SIZE;
216
217 list_t *temp = malloc(new_size * sizeof (list_t));
218 if (temp != NULL)
219 hash_set_rehash(h, temp, new_size);
220 }
221 }
222
223 return cur;
224}
225
226/** Remove all elements for which the function returned non-zero
227 *
228 * The function can also destroy the element.
229 *
230 * @param h Hash set.
231 * @param f Function to be applied.
232 * @param arg Argument to be passed to the function.
233 *
234 */
235void hash_set_remove_selected(hash_set_t *h, int (*f)(link_t *, void *),
236 void *arg)
237{
238 assert(h);
239 assert(h->table);
240
241 for (size_t bucket = 0; bucket < h->size; bucket++) {
242 link_t *prev = &h->table[bucket].head;
243 link_t *cur;
244 link_t *next;
245
246 for (cur = h->table[bucket].head.next;
247 cur != &h->table[bucket].head;
248 cur = next) {
249 next = cur->next;
250 if (f(cur, arg)) {
251 prev->next = next;
252 next->prev = prev;
253 h->count--;
254 } else
255 prev = cur;
256 }
257 }
258
259 if (4 * h->count < h->size && h->size > HASH_SET_MIN_SIZE) {
260 size_t new_size = h->size / 2;
261 if (new_size < HASH_SET_MIN_SIZE)
262 /* possible e.g. if init_size == HASH_SET_MIN_SIZE + 1 */
263 new_size = HASH_SET_MIN_SIZE;
264
265 list_t *temp = malloc(new_size * sizeof (list_t));
266 if (temp != NULL)
267 hash_set_rehash(h, temp, new_size);
268 }
269}
270
271/** Apply function to all items in hash set
272 *
273 * @param h Hash set.
274 * @param f Function to be applied.
275 * @param arg Argument to be passed to the function.
276 *
277 */
278void hash_set_apply(hash_set_t *h, void (*f)(link_t *, void *), void *arg)
279{
280 assert(h);
281 assert(h->table);
282
283 for (size_t bucket = 0; bucket < h->size; bucket++) {
284 link_t *cur;
285 link_t *next;
286
287 for (cur = h->table[bucket].head.next;
288 cur != &h->table[bucket].head;
289 cur = next) {
290
291 /*
292 * The next pointer must be stored prior to the functor
293 * call to allow using destructor as the functor (the
294 * free function could overwrite the cur->next pointer).
295 */
296 next = cur->next;
297 f(cur, arg);
298 }
299 }
300}
301
302/** Remove all elements from the set.
303 *
304 * The table is reallocated to the minimum size.
305 *
306 * @param h Hash set
307 * @param f Function (destructor?) applied to all element. Can be NULL.
308 * @param arg Argument to the destructor.
309 *
310 */
311void hash_set_clear(hash_set_t *h, void (*f)(link_t *, void *), void *arg)
312{
313 assert(h);
314 assert(h->table);
315
316 for (size_t bucket = 0; bucket < h->size; bucket++) {
317 link_t *cur;
318 link_t *next;
319
320 for (cur = h->table[bucket].head.next;
321 cur != &h->table[bucket].head;
322 cur = next) {
323 next = cur->next;
324 list_remove(cur);
325 if (f != NULL)
326 f(cur, arg);
327 }
328 }
329
330 assert(h->size >= HASH_SET_MIN_SIZE);
331 list_t *new_table =
332 realloc(h->table, HASH_SET_MIN_SIZE * sizeof(list_t));
333
334 /* We are shrinking, therefore we shouldn't get NULL */
335 assert(new_table);
336
337 if (h->table != new_table) {
338 /* Init the lists, pointers to itself are used in them */
339 for (size_t bucket = 0; bucket < HASH_SET_MIN_SIZE; ++bucket)
340 list_initialize(&new_table[bucket]);
341
342 h->table = new_table;
343 }
344
345 h->count = 0;
346 h->size = HASH_SET_MIN_SIZE;
347}
348
349/** Get hash set size
350 *
351 * @param hHash set
352 *
353 * @return Number of elements in the set.
354 *
355 */
356size_t hash_set_count(const hash_set_t *h)
357{
358 assert(h);
359 return h->count;
360}
361
362/** Check whether element is contained in the hash set
363 *
364 * @param h Hash set
365 * @param item Item that should be equal to the matched object
366 *
367 * @return True if the hash set contains equal object
368 * @return False otherwise
369 *
370 */
371int hash_set_contains(const hash_set_t *h, const link_t *item)
372{
373 /*
374 * The hash_set_find cannot accept constant hash set,
375 * because we can modify the returned element. But in
376 * this case we are using it safely.
377 */
378 return hash_set_find((hash_set_t *) h, item) != NULL;
379}
380
381/** @}
382 */
Note: See TracBrowser for help on using the repository browser.