\h'-\w'\\$1\ 'u'\\$1\ \c
.ft P
..
-.ie t .ds o \(bu
-.el .ds o o
+.ie t \{\
+. ds o \(bu
+. de VP
+. sp .4v
+..
+\}
+.el \{\
+. ds o o
+. de VP
+. sp
+..
+\}
.TH hash 3 "2 August 1999" "Straylight/Edgeware" "mLib utilities library"
.SH "NAME"
hash \- low-level hashtable implementation
.SH "SYNOPSIS"
.nf
.B "#include <mLib/hash.h>"
-
+.PP
.ta 2n
.B "typedef struct {"
.B " uint32 mask;"
.B " hash_base **v;"
.B " arena *a;"
.B "} hash_table;"
-
+.PP
.B "typedef struct {"
.B " hash_base *next;"
.B " uint32 hash;"
.B "} hash_base;"
-
+.PP
.B "typedef struct { ...\& } hash_iter;"
-
+.PP
.BI "void hash_create(hash_table *" t ", size_t " n );
.BI "void hash_destroy(hash_table *" t );
.BI "hash_base **hash_bin(hash_table *" t ", uint32 " hash );
.BI "void hash_remove(hash_table *" t ", hash_base *" b );
.BI "void hash_mkiter(hash_iter *" i ", hash_table *" t );
.BI "hash_base *hash_next(hash_iter *" i );
-
+.PP
.BI "hash_base **HASH_BIN(hash_table *" t ", uint32 " hash );
.BI "void HASH_MKITER(hash_iter *" i ", hash_table *" t );
.BI "void HASH_NEXT(hash_iter *" i ", " b );
hashtables. Code examples are given throughout. They assume the
following definitions:
.VS
+.ta 2n
/* --- A table of items --- */
-
+.VP
typedef struct item_table {
- hash_table t;
- size_t load;
+ hash_table t;
+ size_t load;
};
-
+.VP
/* --- An item --- */
-
+.VP
typedef struct item {
- hash_base b;
- const char *k;
- /* ... */
+ hash_base b;
+ const char *k;
+ /* ... */
} item;
.VE
The implementation presented here is simple but relatively bad. The
.PP
For example, an item table might be initialized like this:
.VS
+.ta 2n
void item_createtab(item_table *t)
{
- hash_create(&t->t, ITEM_INITSZ);
- t->load = ITEM_INITLOAD;
+ hash_create(&t->t, ITEM_INITSZ);
+ t->load = ITEM_INITLOAD;
}
.VE
A hashtable can be destroyed by calling
The usual way to deallocate the individual hashtable items is using the
iteration constructs described below.
.VS
+.ta 2n +2n
void item_destroytab(item_table *t)
{
- hash_iter i;
- hash_base *b;
- for (hash_mkiter(&i, &t->t); (b = hash_next(&i)) != 0; ) {
- item *ii = (item *)b;
- free(ii->k);
- /* ... */
- DESTROY(ii);
- }
- hash_destroy(&t->t);
+ hash_iter i;
+ hash_base *b;
+ for (hash_mkiter(&i, &t->t); (b = hash_next(&i)) != 0; ) {
+ item *ii = (item *)b;
+ free(ii->k);
+ /* ... */
+ DESTROY(ii);
+ }
+ hash_destroy(&t->t);
}
.VE
.sp -1
Once the bin list has been found, it's fairly easy to search for an
exact match. A simple search might look something like this:
.VS
+.ta 2n +2n +2n 20m
item *lookup(item_table *t, const char *k)
{
- uint32 h = hash(k); /* Hash @k@ somehow */
- hash_base **bin = HASH_BIN(&t->t, h);
- hash_base *b;
- for (b = *bin; b; b = b->next) {
- item *i = (item *)b;
- if (h == i->b.hash && strcmp(k, i->k) == 0)
- return (i);
- }
- return (0);
+ uint32 h = hash(k); /* Hash \fIk\fP somehow */
+ hash_base **bin = HASH_BIN(&t->t, h);
+ hash_base *b;
+ for (b = *bin; b; b = b->next) {
+ item *i = (item *)b;
+ if (h == i->b.hash && strcmp(k, i->k) == 0)
+ return (i);
+ }
+ return (0);
}
.VE
Insertion is also relatively trivial given the bin list head. Insertion
which is passed only the address of the hashtable. It returns nonzero
if extension was successful.
.VS
+.ta 2n +2n
item *add(item_table *t, const char *k, /* ... */)
{
- item *i;
- uint32 h;
- hash_base **bin;
-
- /* --- See if the item is already there --- */
-
- if ((i = = lookup(t, k)) != 0)
- return (i);
-
- /* --- Make a new hashtable item --- */
-
- i = CREATE(item);
- i->k = xstrdup(k);
- /* ... */
-
- /* --- Link it into the bin list --- */
-
- h = i->b.hash = hash(k);
- bin = HASH_BIN(&t->t, h);
- i->b.next = *bin;
- *bin = &i->b.next;
-
- /* --- Maybe extend the hashtable --- */
-
- if (t->load)
- t->load--;
- else if (hash_extend(&t->t))
- t->load = recalc_load(t);
-
- /* --- Done --- */
-
- return (i);
+ item *i;
+ uint32 h;
+ hash_base **bin;
+.VP
+ /* --- See if the item is already there --- */
+.VP
+ if ((i = lookup(t, k)) != 0)
+ return (i);
+.VP
+ /* --- Make a new hashtable item --- */
+.VP
+ i = CREATE(item);
+ i->k = xstrdup(k);
+ /* ... */
+.VP
+ /* --- Link it into the bin list --- */
+.VP
+ h = i->b.hash = hash(k);
+ bin = HASH_BIN(&t->t, h);
+ i->b.next = *bin;
+ *bin = &i->b.next;
+.VP
+ /* --- Maybe extend the hashtable --- */
+.VP
+ if (t->load)
+ t->load--;
+ else if (hash_extend(&t->t))
+ t->load = recalc_load(t);
+.VP
+ /* --- Done --- */
+.VP
+ return (i);
}
.VE
The