1 /* $OpenBSD: ohash.c,v 1.1 2014/06/02 18:52:03 deraadt Exp $ */
3 /* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 #include "compat_ohash.h"
25 struct _ohash_record
{
30 #define DELETED ((const char *)h)
31 #define NONE (h->size)
33 /* Don't bother changing the hash table if the change is small enough. */
34 #define MINSIZE (1UL << 4)
37 static void ohash_resize(struct ohash
*);
40 /* This handles the common case of variable length keys, where the
41 * key is stored at the end of the record.
44 ohash_create_entry(struct ohash_info
*i
, const char *start
, const char **end
)
49 *end
= start
+ strlen(start
);
50 p
= (i
->alloc
)(i
->key_offset
+ (*end
- start
) + 1, i
->data
);
52 memcpy(p
+i
->key_offset
, start
, *end
-start
);
53 p
[i
->key_offset
+ (*end
- start
)] = '\0';
58 /* hash_delete only frees the hash structure. Use hash_first/hash_next
59 * to free entries as well. */
61 ohash_delete(struct ohash
*h
)
63 (h
->info
.free
)(h
->t
, h
->info
.data
);
70 ohash_resize(struct ohash
*h
)
72 struct _ohash_record
*n
;
77 if (4 * h
->deleted
< h
->total
) {
78 if (h
->size
>= (UINT_MAX
>> 1U))
82 } else if (3 * h
->deleted
> 2 * h
->total
)
90 STAT_HASH_SIZE
+= ns
- h
->size
;
93 n
= (h
->info
.calloc
)(ns
, sizeof(struct _ohash_record
), h
->info
.data
);
97 for (j
= 0; j
< h
->size
; j
++) {
98 if (h
->t
[j
].p
!= NULL
&& h
->t
[j
].p
!= DELETED
) {
100 incr
= ((h
->t
[j
].hv
% (ns
- 2)) & ~1) + 1;
101 while (n
[i
].p
!= NULL
) {
106 n
[i
].hv
= h
->t
[j
].hv
;
110 (h
->info
.free
)(h
->t
, h
->info
.data
);
113 h
->total
-= h
->deleted
;
118 ohash_remove(struct ohash
*h
, unsigned int i
)
120 void *result
= (void *)h
->t
[i
].p
;
122 if (result
== NULL
|| result
== DELETED
)
130 if (h
->deleted
>= MINDELETED
&& 4 * h
->deleted
> h
->total
)
136 ohash_find(struct ohash
*h
, unsigned int i
)
138 if (h
->t
[i
].p
== DELETED
)
141 return (void *)h
->t
[i
].p
;
145 ohash_insert(struct ohash
*h
, unsigned int i
, void *p
)
150 if (h
->t
[i
].p
== DELETED
) {
155 /* Arbitrary resize boundary. Tweak if not efficient enough. */
156 if (++h
->total
* 4 > h
->size
* 3)
163 ohash_entries(struct ohash
*h
)
165 return h
->total
- h
->deleted
;
169 ohash_first(struct ohash
*h
, unsigned int *pos
)
172 return ohash_next(h
, pos
);
176 ohash_next(struct ohash
*h
, unsigned int *pos
)
178 for (; *pos
< h
->size
; (*pos
)++)
179 if (h
->t
[*pos
].p
!= DELETED
&& h
->t
[*pos
].p
!= NULL
)
180 return (void *)h
->t
[(*pos
)++].p
;
185 ohash_init(struct ohash
*h
, unsigned int size
, struct ohash_info
*info
)
187 h
->size
= 1UL << size
;
188 if (h
->size
< MINSIZE
)
191 STAT_HASH_CREATION
++;
192 STAT_HASH_SIZE
+= h
->size
;
194 /* Copy info so that caller may free it. */
195 h
->info
.key_offset
= info
->key_offset
;
196 h
->info
.calloc
= info
->calloc
;
197 h
->info
.free
= info
->free
;
198 h
->info
.alloc
= info
->alloc
;
199 h
->info
.data
= info
->data
;
200 h
->t
= (h
->info
.calloc
)(h
->size
, sizeof(struct _ohash_record
),
202 h
->total
= h
->deleted
= 0;
206 ohash_interval(const char *s
, const char **e
)
217 k
= ((k
<< 2) | (k
>> 30)) ^ *s
++;
222 ohash_lookup_interval(struct ohash
*h
, const char *start
, const char *end
,
225 unsigned int i
, incr
;
233 incr
= ((hv
% (h
->size
-2)) & ~1) + 1;
234 while (h
->t
[i
].p
!= NULL
) {
238 if (h
->t
[i
].p
== DELETED
) {
241 } else if (h
->t
[i
].hv
== hv
&&
242 strncmp(h
->t
[i
].p
+h
->info
.key_offset
, start
,
244 (h
->t
[i
].p
+h
->info
.key_offset
)[end
-start
] == '\0') {
247 h
->t
[empty
].p
= h
->t
[i
].p
;
252 STAT_HASH_POSITIVE
++;
262 /* Found an empty position. */
270 ohash_lookup_memory(struct ohash
*h
, const char *k
, size_t size
, uint32_t hv
)
272 unsigned int i
, incr
;
280 incr
= ((hv
% (h
->size
-2)) & ~1) + 1;
281 while (h
->t
[i
].p
!= NULL
) {
285 if (h
->t
[i
].p
== DELETED
) {
288 } else if (h
->t
[i
].hv
== hv
&&
289 memcmp(h
->t
[i
].p
+h
->info
.key_offset
, k
, size
) == 0) {
292 h
->t
[empty
].p
= h
->t
[i
].p
;
297 STAT_HASH_POSITIVE
++;
306 /* Found an empty position. */
314 ohash_qlookup(struct ohash
*h
, const char *s
)
316 const char *e
= NULL
;
317 return ohash_qlookupi(h
, s
, &e
);
321 ohash_qlookupi(struct ohash
*h
, const char *s
, const char **e
)
325 hv
= ohash_interval(s
, e
);
326 return ohash_lookup_interval(h
, s
, *e
, hv
);