5164ffe2e209291388fafcbf812ae57940ed7233
[rrq/rrqmisc.git] / pvector / hashvector.c
1 #include "hashvector.h"
2
3 // Find the slot for the keyed element, and return pointer to it, or
4 // to the first of holes encountered while considering collisions.
5 // Returns a pointer to the place for the item, or 0 in case of OOM or
6 // overfull hashvector (i.e. 0 shouldn't happen).
7 static void **hashvector_find_slot(hashvector *hv,void *key) {
8     unsigned long index = hv->keyhashcode( key ) % hv->table.size;
9     unsigned long i = index;
10     void **hole = 0;
11     void **p = 0;
12     for ( ;; ) {
13         p = pvector_entry(&hv->table, i);
14         if ( p == 0 ) {
15             return 0; // This basically means OOM, and is a failure condition.
16         }
17         if ( *p == 0 ) {
18             break; // Not found
19         }
20         if ( (*p) == HV_HOLE ) {
21             if ( hole == 0 ) {
22                 hole = p; // Remember the first hole
23             }
24         } else if ( hv->haskey( *p, key ) ) {
25             return p; // Found
26         }
27         if ( ++i == hv->table.size ) {
28             i = 0; // Roll-around the table
29         }
30         if ( i == index ) {
31             return 0; // Overfull hashvector!
32         }
33     }
34     return ( hole )? hole : p;
35 }
36
37 // Find the keyed element, and assign the x pointer, or assign 0.
38 // Returns 1 if element is found and 0 otherwise.
39 int hashvector_find(hashvector *hv,void *key,void **x) {
40     void **p = hashvector_find_slot( hv, key );
41     if ( p && *p && *p != HV_HOLE ) {
42         if ( x ) {
43             *x = *p;
44         }
45         return 1;
46     }
47     return 0;
48 }
49
50 static int capture_item(pvector *pv,unsigned long ix,void *item,void *data) {
51     if ( item != HV_HOLE ) {
52         hashvector_add( (hashvector *) data, item );
53     }
54     return 0;
55 }
56
57 static int iter_item(unsigned long ix,void *item,void *data) {
58     if ( item && item != HV_HOLE ) {
59         hashvector_add( (hashvector *) data, item );
60     }
61     return 0;
62 }
63
64 static void hashvector_resize(hashvector *hv,unsigned long new_size) {
65     hashvector tmp = *hv;
66     hv->table.size = new_size;
67     hv->table.entries = 0;
68     hv->fill = 0;
69     hv->holes = 0;
70     if ( new_size < hv->table.size ) {
71         pvector_resize( &tmp.table, 0, capture_item, hv );
72     } else {
73         pvector_iterate( &tmp.table, iter_item, hv );
74     }
75 }
76     
77 // Add the given element.
78 int hashvector_add(hashvector *hv,void *item) {
79     void **p = hashvector_find_slot( hv, hv->itemkey( item ) );
80     if ( p == 0 ) {
81         return -1; // OOM or overfull hashvector
82     }
83     if ( *p ) {
84         if ( *p != HV_HOLE ) {
85             return 0; // Already added.
86         }
87         hv->holes--; // about to reuse a hole
88     }
89     *p = item;
90     hv->fill++;
91     if ( hv->fill + hv->holes > hv->table.size / 2 ) {
92         hashvector_resize( hv, hv->table.size * 2 );
93     }
94     return 1;
95 }
96
97 // Delete the given item
98 int hashvector_delete(hashvector *hv,void *item) {
99     void **p = hashvector_find_slot( hv, hv->itemkey( item ) );
100     if ( p == 0 ) {
101         return -1;
102     }
103     if ( *p != item ) {
104         return 0;
105     }
106     *p = HV_HOLE;
107     hv->holes++;
108     if ( hv->table.size > 256 ) {
109         if ( hv->fill < hv->table.size / 2 ) {
110             hashvector_resize( hv, hv->table.size / 2 );
111         }
112     }
113     return 1;
114 }
115
116 // Copy items into a pvector. Returns 0 on success and -1 on failure.
117 int hashvector_contents(hashvector *hv,pvector *pv) {
118     if ( pvector_resize( pv, hv->fill, 0, 0 ) ) {
119         return -1;
120     }
121     unsigned long from = 0;
122     unsigned long to = 0;
123     while ( to < hv->fill ) {
124         void **slot = pvector_next_used( &hv->table, &from, 0, 0 );
125         if ( slot ) {
126             if ( *slot != HV_HOLE ) {
127                 pvector_set( pv, to++, *slot );
128             }
129             from++;
130         } else {
131             break;
132         }
133     }
134     return 0;
135 }
136
137 // A simple binary hashcode, (before modulo table size)
138 unsigned long hashvector_hashcode(unsigned char *key,unsigned long n) {
139     unsigned long value = 5381;
140     while ( n-- ) {
141         value += ( value << 5 ) + *(key++);
142     }
143     return value;
144 }
145