2 * Copyright 1993, 1995 Christopher Seiwald.
3 * This file is part of Jam - see jam.c for Copyright information.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 * hash.c - simple in-memory hashing routines
22 * hashinit() - initialize a hash table, returning a handle
23 * hashitem() - find a record in the table, and optionally enter a new one
24 * hashdone() - free a hash table, given its handle
28 * hashrehash() - resize and rebuild hp->tab, the hash table
36 static uint32_t hash_joaat (const void *key
, size_t nbytes
) {
38 const uint8_t *k
= (const uint8_t *)key
;
39 while (nbytes
-- > 0) {
51 /* header attached to all data items entered into a hash table. */
54 uint32_t keyval
; /* for quick comparisons */
58 /* this structure overlays the one handed to hashenter() */
59 /* it's actual size is given to hashinit() */
62 /* rest of user data */
72 #define MAX_LISTS (32)
74 /* the hash table, just an array of item pointers */
79 int bloat
; /* tab.nel / items.nel */
80 int inel
; /* initial number of elements */
81 /* the array of records, maintained by these routines essentially a microallocator */
83 int more
; /* how many more ITEMs fit in lists[ list ] */
84 char *next
; /* where to put more ITEMs in lists[ list ] */
85 int datalen
; /* length of records in this hash table */
86 int size
; /* sizeof(ITEM) + aligned datalen */
87 int nel
; /* total ITEMs held by all lists[] */
88 int list
; /* index into lists[] */
90 int nel
; /* total ITEMs held by this list */
91 char *base
; /* base of ITEMs array */
94 char *name
; /* just for hashstats() */
98 #define ALIGNED(x) ((x+sizeof(ITEM)-1)&(~(sizeof(ITEM)-1)))
101 static void hashstat (struct hash
*hp
) {
102 ITEM
**tab
= hp
->tab
.base
;
103 int nel
= hp
->tab
.nel
, count
= 0, sets
= 0;
104 int run
= (tab
[nel
-1] != NULL
);
106 for (i
= nel
; i
> 0; --i
) {
107 if ((here
= (*tab
++ != NULL
))) ++count
;
108 if (here
&& !run
) ++sets
;
111 printf("%s table: %d+%d+%d (%dK+%dK) items+table+hash, %f density\n",
116 hp
->items
.nel
*hp
->items
.size
/1024,
117 (int)(hp
->tab
.nel
*sizeof(ITEM
**)/1024),
118 (float)count
/(float)sets
124 * hashrehash() - resize and rebuild hp->tab, the hash table
126 static void hashrehash (struct hash
*hp
) {
127 int i
= ++hp
->items
.list
;
128 hp
->items
.more
= (i
? 2*hp
->items
.nel
: hp
->inel
);
129 hp
->items
.next
= malloc(hp
->items
.more
*hp
->items
.size
);
130 hp
->items
.lists
[i
].nel
= hp
->items
.more
;
131 hp
->items
.lists
[i
].base
= hp
->items
.next
;
132 hp
->items
.nel
+= hp
->items
.more
;
133 if (hp
->tab
.base
) free(hp
->tab
.base
);
134 hp
->tab
.nel
= hp
->items
.nel
*hp
->bloat
;
135 hp
->tab
.base
= malloc(hp
->tab
.nel
*sizeof(ITEM
**));
136 memset(hp
->tab
.base
, '\0', hp
->tab
.nel
*sizeof(ITEM
*));
137 for (i
= 0; i
< hp
->items
.list
; ++i
) {
138 int nel
= hp
->items
.lists
[i
].nel
;
139 char *next
= hp
->items
.lists
[i
].base
;
140 for (; nel
--; next
+= hp
->items
.size
) {
141 ITEM
*xxi
= (ITEM
*)next
;
142 ITEM
**ip
= hp
->tab
.base
+xxi
->hdr
.keyval
%hp
->tab
.nel
;
151 * hashiterate() - iterate thru all hash entries
152 * return !0 from itercb() to stop
153 * returns result of itercb() or 0 */
154 int hashiterate (struct hash
*hp
, hash_iterator_cb itercb
, void *udata
) {
155 ITEM
**tab
= hp
->tab
.base
;
156 for (int i
= hp
->tab
.nel
; i
> 0; --i
, ++tab
) {
158 int res
= itercb(&((*tab
)->data
), udata
);
167 * hashitem() - find a record in the table, and optionally enter a new one
169 int hashitem (struct hash
*hp
, HASHDATA
**data
, int enter
) {
172 if (enter
&& !hp
->items
.more
) hashrehash(hp
);
173 if (!enter
&& !hp
->items
.nel
) return 0;
174 keyval
= hash_joaat((*data
)->key
, strlen((*data
)->key
));
175 base
= hp
->tab
.base
+(keyval
%hp
->tab
.nel
);
176 for (i
= *base
; i
!= NULL
; i
= i
->hdr
.next
) {
177 if (keyval
== i
->hdr
.keyval
&& strcmp(i
->data
.key
, (*data
)->key
) == 0) {
183 i
= (ITEM
*)hp
->items
.next
;
184 hp
->items
.next
+= hp
->items
.size
;
186 memcpy(&i
->data
, *data
, hp
->items
.datalen
);
187 i
->hdr
.keyval
= keyval
;
197 * hashinit() - initialize a hash table, returning a handle
199 struct hash
*hashinit (int datalen
, const char *name
) {
200 struct hash
*hp
= malloc(sizeof(*hp
));
205 hp
->items
.datalen
= datalen
;
206 hp
->items
.size
= sizeof(struct hashhdr
)+ALIGNED(datalen
);
210 hp
->name
= strdup(name
!= NULL
? name
: "");
216 * hashdone() - free a hash table, given its handle
218 void hashdone (struct hash
*hp
) {
220 if (DEBUG_MEM
) hashstat(hp
);
221 if (hp
->tab
.base
) free((char *)hp
->tab
.base
);
222 for (int i
= 0; i
<= hp
->items
.list
; i
++) free(hp
->items
.lists
[i
].base
);