2 * Copyright (c) 2000-2001
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 #define scan_bucket(b, var) for (var = b; var; var = var->next)
46 region r
; /* Region for this table */
47 hash_fn hash
; /* Function for hashing keys */
48 keyeq_fn cmp
; /* Function for comparing keys */
50 int size
; /* Number of buckets */
51 int elts
; /* Number of elements */
52 bool internal_rgn
; /* TRUE if the ht uses an internal region */
53 bucket
*table
; /* Array of (size) buckets */
56 static void rehash(hash_table ht
) deletes
;
58 /* Make a new hash table, with size buckets initially. The actual
59 table is allocated in a local region, which is discarded on rehashing. */
60 hash_table
make_hash_table(region r
, int size
, hash_fn hash
,
61 keyeq_fn cmp
, bool internal_rgn
)
66 result
= ralloc(r
, struct Hash_table
);
69 result
->r
= newregion();
73 result
->internal_rgn
= internal_rgn
;
78 result
->table
= rarrayalloc(result
->r
, size
, bucket
);
84 static int string_hash(char *str
)
94 h
= 33*h
+ 720 + *c
++; /* SML/NJ's string hash function */
98 /* Return TRUE iff s1 == s2 */
99 static bool string_eq(char *s1
, char *s2
)
101 return !strcmp(s1
, s2
);
104 /* Make a hash table for strings. */
105 hash_table
make_string_hash_table(region rhash
, int size
, bool internal_rgn
)
107 return make_hash_table(rhash
, size
, (hash_fn
) string_hash
,
108 (keyeq_fn
) string_eq
,internal_rgn
);
111 /* Zero out ht. Doesn't reclaim bucket space. */
112 void hash_table_reset(hash_table ht
) deletes
116 if (ht
->internal_rgn
)
123 for (i
= 0; i
< ht
->size
; i
++)
127 void hash_table_delete(hash_table ht
) deletes
129 if (ht
->internal_rgn
)
134 /* Return the number of entries in ht */
135 int hash_table_size(hash_table ht
)
140 /* Return the bucket corresponding to k in ht */
141 static inline bucket
*find_bucket(hash_table ht
, hash_key k
)
148 return &ht
->table
[hash
% ht
->size
];
151 /* Lookup k in ht. Returns corresponding data in *d, and function
152 result is TRUE if the k was in ht, false otherwise. */
153 bool hash_table_lookup(hash_table ht
, hash_key k
, hash_data
*d
)
157 cur
= *find_bucket(ht
, k
);
160 if (ht
->cmp(k
, cur
->key
))
172 /* Add k:d to ht. If k was already in ht, replace old entry by k:d.
173 Rehash if necessary. Returns TRUE if k was not already in ht. */
174 bool hash_table_insert(hash_table ht
, hash_key k
, hash_data d
) deletes
178 if (ht
->elts
> ht
->size
*15)
180 cur
= find_bucket(ht
, k
);
183 if (ht
->cmp(k
, (*cur
)->key
))
186 return FALSE
; /* Replace */
190 *cur
= ralloc(ht
->r
, struct bucket
);
195 return TRUE
; /* New key */
198 /* Remove mapping for k in ht. Returns TRUE if k was in ht. */
199 bool hash_table_remove(hash_table ht
, hash_key k
)
204 cur
= find_bucket(ht
, k
);
207 if (ht
->cmp(k
, (*cur
)->key
))
210 (*prev
)->next
= (*cur
)->next
;
222 /* Return a copy of ht */
223 hash_table
hash_table_copy(region r
, hash_table ht
)
227 bucket cur
, newbucket
, *prev
;
229 result
= make_hash_table(r
, ht
->size
, ht
->hash
, ht
->cmp
,ht
->internal_rgn
);
230 result
->elts
= ht
->elts
;
232 for (i
= 0; i
< ht
->size
; i
++)
234 prev
= &result
->table
[i
];
235 scan_bucket(ht
->table
[i
], cur
)
237 newbucket
= ralloc(result
->r
, struct bucket
);
238 newbucket
->key
= cur
->key
;
239 newbucket
->data
= cur
->data
;
240 newbucket
->next
= NULL
;
243 prev
= &newbucket
->next
;
249 hash_table_scanner hts;
253 result = make_hash_table(r, ht->size, ht->hash, ht->cmp);
254 hash_table_scan(ht, &hts);
255 while (hash_table_next(&hts, &k, &d))
256 insist(hash_table_insert(result, k, d));
262 /* Increase size of ht (double it) and reinsert all the elements */
263 static void rehash(hash_table ht
) deletes
265 int old_table_size
, i
;
266 bucket
*old_table
, cur
;
270 printf("Rehash table size=%d, elts=%d\n", ht
->size
, ht
->elts
);
273 old_table_size
= ht
->size
;
274 old_table
= ht
->table
;
277 if (ht
->internal_rgn
)
280 ht
->size
= ht
->size
*2;
282 ht
->table
= rarrayalloc(ht
->r
, ht
->size
, bucket
);
284 for (i
= 0; i
< old_table_size
; i
++)
285 scan_bucket(old_table
[i
], cur
)
286 insist(hash_table_insert(ht
, cur
->key
, cur
->data
));
288 if (ht
->internal_rgn
)
289 deleteregion(old_region
);
292 /* Begin scanning ht */
293 void hash_table_scan(hash_table ht
, hash_table_scanner
*hts
)
297 hts
->cur
= hts
->ht
->table
[0];
300 /* Get next elt in table, storing the elt in *k and *d if k and d are
301 non-NULL, respectively. Returns TRUE if there is a next elt, FALSE
303 bool hash_table_next(hash_table_scanner
*hts
, hash_key
*k
, hash_data
*d
)
305 while (hts
->cur
== NULL
)
308 if (hts
->i
< hts
->ht
->size
)
309 hts
->cur
= hts
->ht
->table
[hts
->i
];
314 if (hts
->i
== hts
->ht
->size
)
324 hts
->cur
= hts
->cur
->next
;
329 /* Apply f to all elements of ht, in some arbitrary order */
330 void hash_table_apply(hash_table ht
, hash_apply_fn f
, void *arg
)
335 for (i
= 0; i
< ht
->size
; i
++)
336 scan_bucket(ht
->table
[i
], cur
)
337 f(cur
->key
, cur
->data
, arg
);
340 /* Map f to all elements on ht, creating a new hash table */
341 hash_table
hash_table_map(hash_table ht
, hash_map_fn f
, void *arg
)
345 bucket cur
, newbucket
, *prev
;
347 result
= make_hash_table(ht
->r
, ht
->size
, ht
->hash
, ht
->cmp
,ht
->internal_rgn
);
348 result
->elts
= ht
->elts
;
350 for (i
= 0; i
< ht
->size
; i
++)
352 prev
= &result
->table
[i
];
353 scan_bucket(ht
->table
[i
], cur
)
355 newbucket
= ralloc(ht
->r
, struct bucket
);
356 newbucket
->key
= cur
->key
;
357 newbucket
->data
= f(cur
->key
, cur
->data
, arg
);
358 newbucket
->next
= NULL
;
361 prev
= &newbucket
->next
;
370 result = make_hash_table(ht->r, ht->size, ht->hash, ht->cmp);
371 for (i = 0; i < ht->size; i++)
372 scan_bucket(ht->table[i], cur)
373 insist(hash_table_insert(result, cur->key, f(cur->key, cur->data, arg)));
378 static keycmp_fn cur_cmp
= NULL
;
380 static int entry_cmp(const void *a
, const void *b
)
382 struct sorted_entry
*ae
= (struct sorted_entry
*) a
;
383 struct sorted_entry
*be
= (struct sorted_entry
*) b
;
384 return cur_cmp(ae
->k
, be
->k
);
387 /* Begin scanning ht in sorted order according to f */
388 void hash_table_scan_sorted(hash_table ht
, keycmp_fn f
,
389 hash_table_scanner_sorted
*htss
)
391 hash_table_scanner hts
;
394 htss
->r
= newregion();
395 htss
->size
= hash_table_size(ht
);
396 htss
->entries
= rarrayalloc(htss
->r
, htss
->size
, struct sorted_entry
);
399 hash_table_scan(ht
, &hts
);
401 while (hash_table_next(&hts
, &htss
->entries
[i
].k
,
402 &htss
->entries
[i
].d
))
404 assert(i
== htss
->size
);
406 qsort(htss
->entries
, htss
->size
, sizeof(struct sorted_entry
), entry_cmp
);
410 /* Just like hash_table_next, but scans in sorted order */
411 bool hash_table_next_sorted(hash_table_scanner_sorted
*htss
, hash_key
*k
,
412 hash_data
*d
) deletes
414 if (htss
->i
< htss
->size
)
416 *k
= htss
->entries
[htss
->i
].k
;
417 *d
= htss
->entries
[htss
->i
].d
;
423 deleteregion(htss
->r
);