blob: 226d680fc7828700514683f22edd462b99ebc7a1 [file] [log] [blame]
Alexandre Lision67916dd2014-01-24 13:33:04 -05001/* $Id$ */
2/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include <pj/hash.h>
21#include <pj/log.h>
22#include <pj/string.h>
23#include <pj/pool.h>
24#include <pj/os.h>
25#include <pj/ctype.h>
26#include <pj/assert.h>
27
28/**
29 * The hash multiplier used to calculate hash value.
30 */
31#define PJ_HASH_MULTIPLIER 33
32
33
34struct pj_hash_entry
35{
36 struct pj_hash_entry *next;
37 void *key;
38 pj_uint32_t hash;
39 pj_uint32_t keylen;
40 void *value;
41};
42
43
44struct pj_hash_table_t
45{
46 pj_hash_entry **table;
47 unsigned count, rows;
48 pj_hash_iterator_t iterator;
49};
50
51
52
53PJ_DEF(pj_uint32_t) pj_hash_calc(pj_uint32_t hash, const void *key,
54 unsigned keylen)
55{
56 PJ_CHECK_STACK();
57
58 if (keylen==PJ_HASH_KEY_STRING) {
59 const pj_uint8_t *p = (const pj_uint8_t*)key;
60 for ( ; *p; ++p ) {
61 hash = (hash * PJ_HASH_MULTIPLIER) + *p;
62 }
63 } else {
64 const pj_uint8_t *p = (const pj_uint8_t*)key,
65 *end = p + keylen;
66 for ( ; p!=end; ++p) {
67 hash = (hash * PJ_HASH_MULTIPLIER) + *p;
68 }
69 }
70 return hash;
71}
72
73PJ_DEF(pj_uint32_t) pj_hash_calc_tolower( pj_uint32_t hval,
74 char *result,
75 const pj_str_t *key)
76{
77 long i;
78
79#if defined(PJ_HASH_USE_OWN_TOLOWER) && PJ_HASH_USE_OWN_TOLOWER != 0
80 for (i=0; i<key->slen; ++i) {
81 pj_uint8_t c = key->ptr[i];
82 char lower;
83 if (c & 64)
84 lower = (char)(c | 32);
85 else
86 lower = (char)c;
87 if (result)
88 result[i] = lower;
89 hval = hval * PJ_HASH_MULTIPLIER + lower;
90 }
91#else
92 for (i=0; i<key->slen; ++i) {
93 char lower = (char)pj_tolower(key->ptr[i]);
94 if (result)
95 result[i] = lower;
96 hval = hval * PJ_HASH_MULTIPLIER + lower;
97 }
98#endif
99
100 return hval;
101}
102
103
104PJ_DEF(pj_hash_table_t*) pj_hash_create(pj_pool_t *pool, unsigned size)
105{
106 pj_hash_table_t *h;
107 unsigned table_size;
108
109 /* Check that PJ_HASH_ENTRY_BUF_SIZE is correct. */
110 PJ_ASSERT_RETURN(sizeof(pj_hash_entry)<=PJ_HASH_ENTRY_BUF_SIZE, NULL);
111
112 h = PJ_POOL_ALLOC_T(pool, pj_hash_table_t);
113 h->count = 0;
114
115 PJ_LOG( 6, ("hashtbl", "hash table %p created from pool %s", h, pj_pool_getobjname(pool)));
116
117 /* size must be 2^n - 1.
118 round-up the size to this rule, except when size is 2^n, then size
119 will be round-down to 2^n-1.
120 */
121 table_size = 8;
122 do {
123 table_size <<= 1;
124 } while (table_size < size);
125 table_size -= 1;
126
127 h->rows = table_size;
128 h->table = (pj_hash_entry**)
129 pj_pool_calloc(pool, table_size+1, sizeof(pj_hash_entry*));
130 return h;
131}
132
133static pj_hash_entry **find_entry( pj_pool_t *pool, pj_hash_table_t *ht,
134 const void *key, unsigned keylen,
135 void *val, pj_uint32_t *hval,
136 void *entry_buf, pj_bool_t lower)
137{
138 pj_uint32_t hash;
139 pj_hash_entry **p_entry, *entry;
140
141 if (hval && *hval != 0) {
142 hash = *hval;
143 if (keylen==PJ_HASH_KEY_STRING) {
144 keylen = (unsigned)pj_ansi_strlen((const char*)key);
145 }
146 } else {
147 /* This slightly differs with pj_hash_calc() because we need
148 * to get the keylen when keylen is PJ_HASH_KEY_STRING.
149 */
150 hash=0;
151 if (keylen==PJ_HASH_KEY_STRING) {
152 const pj_uint8_t *p = (const pj_uint8_t*)key;
153 for ( ; *p; ++p ) {
154 if (lower)
155 hash = hash * PJ_HASH_MULTIPLIER + pj_tolower(*p);
156 else
157 hash = hash * PJ_HASH_MULTIPLIER + *p;
158 }
159 keylen = (unsigned)(p - (const unsigned char*)key);
160 } else {
161 const pj_uint8_t *p = (const pj_uint8_t*)key,
162 *end = p + keylen;
163 for ( ; p!=end; ++p) {
164 if (lower)
165 hash = hash * PJ_HASH_MULTIPLIER + pj_tolower(*p);
166 else
167 hash = hash * PJ_HASH_MULTIPLIER + *p;
168 }
169 }
170
171 /* Report back the computed hash. */
172 if (hval)
173 *hval = hash;
174 }
175
176 /* scan the linked list */
177 for (p_entry = &ht->table[hash & ht->rows], entry=*p_entry;
178 entry;
179 p_entry = &entry->next, entry = *p_entry)
180 {
181 if (entry->hash==hash && entry->keylen==keylen &&
182 ((lower && pj_ansi_strnicmp((const char*)entry->key,
183 (const char*)key, keylen)==0) ||
184 (!lower && pj_memcmp(entry->key, key, keylen)==0)))
185 {
186 break;
187 }
188 }
189
190 if (entry || val==NULL)
191 return p_entry;
192
193 /* Entry not found, create a new one.
194 * If entry_buf is specified, use it. Otherwise allocate from pool.
195 */
196 if (entry_buf) {
197 entry = (pj_hash_entry*)entry_buf;
198 } else {
199 /* Pool must be specified! */
200 PJ_ASSERT_RETURN(pool != NULL, NULL);
201
202 entry = PJ_POOL_ALLOC_T(pool, pj_hash_entry);
203 PJ_LOG(6, ("hashtbl",
204 "%p: New p_entry %p created, pool used=%u, cap=%u",
205 ht, entry, pj_pool_get_used_size(pool),
206 pj_pool_get_capacity(pool)));
207 }
208 entry->next = NULL;
209 entry->hash = hash;
210 if (pool) {
211 entry->key = pj_pool_alloc(pool, keylen);
212 pj_memcpy(entry->key, key, keylen);
213 } else {
214 entry->key = (void*)key;
215 }
216 entry->keylen = keylen;
217 entry->value = val;
218 *p_entry = entry;
219
220 ++ht->count;
221
222 return p_entry;
223}
224
225PJ_DEF(void *) pj_hash_get( pj_hash_table_t *ht,
226 const void *key, unsigned keylen,
227 pj_uint32_t *hval)
228{
229 pj_hash_entry *entry;
230 entry = *find_entry( NULL, ht, key, keylen, NULL, hval, NULL, PJ_FALSE);
231 return entry ? entry->value : NULL;
232}
233
234PJ_DEF(void *) pj_hash_get_lower( pj_hash_table_t *ht,
235 const void *key, unsigned keylen,
236 pj_uint32_t *hval)
237{
238 pj_hash_entry *entry;
239 entry = *find_entry( NULL, ht, key, keylen, NULL, hval, NULL, PJ_TRUE);
240 return entry ? entry->value : NULL;
241}
242
243static void hash_set( pj_pool_t *pool, pj_hash_table_t *ht,
244 const void *key, unsigned keylen, pj_uint32_t hval,
245 void *value, void *entry_buf, pj_bool_t lower )
246{
247 pj_hash_entry **p_entry;
248
249 p_entry = find_entry( pool, ht, key, keylen, value, &hval, entry_buf,
250 lower);
251 if (*p_entry) {
252 if (value == NULL) {
253 /* delete entry */
254 PJ_LOG(6, ("hashtbl", "%p: p_entry %p deleted", ht, *p_entry));
255 *p_entry = (*p_entry)->next;
256 --ht->count;
257
258 } else {
259 /* overwrite */
260 (*p_entry)->value = value;
261 PJ_LOG(6, ("hashtbl", "%p: p_entry %p value set to %p", ht,
262 *p_entry, value));
263 }
264 }
265}
266
267PJ_DEF(void) pj_hash_set( pj_pool_t *pool, pj_hash_table_t *ht,
268 const void *key, unsigned keylen, pj_uint32_t hval,
269 void *value )
270{
271 hash_set(pool, ht, key, keylen, hval, value, NULL, PJ_FALSE);
272}
273
274PJ_DEF(void) pj_hash_set_lower( pj_pool_t *pool, pj_hash_table_t *ht,
275 const void *key, unsigned keylen,
276 pj_uint32_t hval, void *value )
277{
278 hash_set(pool, ht, key, keylen, hval, value, NULL, PJ_TRUE);
279}
280
281PJ_DEF(void) pj_hash_set_np( pj_hash_table_t *ht,
282 const void *key, unsigned keylen,
283 pj_uint32_t hval, pj_hash_entry_buf entry_buf,
284 void *value)
285{
286 hash_set(NULL, ht, key, keylen, hval, value, (void *)entry_buf, PJ_FALSE);
287}
288
289PJ_DEF(void) pj_hash_set_np_lower( pj_hash_table_t *ht,
290 const void *key, unsigned keylen,
291 pj_uint32_t hval,
292 pj_hash_entry_buf entry_buf,
293 void *value)
294{
295 hash_set(NULL, ht, key, keylen, hval, value, (void *)entry_buf, PJ_TRUE);
296}
297
298PJ_DEF(unsigned) pj_hash_count( pj_hash_table_t *ht )
299{
300 return ht->count;
301}
302
303PJ_DEF(pj_hash_iterator_t*) pj_hash_first( pj_hash_table_t *ht,
304 pj_hash_iterator_t *it )
305{
306 it->index = 0;
307 it->entry = NULL;
308
309 for (; it->index <= ht->rows; ++it->index) {
310 it->entry = ht->table[it->index];
311 if (it->entry) {
312 break;
313 }
314 }
315
316 return it->entry ? it : NULL;
317}
318
319PJ_DEF(pj_hash_iterator_t*) pj_hash_next( pj_hash_table_t *ht,
320 pj_hash_iterator_t *it )
321{
322 it->entry = it->entry->next;
323 if (it->entry) {
324 return it;
325 }
326
327 for (++it->index; it->index <= ht->rows; ++it->index) {
328 it->entry = ht->table[it->index];
329 if (it->entry) {
330 break;
331 }
332 }
333
334 return it->entry ? it : NULL;
335}
336
337PJ_DEF(void*) pj_hash_this( pj_hash_table_t *ht, pj_hash_iterator_t *it )
338{
339 PJ_CHECK_STACK();
340 PJ_UNUSED_ARG(ht);
341 return it->entry->value;
342}
343
344#if 0
345void pj_hash_dump_collision( pj_hash_table_t *ht )
346{
347 unsigned min=0xFFFFFFFF, max=0;
348 unsigned i;
349 char line[120];
350 int len, totlen = 0;
351
352 for (i=0; i<=ht->rows; ++i) {
353 unsigned count = 0;
354 pj_hash_entry *entry = ht->table[i];
355 while (entry) {
356 ++count;
357 entry = entry->next;
358 }
359 if (count < min)
360 min = count;
361 if (count > max)
362 max = count;
363 len = pj_snprintf( line+totlen, sizeof(line)-totlen, "%3d:%3d ", i, count);
364 if (len < 1)
365 break;
366 totlen += len;
367
368 if ((i+1) % 10 == 0) {
369 line[totlen] = '\0';
370 PJ_LOG(4,(__FILE__, line));
371 }
372 }
373
374 PJ_LOG(4,(__FILE__,"Count: %d, min: %d, max: %d\n", ht->count, min, max));
375}
376#endif
377
378