1 /* $Id: compat_ohash.c,v 1.7 2020/06/15 01:37:15 schwarze Exp $ */
2 /* $OpenBSD: ohash.c,v 1.1 2014/06/02 18:52:03 deraadt Exp $ */
4 /* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include <sys/types.h>
26 #include "compat_ohash.h"
28 struct _ohash_record
{
33 #define DELETED ((const char *)h)
34 #define NONE (h->size)
36 /* Don't bother changing the hash table if the change is small enough. */
37 #define MINSIZE (1UL << 4)
40 static void ohash_resize(struct ohash
*);
43 /* This handles the common case of variable length keys, where the
44 * key is stored at the end of the record.
47 ohash_create_entry(struct ohash_info
*i
, const char *start
, const char **end
)
52 *end
= start
+ strlen(start
);
53 p
= (i
->alloc
)(i
->key_offset
+ (*end
- start
) + 1, i
->data
);
55 memcpy(p
+i
->key_offset
, start
, *end
-start
);
56 p
[i
->key_offset
+ (*end
- start
)] = '\0';
61 /* hash_delete only frees the hash structure. Use hash_first/hash_next
62 * to free entries as well. */
64 ohash_delete(struct ohash
*h
)
66 (h
->info
.free
)(h
->t
, h
->info
.data
);
73 ohash_resize(struct ohash
*h
)
75 struct _ohash_record
*n
;
80 if (4 * h
->deleted
< h
->total
) {
81 if (h
->size
>= (UINT_MAX
>> 1U))
85 } else if (3 * h
->deleted
> 2 * h
->total
)
93 STAT_HASH_SIZE
+= ns
- h
->size
;
96 n
= (h
->info
.calloc
)(ns
, sizeof(struct _ohash_record
), h
->info
.data
);
100 for (j
= 0; j
< h
->size
; j
++) {
101 if (h
->t
[j
].p
!= NULL
&& h
->t
[j
].p
!= DELETED
) {
103 incr
= ((h
->t
[j
].hv
% (ns
- 2)) & ~1) + 1;
104 while (n
[i
].p
!= NULL
) {
109 n
[i
].hv
= h
->t
[j
].hv
;
113 (h
->info
.free
)(h
->t
, h
->info
.data
);
116 h
->total
-= h
->deleted
;
121 ohash_remove(struct ohash
*h
, unsigned int i
)
123 void *result
= (void *)h
->t
[i
].p
;
125 if (result
== NULL
|| result
== DELETED
)
133 if (h
->deleted
>= MINDELETED
&& 4 * h
->deleted
> h
->total
)
139 ohash_find(struct ohash
*h
, unsigned int i
)
141 if (h
->t
[i
].p
== DELETED
)
144 return (void *)h
->t
[i
].p
;
148 ohash_insert(struct ohash
*h
, unsigned int i
, void *p
)
153 if (h
->t
[i
].p
== DELETED
) {
158 /* Arbitrary resize boundary. Tweak if not efficient enough. */
159 if (++h
->total
* 4 > h
->size
* 3)
166 ohash_entries(struct ohash
*h
)
168 return h
->total
- h
->deleted
;
172 ohash_first(struct ohash
*h
, unsigned int *pos
)
175 return ohash_next(h
, pos
);
179 ohash_next(struct ohash
*h
, unsigned int *pos
)
181 for (; *pos
< h
->size
; (*pos
)++)
182 if (h
->t
[*pos
].p
!= DELETED
&& h
->t
[*pos
].p
!= NULL
)
183 return (void *)h
->t
[(*pos
)++].p
;
188 ohash_init(struct ohash
*h
, unsigned int size
, struct ohash_info
*info
)
190 h
->size
= 1UL << size
;
191 if (h
->size
< MINSIZE
)
194 STAT_HASH_CREATION
++;
195 STAT_HASH_SIZE
+= h
->size
;
197 /* Copy info so that caller may free it. */
198 h
->info
.key_offset
= info
->key_offset
;
199 h
->info
.calloc
= info
->calloc
;
200 h
->info
.free
= info
->free
;
201 h
->info
.alloc
= info
->alloc
;
202 h
->info
.data
= info
->data
;
203 h
->t
= (h
->info
.calloc
)(h
->size
, sizeof(struct _ohash_record
),
205 h
->total
= h
->deleted
= 0;
209 ohash_interval(const char *s
, const char **e
)
220 k
= ((k
<< 2) | (k
>> 30)) ^ *s
++;
225 ohash_lookup_interval(struct ohash
*h
, const char *start
, const char *end
,
228 unsigned int i
, incr
;
236 incr
= ((hv
% (h
->size
-2)) & ~1) + 1;
237 while (h
->t
[i
].p
!= NULL
) {
241 if (h
->t
[i
].p
== DELETED
) {
244 } else if (h
->t
[i
].hv
== hv
&&
245 strncmp(h
->t
[i
].p
+h
->info
.key_offset
, start
,
247 (h
->t
[i
].p
+h
->info
.key_offset
)[end
-start
] == '\0') {
250 h
->t
[empty
].p
= h
->t
[i
].p
;
255 STAT_HASH_POSITIVE
++;
265 /* Found an empty position. */
273 ohash_lookup_memory(struct ohash
*h
, const char *k
, size_t size
, uint32_t hv
)
275 unsigned int i
, incr
;
283 incr
= ((hv
% (h
->size
-2)) & ~1) + 1;
284 while (h
->t
[i
].p
!= NULL
) {
288 if (h
->t
[i
].p
== DELETED
) {
291 } else if (h
->t
[i
].hv
== hv
&&
292 memcmp(h
->t
[i
].p
+h
->info
.key_offset
, k
, size
) == 0) {
295 h
->t
[empty
].p
= h
->t
[i
].p
;
300 STAT_HASH_POSITIVE
++;
309 /* Found an empty position. */
317 ohash_qlookup(struct ohash
*h
, const char *s
)
319 const char *e
= NULL
;
320 return ohash_qlookupi(h
, s
, &e
);
324 ohash_qlookupi(struct ohash
*h
, const char *s
, const char **e
)
328 hv
= ohash_interval(s
, e
);
329 return ohash_lookup_interval(h
, s
, *e
, hv
);