CS318 - Pintos
Pintos source browser for JHU CS318 course
hash.c
Go to the documentation of this file.
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 
15 static struct list *find_bucket (struct hash *, struct hash_elem *);
16 static struct hash_elem *find_elem (struct hash *, struct list *,
17  struct hash_elem *);
18 static void insert_elem (struct hash *, struct list *, struct hash_elem *);
19 static void remove_elem (struct hash *, struct hash_elem *);
20 static 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. */
24 bool
25 hash_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. */
53 void
54 hash_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);
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. */
86 void
87 hash_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. */
98 struct hash_elem *
99 hash_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. */
114 struct hash_elem *
115 hash_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. */
131 struct hash_elem *
132 hash_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. */
144 struct hash_elem *
145 hash_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. */
162 void
163 hash_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. */
199 void
200 hash_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;
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. */
218 struct hash_elem *
220 {
221  ASSERT (i != NULL);
222 
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  }
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(). */
240 struct hash_elem *
242 {
243  return i->elem;
244 }
245 
246 /** Returns the number of elements in H. */
247 size_t
248 hash_size (struct hash *h)
249 {
250  return h->elem_cnt;
251 }
252 
253 /** Returns true if H contains no elements, false otherwise. */
254 bool
255 hash_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. */
265 unsigned
266 hash_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. */
282 unsigned
283 hash_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. */
298 unsigned
299 hash_int (int i)
300 {
301  return hash_bytes (&i, sizeof i);
302 }
303 
304 /** Returns the bucket in H that E belongs in. */
305 static struct list *
306 find_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. */
314 static struct hash_elem *
315 find_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. */
329 static inline size_t
331 {
332  return x & (x - 1);
333 }
334 
335 /** Returns true if X is a power of 2, otherwise false. */
336 static inline size_t
337 is_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. */
351 static void
352 rehash (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). */
416 static void
417 insert_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. */
424 static void
425 remove_elem (struct hash *h, struct hash_elem *e)
426 {
427  h->elem_cnt--;
428  list_remove (&e->list_elem);
429 }
430 
hash_hash_func
unsigned hash_hash_func(const struct hash_elem *e, void *aux)
Computes and returns the hash value for hash element E, given auxiliary data AUX.
Definition: hash.h:45
malloc
void * malloc(size_t size)
Obtains and returns a new block of at least SIZE bytes.
Definition: malloc.c:90
hash_empty
bool hash_empty(struct hash *h)
Returns true if H contains no elements, false otherwise.
Definition: hash.c:255
hash_elem
Hash table.
Definition: hash.h:29
hash::hash
hash_hash_func * hash
Hash function.
Definition: hash.h:64
list_elem
Doubly linked list.
Definition: list.h:90
hash_init
bool hash_init(struct hash *h, hash_hash_func *hash, hash_less_func *less, void *aux)
Initializes hash table H to compute hash values using HASH and compare hash elements using LESS,...
Definition: hash.c:25
hash_string
unsigned hash_string(const char *s_)
Returns a hash of string S.
Definition: hash.c:283
hash_iterator
A hash table iterator.
Definition: hash.h:70
hash_int
unsigned hash_int(int i)
Returns a hash of integer I.
Definition: hash.c:299
NULL
#define NULL
Definition: stddef.h:4
hash
Hash table.
Definition: hash.h:59
list_init
void list_init(struct list *list)
Initializes LIST as an empty list.
Definition: list.c:61
hash_cur
struct hash_elem * hash_cur(struct hash_iterator *i)
Returns the current element in the hash table iteration, or a null pointer at the end of the table.
Definition: hash.c:241
buf
static char buf[BUF_SIZE]
Definition: child-syn-read.c:16
hash_elem::list_elem
struct list_elem list_elem
Definition: hash.h:31
hash_size
size_t hash_size(struct hash *h)
Returns the number of elements in H.
Definition: hash.c:248
list_next
struct list_elem * list_next(struct list_elem *elem)
Returns the element after ELEM in its list.
Definition: list.c:82
hash_less_func
bool hash_less_func(const struct hash_elem *a, const struct hash_elem *b, void *aux)
Compares the value of two hash elements A and B, given auxiliary data AUX.
Definition: hash.h:50
free
void free(void *p)
Frees block P, which must have been previously allocated with malloc(), calloc(), or realloc().
Definition: malloc.c:219
hash::elem_cnt
size_t elem_cnt
Number of elements in table.
Definition: hash.h:61
hash_destroy
void hash_destroy(struct hash *h, hash_action_func *destructor)
Destroys hash table H.
Definition: hash.c:87
hash::aux
void * aux
Auxiliary data for ‘hash’ and ‘less’.
Definition: hash.h:66
rehash
static void rehash(struct hash *)
Changes the number of buckets in hash table H to match the ideal.
Definition: hash.c:352
list_remove
struct list_elem * list_remove(struct list_elem *elem)
Removes ELEM from its list and returns the element that followed it.
Definition: list.c:249
hash_apply
void hash_apply(struct hash *h, hash_action_func *action)
Calls ACTION for each element in hash table H in arbitrary order.
Definition: hash.c:163
next
static int next(int pos)
Returns the position after POS within an intq.
Definition: intq.c:79
hash_insert
struct hash_elem * hash_insert(struct hash *h, struct hash_elem *new)
Inserts NEW into hash table H and returns a null pointer, if no equal element is already in the table...
Definition: hash.c:99
insert_elem
static void insert_elem(struct hash *, struct list *, struct hash_elem *)
Inserts E into BUCKET (in hash table H).
Definition: hash.c:417
hash_iterator::bucket
struct list * bucket
Current bucket.
Definition: hash.h:73
list_head
struct list_elem * list_head(struct list *list)
Return's LIST's head.
Definition: list.c:151
remove_elem
static void remove_elem(struct hash *, struct hash_elem *)
Removes E from hash table H.
Definition: hash.c:425
hash::less
hash_less_func * less
Comparison function.
Definition: hash.h:65
find_elem
static struct hash_elem * find_elem(struct hash *, struct list *, struct hash_elem *)
Searches BUCKET in H for a hash element equal to E.
Definition: hash.c:315
malloc.h
hash_find
struct hash_elem * hash_find(struct hash *h, struct hash_elem *e)
Finds and returns an element equal to E in hash table H, or a null pointer if no equal element exists...
Definition: hash.c:132
list_end
struct list_elem * list_end(struct list *list)
Returns LIST's tail.
Definition: list.c:94
list_empty
bool list_empty(struct list *list)
Returns true if LIST is empty, false otherwise.
Definition: list.c:310
list_begin
struct list_elem * list_begin(struct list *list)
Returns the beginning of LIST.
Definition: list.c:72
x
static char x
Verifies that mapping over the data segment is disallowed.
Definition: mmap-over-data.c:9
ASSERT
#define ASSERT(CONDITION)
This is outside the header guard so that debug.h may be included multiple times with different settin...
Definition: debug.h:31
list_pop_front
struct list_elem * list_pop_front(struct list *list)
Removes the front element from LIST and returns it.
Definition: list.c:260
hash::buckets
struct list * buckets
Array of ‘bucket_cnt’ lists.
Definition: hash.h:63
hash_action_func
void hash_action_func(struct hash_elem *e, void *aux)
Performs some operation on hash element E, given auxiliary data AUX.
Definition: hash.h:56
hash_bytes
unsigned hash_bytes(const void *buf_, size_t size)
Returns a hash of the SIZE bytes in BUF.
Definition: hash.c:266
list_push_front
void list_push_front(struct list *list, struct list_elem *elem)
Inserts ELEM at the beginning of LIST, so that it becomes the front in LIST.
Definition: list.c:209
BEST_ELEMS_PER_BUCKET
#define BEST_ELEMS_PER_BUCKET
Ideal elems/bucket.
Definition: hash.c:344
s
static uint8_t s[256]
RC4-based pseudo-random number generator (PRNG).
Definition: random.c:17
is_power_of_2
static size_t is_power_of_2(size_t x)
Returns true if X is a power of 2, otherwise false.
Definition: hash.c:337
hash::bucket_cnt
size_t bucket_cnt
Number of buckets, a power of 2.
Definition: hash.h:62
hash_first
void hash_first(struct hash_iterator *i, struct hash *h)
Initializes I for iterating hash table H.
Definition: hash.c:200
hash_delete
struct hash_elem * hash_delete(struct hash *h, struct hash_elem *e)
Finds, removes, and returns an element equal to E in hash table H.
Definition: hash.c:145
hash_iterator::elem
struct hash_elem * elem
Current hash element in current bucket.
Definition: hash.h:74
hash.h
hash_iterator::hash
struct hash * hash
The hash table.
Definition: hash.h:72
hash_replace
struct hash_elem * hash_replace(struct hash *h, struct hash_elem *new)
Inserts NEW into hash table H, replacing any equal element already in the table, which is returned.
Definition: hash.c:115
FNV_32_BASIS
#define FNV_32_BASIS
Definition: hash.c:262
FNV_32_PRIME
#define FNV_32_PRIME
Fowler-Noll-Vo hash constants, for 32-bit word sizes.
Definition: hash.c:261
turn_off_least_1bit
static size_t turn_off_least_1bit(size_t x)
Returns X with its lowest-order bit set to 1 turned off.
Definition: hash.c:330
list_elem_to_hash_elem
#define list_elem_to_hash_elem(LIST_ELEM)
Hash table.
Definition: hash.c:12
hash_clear
void hash_clear(struct hash *h, hash_action_func *destructor)
Removes all the elements from H.
Definition: hash.c:54
list
List.
Definition: list.h:97
find_bucket
static struct list * find_bucket(struct hash *, struct hash_elem *)
Returns the bucket in H that E belongs in.
Definition: hash.c:306
hash_next
struct hash_elem * hash_next(struct hash_iterator *i)
Advances I to the next element in the hash table and returns it.
Definition: hash.c:219