summaryrefslogtreecommitdiffstats
path: root/lib/kernel/hash.c
diff options
context:
space:
mode:
authormanuel <manuel@mausz.at>2012-03-27 11:51:08 +0200
committermanuel <manuel@mausz.at>2012-03-27 11:51:08 +0200
commit4f670845ff9ab6c48bcb5f7bf4d4ef6dc3c3064b (patch)
tree868c52e06f207b5ec8a3cc141f4b8b2bdfcc165c /lib/kernel/hash.c
parenteae0bd57f0a26314a94785061888d193d186944a (diff)
downloadprogos-4f670845ff9ab6c48bcb5f7bf4d4ef6dc3c3064b.tar.gz
progos-4f670845ff9ab6c48bcb5f7bf4d4ef6dc3c3064b.tar.bz2
progos-4f670845ff9ab6c48bcb5f7bf4d4ef6dc3c3064b.zip
reorganize file structure to match the upstream requirements
Diffstat (limited to 'lib/kernel/hash.c')
-rw-r--r--lib/kernel/hash.c430
1 files changed, 430 insertions, 0 deletions
diff --git a/lib/kernel/hash.c b/lib/kernel/hash.c
new file mode 100644
index 0000000..57eed45
--- /dev/null
+++ b/lib/kernel/hash.c
@@ -0,0 +1,430 @@
1/* Hash table.
2
3 This data structure is thoroughly documented in the Tour of
4 Pintos for Project 3.
5
6 See hash.h for basic information. */
7
8#include "hash.h"
9#include "../debug.h"
10#include "threads/malloc.h"
11
12#define list_elem_to_hash_elem(LIST_ELEM) \
13 list_entry(LIST_ELEM, struct hash_elem, list_elem)
14
15static struct list *find_bucket (struct hash *, struct hash_elem *);
16static struct hash_elem *find_elem (struct hash *, struct list *,
17 struct hash_elem *);
18static void insert_elem (struct hash *, struct list *, struct hash_elem *);
19static void remove_elem (struct hash *, struct hash_elem *);
20static void rehash (struct hash *);
21
22/* Initializes hash table H to compute hash values using HASH and
23 compare hash elements using LESS, given auxiliary data AUX. */
24bool
25hash_init (struct hash *h,
26 hash_hash_func *hash, hash_less_func *less, void *aux)
27{
28 h->elem_cnt = 0;
29 h->bucket_cnt = 4;
30 h->buckets = malloc (sizeof *h->buckets * h->bucket_cnt);
31 h->hash = hash;
32 h->less = less;
33 h->aux = aux;
34
35 if (h->buckets != NULL)
36 {
37 hash_clear (h, NULL);
38 return true;
39 }
40 else
41 return false;
42}
43
44/* Removes all the elements from H.
45
46 If DESTRUCTOR is non-null, then it is called for each element
47 in the hash. DESTRUCTOR may, if appropriate, deallocate the
48 memory used by the hash element. However, modifying hash
49 table H while hash_clear() is running, using any of the
50 functions hash_clear(), hash_destroy(), hash_insert(),
51 hash_replace(), or hash_delete(), yields undefined behavior,
52 whether done in DESTRUCTOR or elsewhere. */
53void
54hash_clear (struct hash *h, hash_action_func *destructor)
55{
56 size_t i;
57
58 for (i = 0; i < h->bucket_cnt; i++)
59 {
60 struct list *bucket = &h->buckets[i];
61
62 if (destructor != NULL)
63 while (!list_empty (bucket))
64 {
65 struct list_elem *list_elem = list_pop_front (bucket);
66 struct hash_elem *hash_elem = list_elem_to_hash_elem (list_elem);
67 destructor (hash_elem, h->aux);
68 }
69
70 list_init (bucket);
71 }
72
73 h->elem_cnt = 0;
74}
75
76/* Destroys hash table H.
77
78 If DESTRUCTOR is non-null, then it is first called for each
79 element in the hash. DESTRUCTOR may, if appropriate,
80 deallocate the memory used by the hash element. However,
81 modifying hash table H while hash_clear() is running, using
82 any of the functions hash_clear(), hash_destroy(),
83 hash_insert(), hash_replace(), or hash_delete(), yields
84 undefined behavior, whether done in DESTRUCTOR or
85 elsewhere. */
86void
87hash_destroy (struct hash *h, hash_action_func *destructor)
88{
89 if (destructor != NULL)
90 hash_clear (h, destructor);
91 free (h->buckets);
92}
93
94/* Inserts NEW into hash table H and returns a null pointer, if
95 no equal element is already in the table.
96 If an equal element is already in the table, returns it
97 without inserting NEW. */
98struct hash_elem *
99hash_insert (struct hash *h, struct hash_elem *new)
100{
101 struct list *bucket = find_bucket (h, new);
102 struct hash_elem *old = find_elem (h, bucket, new);
103
104 if (old == NULL)
105 insert_elem (h, bucket, new);
106
107 rehash (h);
108
109 return old;
110}
111
112/* Inserts NEW into hash table H, replacing any equal element
113 already in the table, which is returned. */
114struct hash_elem *
115hash_replace (struct hash *h, struct hash_elem *new)
116{
117 struct list *bucket = find_bucket (h, new);
118 struct hash_elem *old = find_elem (h, bucket, new);
119
120 if (old != NULL)
121 remove_elem (h, old);
122 insert_elem (h, bucket, new);
123
124 rehash (h);
125
126 return old;
127}
128
129/* Finds and returns an element equal to E in hash table H, or a
130 null pointer if no equal element exists in the table. */
131struct hash_elem *
132hash_find (struct hash *h, struct hash_elem *e)
133{
134 return find_elem (h, find_bucket (h, e), e);
135}
136
137/* Finds, removes, and returns an element equal to E in hash
138 table H. Returns a null pointer if no equal element existed
139 in the table.
140
141 If the elements of the hash table are dynamically allocated,
142 or own resources that are, then it is the caller's
143 responsibility to deallocate them. */
144struct hash_elem *
145hash_delete (struct hash *h, struct hash_elem *e)
146{
147 struct hash_elem *found = find_elem (h, find_bucket (h, e), e);
148 if (found != NULL)
149 {
150 remove_elem (h, found);
151 rehash (h);
152 }
153 return found;
154}
155
156/* Calls ACTION for each element in hash table H in arbitrary
157 order.
158 Modifying hash table H while hash_apply() is running, using
159 any of the functions hash_clear(), hash_destroy(),
160 hash_insert(), hash_replace(), or hash_delete(), yields
161 undefined behavior, whether done from ACTION or elsewhere. */
162void
163hash_apply (struct hash *h, hash_action_func *action)
164{
165 size_t i;
166
167 ASSERT (action != NULL);
168
169 for (i = 0; i < h->bucket_cnt; i++)
170 {
171 struct list *bucket = &h->buckets[i];
172 struct list_elem *elem, *next;
173
174 for (elem = list_begin (bucket); elem != list_end (bucket); elem = next)
175 {
176 next = list_next (elem);
177 action (list_elem_to_hash_elem (elem), h->aux);
178 }
179 }
180}
181
182/* Initializes I for iterating hash table H.
183
184 Iteration idiom:
185
186 struct hash_iterator i;
187
188 hash_first (&i, h);
189 while (hash_next (&i))
190 {
191 struct foo *f = hash_entry (hash_cur (&i), struct foo, elem);
192 ...do something with f...
193 }
194
195 Modifying hash table H during iteration, using any of the
196 functions hash_clear(), hash_destroy(), hash_insert(),
197 hash_replace(), or hash_delete(), invalidates all
198 iterators. */
199void
200hash_first (struct hash_iterator *i, struct hash *h)
201{
202 ASSERT (i != NULL);
203 ASSERT (h != NULL);
204
205 i->hash = h;
206 i->bucket = i->hash->buckets;
207 i->elem = list_elem_to_hash_elem (list_head (i->bucket));
208}
209
210/* Advances I to the next element in the hash table and returns
211 it. Returns a null pointer if no elements are left. Elements
212 are returned in arbitrary order.
213
214 Modifying a hash table H during iteration, using any of the
215 functions hash_clear(), hash_destroy(), hash_insert(),
216 hash_replace(), or hash_delete(), invalidates all
217 iterators. */
218struct hash_elem *
219hash_next (struct hash_iterator *i)
220{
221 ASSERT (i != NULL);
222
223 i->elem = list_elem_to_hash_elem (list_next (&i->elem->list_elem));
224 while (i->elem == list_elem_to_hash_elem (list_end (i->bucket)))
225 {
226 if (++i->bucket >= i->hash->buckets + i->hash->bucket_cnt)
227 {
228 i->elem = NULL;
229 break;
230 }
231 i->elem = list_elem_to_hash_elem (list_begin (i->bucket));
232 }
233
234 return i->elem;
235}
236
237/* Returns the current element in the hash table iteration, or a
238 null pointer at the end of the table. Undefined behavior
239 after calling hash_first() but before hash_next(). */
240struct hash_elem *
241hash_cur (struct hash_iterator *i)
242{
243 return i->elem;
244}
245
246/* Returns the number of elements in H. */
247size_t
248hash_size (struct hash *h)
249{
250 return h->elem_cnt;
251}
252
253/* Returns true if H contains no elements, false otherwise. */
254bool
255hash_empty (struct hash *h)
256{
257 return h->elem_cnt == 0;
258}
259
260/* Fowler-Noll-Vo hash constants, for 32-bit word sizes. */
261#define FNV_32_PRIME 16777619u
262#define FNV_32_BASIS 2166136261u
263
264/* Returns a hash of the SIZE bytes in BUF. */
265unsigned
266hash_bytes (const void *buf_, size_t size)
267{
268 /* Fowler-Noll-Vo 32-bit hash, for bytes. */
269 const unsigned char *buf = buf_;
270 unsigned hash;
271
272 ASSERT (buf != NULL);
273
274 hash = FNV_32_BASIS;
275 while (size-- > 0)
276 hash = (hash * FNV_32_PRIME) ^ *buf++;
277
278 return hash;
279}
280
281/* Returns a hash of string S. */
282unsigned
283hash_string (const char *s_)
284{
285 const unsigned char *s = (const unsigned char *) s_;
286 unsigned hash;
287
288 ASSERT (s != NULL);
289
290 hash = FNV_32_BASIS;
291 while (*s != '\0')
292 hash = (hash * FNV_32_PRIME) ^ *s++;
293
294 return hash;
295}
296
297/* Returns a hash of integer I. */
298unsigned
299hash_int (int i)
300{
301 return hash_bytes (&i, sizeof i);
302}
303
304/* Returns the bucket in H that E belongs in. */
305static struct list *
306find_bucket (struct hash *h, struct hash_elem *e)
307{
308 size_t bucket_idx = h->hash (e, h->aux) & (h->bucket_cnt - 1);
309 return &h->buckets[bucket_idx];
310}
311
312/* Searches BUCKET in H for a hash element equal to E. Returns
313 it if found or a null pointer otherwise. */
314static struct hash_elem *
315find_elem (struct hash *h, struct list *bucket, struct hash_elem *e)
316{
317 struct list_elem *i;
318
319 for (i = list_begin (bucket); i != list_end (bucket); i = list_next (i))
320 {
321 struct hash_elem *hi = list_elem_to_hash_elem (i);
322 if (!h->less (hi, e, h->aux) && !h->less (e, hi, h->aux))
323 return hi;
324 }
325 return NULL;
326}
327
328/* Returns X with its lowest-order bit set to 1 turned off. */
329static inline size_t
330turn_off_least_1bit (size_t x)
331{
332 return x & (x - 1);
333}
334
335/* Returns true if X is a power of 2, otherwise false. */
336static inline size_t
337is_power_of_2 (size_t x)
338{
339 return x != 0 && turn_off_least_1bit (x) == 0;
340}
341
342/* Element per bucket ratios. */
343#define MIN_ELEMS_PER_BUCKET 1 /* Elems/bucket < 1: reduce # of buckets. */
344#define BEST_ELEMS_PER_BUCKET 2 /* Ideal elems/bucket. */
345#define MAX_ELEMS_PER_BUCKET 4 /* Elems/bucket > 4: increase # of buckets. */
346
347/* Changes the number of buckets in hash table H to match the
348 ideal. This function can fail because of an out-of-memory
349 condition, but that'll just make hash accesses less efficient;
350 we can still continue. */
351static void
352rehash (struct hash *h)
353{
354 size_t old_bucket_cnt, new_bucket_cnt;
355 struct list *new_buckets, *old_buckets;
356 size_t i;
357
358 ASSERT (h != NULL);
359
360 /* Save old bucket info for later use. */
361 old_buckets = h->buckets;
362 old_bucket_cnt = h->bucket_cnt;
363
364 /* Calculate the number of buckets to use now.
365 We want one bucket for about every BEST_ELEMS_PER_BUCKET.
366 We must have at least four buckets, and the number of
367 buckets must be a power of 2. */
368 new_bucket_cnt = h->elem_cnt / BEST_ELEMS_PER_BUCKET;
369 if (new_bucket_cnt < 4)
370 new_bucket_cnt = 4;
371 while (!is_power_of_2 (new_bucket_cnt))
372 new_bucket_cnt = turn_off_least_1bit (new_bucket_cnt);
373
374 /* Don't do anything if the bucket count wouldn't change. */
375 if (new_bucket_cnt == old_bucket_cnt)
376 return;
377
378 /* Allocate new buckets and initialize them as empty. */
379 new_buckets = malloc (sizeof *new_buckets * new_bucket_cnt);
380 if (new_buckets == NULL)
381 {
382 /* Allocation failed. This means that use of the hash table will
383 be less efficient. However, it is still usable, so
384 there's no reason for it to be an error. */
385 return;
386 }
387 for (i = 0; i < new_bucket_cnt; i++)
388 list_init (&new_buckets[i]);
389
390 /* Install new bucket info. */
391 h->buckets = new_buckets;
392 h->bucket_cnt = new_bucket_cnt;
393
394 /* Move each old element into the appropriate new bucket. */
395 for (i = 0; i < old_bucket_cnt; i++)
396 {
397 struct list *old_bucket;
398 struct list_elem *elem, *next;
399
400 old_bucket = &old_buckets[i];
401 for (elem = list_begin (old_bucket);
402 elem != list_end (old_bucket); elem = next)
403 {
404 struct list *new_bucket
405 = find_bucket (h, list_elem_to_hash_elem (elem));
406 next = list_next (elem);
407 list_remove (elem);
408 list_push_front (new_bucket, elem);
409 }
410 }
411
412 free (old_buckets);
413}
414
415/* Inserts E into BUCKET (in hash table H). */
416static void
417insert_elem (struct hash *h, struct list *bucket, struct hash_elem *e)
418{
419 h->elem_cnt++;
420 list_push_front (bucket, &e->list_elem);
421}
422
423/* Removes E from hash table H. */
424static void
425remove_elem (struct hash *h, struct hash_elem *e)
426{
427 h->elem_cnt--;
428 list_remove (&e->list_elem);
429}
430