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, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 * hash.c - simple in-memory hashing routines
23 * hashinit() - initialize a hash table, returning a handle
24 * hashitem() - find a record in the table, and optionally enter a new one
25 * hashdone() - free a hash table, given its handle
29 * hashrehash() - resize and rebuild hp->tab, the hash table
37 static uint32_t hash_joaat (const void *key
, size_t nbytes
) {
39 const uint8_t *k
= (const uint8_t *)key
;
40 while (nbytes
-- > 0) {
52 /* header attached to all data items entered into a hash table. */
55 uint32_t keyval
; /* for quick comparisons */
59 /* this structure overlays the one handed to hashenter() */
60 /* it's actual size is given to hashinit() */
63 /* rest of user data */
73 #define MAX_LISTS (32)
75 /* the hash table, just an array of item pointers */
80 int bloat
; /* tab.nel / items.nel */
81 int inel
; /* initial number of elements */
82 /* the array of records, maintained by these routines essentially a microallocator */
84 int more
; /* how many more ITEMs fit in lists[ list ] */
85 char *next
; /* where to put more ITEMs in lists[ list ] */
86 int datalen
; /* length of records in this hash table */
87 int size
; /* sizeof(ITEM) + aligned datalen */
88 int nel
; /* total ITEMs held by all lists[] */
89 int list
; /* index into lists[] */
91 int nel
; /* total ITEMs held by this list */
92 char *base
; /* base of ITEMs array */
95 char *name
; /* just for hashstats() */
99 #define ALIGNED(x) ((x+sizeof(ITEM)-1)&(~(sizeof(ITEM)-1)))
102 static void hashstat (struct hash
*hp
) {
103 ITEM
**tab
= hp
->tab
.base
;
104 int nel
= hp
->tab
.nel
, count
= 0, sets
= 0;
105 int run
= (tab
[nel
-1] != NULL
);
107 for (i
= nel
; i
> 0; --i
) {
108 if ((here
= (*tab
++ != NULL
))) ++count
;
109 if (here
&& !run
) ++sets
;
112 printf("%s table: %d+%d+%d (%dK+%dK) items+table+hash, %f density\n",
117 hp
->items
.nel
*hp
->items
.size
/1024,
118 (int)(hp
->tab
.nel
*sizeof(ITEM
**)/1024),
119 (float)count
/(float)sets
125 * hashrehash() - resize and rebuild hp->tab, the hash table
127 static void hashrehash (struct hash
*hp
) {
128 int i
= ++hp
->items
.list
;
129 hp
->items
.more
= (i
? 2*hp
->items
.nel
: hp
->inel
);
130 hp
->items
.next
= malloc(hp
->items
.more
*hp
->items
.size
);
131 hp
->items
.lists
[i
].nel
= hp
->items
.more
;
132 hp
->items
.lists
[i
].base
= hp
->items
.next
;
133 hp
->items
.nel
+= hp
->items
.more
;
134 if (hp
->tab
.base
) free(hp
->tab
.base
);
135 hp
->tab
.nel
= hp
->items
.nel
*hp
->bloat
;
136 hp
->tab
.base
= malloc(hp
->tab
.nel
*sizeof(ITEM
**));
137 memset(hp
->tab
.base
, '\0', hp
->tab
.nel
*sizeof(ITEM
*));
138 for (i
= 0; i
< hp
->items
.list
; ++i
) {
139 int nel
= hp
->items
.lists
[i
].nel
;
140 char *next
= hp
->items
.lists
[i
].base
;
141 for (; nel
--; next
+= hp
->items
.size
) {
142 ITEM
*i
= (ITEM
*)next
;
143 ITEM
**ip
= hp
->tab
.base
+i
->hdr
.keyval
%hp
->tab
.nel
;
152 * hashiterate() - iterate thru all hash entries
153 * return !0 from itercb() to stop
154 * returns result of itercb() or 0 */
155 int hashiterate (struct hash
*hp
, hash_iterator_cb itercb
, void *udata
) {
156 ITEM
**tab
= hp
->tab
.base
;
157 for (int i
= hp
->tab
.nel
; i
> 0; --i
, ++tab
) {
159 int res
= itercb(&((*tab
)->data
), udata
);
168 * hashitem() - find a record in the table, and optionally enter a new one
170 int hashitem (struct hash
*hp
, HASHDATA
**data
, int enter
) {
173 if (enter
&& !hp
->items
.more
) hashrehash(hp
);
174 if (!enter
&& !hp
->items
.nel
) return 0;
175 keyval
= hash_joaat((*data
)->key
, strlen((*data
)->key
));
176 base
= hp
->tab
.base
+(keyval
%hp
->tab
.nel
);
177 for (i
= *base
; i
!= NULL
; i
= i
->hdr
.next
) {
178 if (keyval
== i
->hdr
.keyval
&& strcmp(i
->data
.key
, (*data
)->key
) == 0) {
184 i
= (ITEM
*)hp
->items
.next
;
185 hp
->items
.next
+= hp
->items
.size
;
187 memcpy(&i
->data
, *data
, hp
->items
.datalen
);
188 i
->hdr
.keyval
= keyval
;
198 * hashinit() - initialize a hash table, returning a handle
200 struct hash
*hashinit (int datalen
, const char *name
) {
201 struct hash
*hp
= malloc(sizeof(*hp
));
206 hp
->items
.datalen
= datalen
;
207 hp
->items
.size
= sizeof(struct hashhdr
)+ALIGNED(datalen
);
211 hp
->name
= strdup(name
!= NULL
? name
: "");
217 * hashdone() - free a hash table, given its handle
219 void hashdone (struct hash
*hp
) {
221 if (DEBUG_MEM
) hashstat(hp
);
222 if (hp
->tab
.base
) free((char *)hp
->tab
.base
);
223 for (int i
= 0; i
<= hp
->items
.list
; i
++) free(hp
->items
.lists
[i
].base
);