1 /* Jitter: hash table data structure.
2
3 Copyright (C) 2017, 2018 Luca Saiu
4 Written by Luca Saiu
5
6 This file is part of Jitter.
7
8 Jitter is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 Jitter is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with Jitter. If not, see <http://www.gnu.org/licenses/>. */
20
21
22 #include <assert.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "jitter.h"
28
29 #include "jitter-malloc.h"
30 #include "jitter-fatal.h"
31 #include "jitter-hash.h"
32
33
34 /* Initial buffer sizes, in elements.
35 * ************************************************************************** */
36
37 /* How many bindings to have in a bucket at allocation time. In practice one
38 will be used immediately, at bucket allocation; the others are pre-allocated
39 to be used in the event of collisions. */
40 #define INITIAL_BINDING_NO_PER_BUCKET 4
41
42 /* How many buckets an empty hash table should have. */
43 #define INITIAL_BUCKET_NO 65
44
45
46
47
48 /* Hash table data structures.
49 * ************************************************************************** */
50
51 /* A binding is simply a pair associating a key and a value. */
52 struct jitter_hash_binding
53 {
54 /* The binding key; this should normally be a pointer. */
55 union jitter_word key;
56
57 /* The binding value, associated to the key. This can point to any value.
58 Notice that the pointed value is not automatically deallocated. */
59 union jitter_word value;
60 };
61
62 struct jitter_hash_bucket
63 {
64 /* How many bindings we can hold before resizing the bucket. */
65 size_t allocated_binding_no;
66
67 /* How many bindings we are actually holding. */
68 size_t used_binding_no;
69
70 /* A pointer to a malloc-allocated array of struct jitter_hash_binding, of
71 allocated_binding_no elements. Bindings are added to the right, and
72 searched from the right to the left; search semantics is therefore LIFO. */
73 struct jitter_hash_binding *bindings;
74 };
75
76
77
78
79 /* Hash functions.
80 * ************************************************************************** */
81
82 /* This is defined in jitter-hash-random-table.c . There is no need to declare
83 it in a header. */
84 extern const jitter_uint
85 jitter_hash_random_words [256];
86
87 /* The underlying implementation of every hash function here. Given the
88 previous state of the hash function, combine the new char to it and return
89 the next state. */
90 __attribute__ ((always_inline))
91 static inline jitter_uint
jitter_hash_combine_char(jitter_uint previous,unsigned char new)92 jitter_hash_combine_char (jitter_uint previous, unsigned char new)
93 {
94 jitter_uint state = previous;
95 state ^= (state << 1) ^ jitter_hash_random_words [new];
96 return state;
97 }
98
99 /* Hash memory starting from the given pointer, reading the given number of
100 chars. */
101 __attribute__ ((always_inline))
102 static inline jitter_uint
jitter_hash_memory_for(const void * initial_pointer,size_t char_no)103 jitter_hash_memory_for (const void *initial_pointer, size_t char_no)
104 {
105 const unsigned char *p = (const unsigned char *) initial_pointer;
106 jitter_uint res = 0;
107 int i;
108 for (i = 0; i < char_no; i ++)
109 res = jitter_hash_combine_char (res, p [i]);
110 return res;
111 }
112
113 /* Hash memory starting from the given pointer, until the given terminator byte
114 is found. */
115 __attribute__ ((always_inline))
116 static inline jitter_uint
jitter_hash_memory_until(const void * initial_pointer,unsigned char terminator)117 jitter_hash_memory_until (const void *initial_pointer,
118 unsigned char terminator)
119 {
120 const unsigned char *key = (const unsigned char *) initial_pointer;
121 jitter_uint res = 0;
122 const unsigned char *p;
123 for (p = key; *p != terminator; p ++)
124 res = jitter_hash_combine_char (res, * p);
125 return res;
126 }
127
128 jitter_uint
jitter_string_hash_function(const union jitter_word key)129 jitter_string_hash_function (const union jitter_word key)
130 {
131 return jitter_hash_memory_until (key.pointer_to_char, '\0');
132 }
133
134 jitter_uint
jitter_word_hash_function(const union jitter_word key_as_union)135 jitter_word_hash_function (const union jitter_word key_as_union)
136 {
137 const jitter_uint key = key_as_union.ufixnum;
138 return jitter_hash_memory_for (& key, sizeof (jitter_uint));
139 }
140
141
142
143
144 /* Comparison functions.
145 * ************************************************************************** */
146
147 bool
jitter_string_hash_key_equal(const union jitter_word key_1,const union jitter_word key_2)148 jitter_string_hash_key_equal (const union jitter_word key_1,
149 const union jitter_word key_2)
150 {
151 return ! strcmp (key_1.pointer_to_char, key_2.pointer_to_char);
152 }
153
154 bool
jitter_word_hash_key_equal(const union jitter_word key_1,const union jitter_word key_2)155 jitter_word_hash_key_equal (const union jitter_word key_1,
156 const union jitter_word key_2)
157 {
158 /* Of course this makes no sense if the word size is different from the union
159 size. Do a compile-time sanity check. */
160 assert (sizeof (union jitter_word) == sizeof (jitter_int));
161
162 return key_1.fixnum == key_2.fixnum;
163 }
164
165
166
167 /* Key and value functions.
168 * ************************************************************************** */
169
170 void
jitter_do_nothing_on_word(const union jitter_word key)171 jitter_do_nothing_on_word (const union jitter_word key)
172 {
173 /* Do nothing. */
174 }
175
176
177
178
179 /* Initialization and finalization.
180 * ************************************************************************** */
181
182 /* Initialize the pointed hash table. */
183 static void
jitter_hash_initialize_with_bucket_no(struct jitter_hash_table * t,size_t bucket_no)184 jitter_hash_initialize_with_bucket_no (struct jitter_hash_table *t,
185 size_t bucket_no)
186 {
187 t->bucket_no = bucket_no;
188 t->binding_no = 0;
189 t->buckets = jitter_xmalloc (sizeof (struct jitter_hash_bucket*)
190 * bucket_no);
191 int i;
192 for (i = 0; i < bucket_no; i ++)
193 t->buckets [i] = NULL;
194 }
195
196 void
jitter_hash_initialize(struct jitter_hash_table * t)197 jitter_hash_initialize (struct jitter_hash_table *t)
198 {
199 jitter_hash_initialize_with_bucket_no (t, INITIAL_BUCKET_NO);
200 }
201
202 void
jitter_hash_finalize(struct jitter_hash_table * t,jitter_word_function finalize_key,jitter_word_function finalize_value)203 jitter_hash_finalize (struct jitter_hash_table *t,
204 jitter_word_function finalize_key,
205 jitter_word_function finalize_value)
206 {
207 int i;
208 for (i = 0; i < t->bucket_no; i ++)
209 {
210 if (t->buckets [i] == NULL)
211 continue;
212
213 struct jitter_hash_bucket *bucket = t->buckets [i];
214 int j;
215 for (j = 0; j < bucket->used_binding_no; j ++)
216 {
217 struct jitter_hash_binding *binding = bucket->bindings + j;
218 if (finalize_key)
219 finalize_key (binding->key);
220 if (finalize_value)
221 finalize_value (binding->value);
222 }
223 free (bucket->bindings);
224 free (bucket);
225 }
226 free (t->buckets);
227
228 /* For defensiveness, fill the struct with invalid data. */
229 memset (t, 0xff, sizeof (struct jitter_hash_table));
230 }
231
232
233
234
235 /* Access.
236 * ************************************************************************** */
237
238 static bool
jitter_hash_bucket_has(const struct jitter_hash_bucket * b,const union jitter_word key,jitter_hash_key_equal eq)239 jitter_hash_bucket_has (const struct jitter_hash_bucket *b,
240 const union jitter_word key,
241 jitter_hash_key_equal eq)
242 {
243 jitter_uint limit = b->used_binding_no;
244 jitter_uint i;
245 struct jitter_hash_binding *bindings = b->bindings;
246 for (i = 0; i < limit; i ++)
247 if (eq (key, bindings [i].key))
248 return true;
249 return false;
250 }
251
252 static const union jitter_word
jitter_hash_bucket_get(const struct jitter_hash_bucket * b,const union jitter_word key,jitter_hash_key_equal eq)253 jitter_hash_bucket_get (const struct jitter_hash_bucket *b,
254 const union jitter_word key,
255 jitter_hash_key_equal eq)
256 {
257 jitter_uint limit = b->used_binding_no;
258 jitter_int i;
259 struct jitter_hash_binding *bindings = b->bindings;
260 for (i = limit - 1; i >= 0; i --)
261 if (eq (key, bindings [i].key))
262 return bindings [i].value;
263
264 jitter_fatal ("jitter_hash_bucket_get: unbound key");
265 }
266
267 /* Return true iff there was actually one element to remove. */
268 static bool
jitter_hash_bucket_remove(struct jitter_hash_bucket * b,const union jitter_word key,jitter_word_function key_function,jitter_word_function value_function,jitter_hash_key_equal eq)269 jitter_hash_bucket_remove (struct jitter_hash_bucket *b,
270 const union jitter_word key,
271 jitter_word_function key_function,
272 jitter_word_function value_function,
273 jitter_hash_key_equal eq)
274 {
275 jitter_uint limit = b->used_binding_no;
276 jitter_int i;
277 struct jitter_hash_binding *bindings = b->bindings;
278 for (i = limit - 1; i >= 0; i --)
279 if (eq (key, bindings [i].key))
280 {
281 if (key_function)
282 key_function (bindings [i].key);
283 if (value_function)
284 value_function (bindings [i].value);
285 memcpy (bindings + i, bindings + i + 1,
286 sizeof (struct jitter_hash_binding) * (limit - i - 1));
287 b->used_binding_no --;
288 return true;
289 }
290 return false;
291 }
292
293 bool
jitter_hash_table_has(const struct jitter_hash_table * t,const union jitter_word key,jitter_hash_function f,jitter_hash_key_equal eq)294 jitter_hash_table_has (const struct jitter_hash_table *t,
295 const union jitter_word key,
296 jitter_hash_function f,
297 jitter_hash_key_equal eq)
298 {
299 const struct jitter_hash_bucket *b = t->buckets [f (key) % t->bucket_no];
300 if (b == NULL)
301 return false;
302 else
303 return jitter_hash_bucket_has (b, key, eq);
304 }
305
306 const union jitter_word
jitter_hash_table_get(const struct jitter_hash_table * t,const union jitter_word key,jitter_hash_function f,jitter_hash_key_equal eq)307 jitter_hash_table_get (const struct jitter_hash_table *t,
308 const union jitter_word key,
309 jitter_hash_function f,
310 jitter_hash_key_equal eq)
311 {
312 const struct jitter_hash_bucket *b = t->buckets [f (key) % t->bucket_no];
313 if (b == NULL)
314 jitter_fatal ("jitter_hash_table_get: unbound key");
315
316 return jitter_hash_bucket_get (b, key, eq);
317 }
318
319 inline static bool
jitter_hash_table_overfull(struct jitter_hash_table * t)320 jitter_hash_table_overfull (struct jitter_hash_table *t)
321 {
322 /* A hash should be enlarged when it's over 75% full; assuming no collisions,
323 this means that the bindings are at least three fourths of the buckets.
324 Actually doing this check in integer arithmetic makes the table look
325 overfull a little in advance, which is all for the better. */
326 return t->binding_no >= (t->bucket_no * 3 / 4);
327 }
328
329 __attribute__ ((noinline, cold)) static void
jitter_hash_table_enlarge(struct jitter_hash_table * t,jitter_hash_function f)330 jitter_hash_table_enlarge (struct jitter_hash_table *t,
331 jitter_hash_function f)
332 {
333 /* Make a bigger table. */
334 size_t new_bucket_no = t->bucket_no * 2 + 1;
335 //printf ("enlarging the table: %li to %li (there are %li bindings)\n", (long)t->bucket_no, (long)new_bucket_no, (long)t->binding_no);
336 struct jitter_hash_table new_table;
337 jitter_hash_initialize_with_bucket_no (& new_table, new_bucket_no);
338
339 /* Copy every binding from the old table to the new. It's important to scan
340 the old buckets left-to-right, so that, even if collisions are different in
341 the new table, the elements taking precedence are still on the right in
342 each new bucket. */
343 int i;
344 for (i = 0; i < t->bucket_no; i ++)
345 {
346 struct jitter_hash_bucket *bucket = t->buckets [i];
347 if (bucket == NULL)
348 continue;
349 struct jitter_hash_binding *bindings = bucket->bindings;
350 size_t binding_no = bucket->used_binding_no;
351 int j;
352 for (j = 0; j < binding_no; j ++)
353 jitter_hash_table_add (& new_table,
354 bindings [j].key,
355 bindings [j].value,
356 f);
357 }
358
359 /* Finalize the old table, without deallocating the elements (since we share
360 them) and move the new table content to the old table. Now the old table
361 has more buckets, and all that survives of the new one is an automatic
362 variable which will be deallocated on return. */
363 jitter_hash_finalize (t, NULL, NULL);
364 memcpy (t, &new_table, sizeof (struct jitter_hash_table));
365 }
366
367 void
jitter_hash_table_add(struct jitter_hash_table * t,const union jitter_word key,const union jitter_word value,jitter_hash_function f)368 jitter_hash_table_add (struct jitter_hash_table *t,
369 const union jitter_word key,
370 const union jitter_word value,
371 jitter_hash_function f)
372 {
373 /* We only enlarge the table (when it's getting too full) on add, and never
374 shrink it. */
375 if (jitter_hash_table_overfull (t))
376 jitter_hash_table_enlarge (t, f);
377
378 t->binding_no ++;
379 jitter_uint bucket_index = f (key) % t->bucket_no;
380
381 /* Find the bucket; make it if needed. */
382 struct jitter_hash_bucket *b = t->buckets [bucket_index];
383 if (b == NULL)
384 {
385 b = jitter_xmalloc (sizeof (struct jitter_hash_bucket));
386 b->allocated_binding_no = INITIAL_BINDING_NO_PER_BUCKET;
387 b->used_binding_no = 0;
388 b->bindings
389 = jitter_xmalloc (sizeof (struct jitter_hash_binding)
390 * INITIAL_BINDING_NO_PER_BUCKET);
391 t->buckets [bucket_index] = b;
392 }
393
394 /* Find the binding where we need to write within the bucket; make place and
395 reallocate if needed. */
396 if (b->used_binding_no == b->allocated_binding_no)
397 {
398 //printf ("enlarging the %i-th bucket: %li to %li\n", (int)bucket_index, (long)b->allocated_binding_no, (long)(b->allocated_binding_no * 2));
399
400 b->bindings = jitter_xrealloc (b->bindings,
401 sizeof (struct jitter_hash_binding)
402 * (b->allocated_binding_no *= 2));
403 }
404 struct jitter_hash_binding *bi = b->bindings + (b->used_binding_no ++);
405 bi->key = key;
406 bi->value = value;
407 }
408
409 void
jitter_hash_table_remove(struct jitter_hash_table * t,const union jitter_word key,jitter_word_function key_function,jitter_word_function value_function,jitter_hash_function f,jitter_hash_key_equal eq)410 jitter_hash_table_remove (struct jitter_hash_table *t,
411 const union jitter_word key,
412 jitter_word_function key_function,
413 jitter_word_function value_function,
414 jitter_hash_function f,
415 jitter_hash_key_equal eq)
416 {
417 struct jitter_hash_bucket *b = t->buckets [f (key) % t->bucket_no];
418 if (b == NULL)
419 return;
420 else
421 {
422 if (jitter_hash_bucket_remove (b, key, key_function, value_function, eq))
423 t->binding_no --;
424 }
425 }
426
427
428
429
430 /* Hash iteration.
431 * ************************************************************************** */
432
433 void
jitter_hash_for_all_bindings(const struct jitter_hash_table * t,jitter_hash_for_all_bindings_function f,void * extra_datum)434 jitter_hash_for_all_bindings (const struct jitter_hash_table *t,
435 jitter_hash_for_all_bindings_function f,
436 void *extra_datum)
437 {
438 /* For each bucket... */
439 int i, j;
440 for (i = 0; i < t->bucket_no; i ++)
441 {
442 /* Do nothing if the bucket is NULL. */
443 struct jitter_hash_bucket * const bucket = t->buckets [i];
444 if (bucket == NULL)
445 continue;
446
447 /* For each binding in the bucket... */
448 struct jitter_hash_binding * const bindings = bucket->bindings;
449 for (j = 0; j < bucket->used_binding_no; j ++)
450 /* ... Call the function provided by the user. */
451 f (bindings [j].key, bindings [j].value, extra_datum);
452 }
453 }
454
455
456
457 /* Debugging and tuning.
458 * ************************************************************************** */
459
460 /* Return the square of the given number. */
461 static double
square(double x)462 square (double x)
463 {
464 return x * x;
465 }
466
467 /* Print information about collisions. */
468 void
jitter_hash_print_debug_stats(const struct jitter_hash_table * t)469 jitter_hash_print_debug_stats (const struct jitter_hash_table *t)
470 {
471 size_t min_bucket_size = t->bucket_no + 1;
472 size_t min_nonempty_bucket_size = t->bucket_no + 1;
473 size_t max_bucket_size = 0;
474 size_t nonempty_bucket_no = 0;
475 int i;
476 for (i = 0; i < t->bucket_no; i ++)
477 {
478 struct jitter_hash_bucket *b = t->buckets [i];
479 size_t used_size = b != NULL ? b->used_binding_no : 0;
480 if (used_size > 0)
481 nonempty_bucket_no ++;
482 if (used_size > max_bucket_size)
483 max_bucket_size = used_size;
484 if (used_size < min_bucket_size)
485 min_bucket_size = used_size;
486 if (used_size > 0 && used_size < min_nonempty_bucket_size)
487 min_nonempty_bucket_size = used_size;
488 }
489 double bucket_size_mean = t->binding_no / (double) t->bucket_no;
490 double nonempty_bucket_size_mean
491 = t->binding_no / (double) nonempty_bucket_no;
492
493 double bucket_size_variance = 0;
494 double nonempty_bucket_size_variance = 0;
495 for (i = 0; i < t->bucket_no; i ++)
496 {
497 struct jitter_hash_bucket *b = t->buckets [i];
498 size_t used_size = b != NULL ? b->used_binding_no : 0;
499 bucket_size_variance += square (used_size - bucket_size_mean);
500 if (used_size > 0)
501 nonempty_bucket_size_variance
502 += square (used_size - nonempty_bucket_size_mean);
503 }
504 bucket_size_variance /= t->bucket_no;
505 nonempty_bucket_size_variance /= nonempty_bucket_no;
506
507 printf ("Binding no: %lu\n",
508 (unsigned long) t->binding_no);
509 printf ("Fill factor or bucket size mean: %f\n", bucket_size_mean);
510 printf ("Bucket no: %lu\n",
511 (unsigned long) t->bucket_no);
512 printf ("Nonempty bucket no: %lu\n",
513 (unsigned long) nonempty_bucket_no);
514 printf ("Minimum bucket size: %lu\n",
515 (unsigned long) min_bucket_size);
516 printf ("Minimum nonempty bucket size: %lu\n",
517 (unsigned long) min_nonempty_bucket_size);
518 printf ("Nonempty bucket size mean: %f\n", nonempty_bucket_size_mean);
519 printf ("Nonempty bucket size variance: %f\n",
520 nonempty_bucket_size_variance);
521 printf ("Bucket size variance: %f\n", bucket_size_variance);
522 printf ("Maximum bucket size: %lu\n",
523 (unsigned long) max_bucket_size);
524 }
525
526
527
528
529 /* String hash utility.
530 * ************************************************************************** */
531
532 static void
jitter_string_hash_key_function(union jitter_word key)533 jitter_string_hash_key_function (union jitter_word key)
534 {
535 free (key.pointer_to_char);
536 }
537
538 inline static union jitter_word
jitter_string_to_word(const char * s)539 jitter_string_to_word (const char *s)
540 {
541 union jitter_word w = {.pointer_to_char = (char*) s};
542 return w;
543 }
544
545 inline static union jitter_word
jitter_clone_string_into_word(const char * s)546 jitter_clone_string_into_word (const char *s)
547 {
548 char *copy_of_s = jitter_xmalloc (strlen (s) + 1);
549 strcpy (copy_of_s, s);
550 union jitter_word w = {.pointer_to_char = (char*) copy_of_s};
551 return w;
552 }
553
554 bool
jitter_string_hash_table_has(const struct jitter_hash_table * t,const char * s)555 jitter_string_hash_table_has (const struct jitter_hash_table *t,
556 const char *s)
557 {
558 return jitter_hash_table_has (t,
559 jitter_string_to_word (s),
560 jitter_string_hash_function,
561 jitter_string_hash_key_equal);
562 }
563
564 const union jitter_word
jitter_string_hash_table_get(const struct jitter_hash_table * t,const char * s)565 jitter_string_hash_table_get (const struct jitter_hash_table *t,
566 const char *s)
567 {
568 return jitter_hash_table_get (t,
569 jitter_string_to_word (s),
570 jitter_string_hash_function,
571 jitter_string_hash_key_equal);
572 }
573
574 void
jitter_string_hash_table_add(struct jitter_hash_table * t,const char * s,const union jitter_word value)575 jitter_string_hash_table_add (struct jitter_hash_table *t,
576 const char *s,
577 const union jitter_word value)
578 {
579 jitter_hash_table_add (t,
580 jitter_clone_string_into_word (s),
581 value,
582 jitter_string_hash_function);
583 }
584
585 void
jitter_string_hash_table_remove(struct jitter_hash_table * t,const char * s,jitter_word_function value_function)586 jitter_string_hash_table_remove (struct jitter_hash_table *t,
587 const char *s,
588 jitter_word_function value_function)
589 {
590 jitter_hash_table_remove (t,
591 jitter_string_to_word (s),
592 jitter_string_hash_key_function,
593 value_function,
594 jitter_string_hash_function,
595 jitter_string_hash_key_equal);
596 }
597
598 void
jitter_string_hash_finalize(struct jitter_hash_table * t,jitter_word_function finalize_value)599 jitter_string_hash_finalize (struct jitter_hash_table *t,
600 jitter_word_function finalize_value)
601 {
602 jitter_hash_finalize (t, jitter_string_hash_key_function, finalize_value);
603 }
604
605
606
607
608 /* Word hash utility.
609 * ************************************************************************** */
610
611 static void
jitter_word_hash_key_function(union jitter_word key)612 jitter_word_hash_key_function (union jitter_word key)
613 {
614 /* Do nothing: word keys don't use heap allocation. */
615 }
616
617 /* Convert a jitter_int to a union jitter_word. */
618 inline static union jitter_word
jitter_int_to_word(jitter_int k)619 jitter_int_to_word (jitter_int k)
620 {
621 union jitter_word w = {.fixnum = k};
622 return w;
623 }
624
625 bool
jitter_word_hash_table_has(const struct jitter_hash_table * t,jitter_int k)626 jitter_word_hash_table_has (const struct jitter_hash_table *t, jitter_int k)
627 {
628 return jitter_hash_table_has (t,
629 jitter_int_to_word (k),
630 jitter_word_hash_function,
631 jitter_word_hash_key_equal);
632 }
633
634 const union jitter_word
jitter_word_hash_table_get(const struct jitter_hash_table * t,jitter_int k)635 jitter_word_hash_table_get (const struct jitter_hash_table *t, jitter_int k)
636 {
637 return jitter_hash_table_get (t,
638 jitter_int_to_word (k),
639 jitter_word_hash_function,
640 jitter_word_hash_key_equal);
641 }
642
643 void
jitter_word_hash_table_add(struct jitter_hash_table * t,jitter_int k,const union jitter_word value)644 jitter_word_hash_table_add (struct jitter_hash_table *t,
645 jitter_int k,
646 const union jitter_word value)
647 {
648 jitter_hash_table_add (t,
649 jitter_int_to_word (k),
650 value,
651 jitter_word_hash_function);
652 }
653
654 void
jitter_word_hash_table_remove(struct jitter_hash_table * t,jitter_int k,jitter_word_function value_function)655 jitter_word_hash_table_remove (struct jitter_hash_table *t,
656 jitter_int k,
657 jitter_word_function value_function)
658 {
659 jitter_hash_table_remove (t,
660 jitter_int_to_word (k),
661 jitter_word_hash_key_function,
662 value_function,
663 jitter_word_hash_function,
664 jitter_word_hash_key_equal);
665 }
666
667 void
jitter_word_hash_finalize(struct jitter_hash_table * t,jitter_word_function finalize_value)668 jitter_word_hash_finalize (struct jitter_hash_table *t,
669 jitter_word_function finalize_value)
670 {
671 jitter_hash_finalize (t, jitter_word_hash_key_function, finalize_value);
672 }
673