blob: 6d31fd8481e2d8b38bef74d64cb9a6b288d465d0 [file] [log] [blame]
Benny Prijono9033e312005-11-21 02:08:39 +00001/* $Id$ */
2/*
Benny Prijono844653c2008-12-23 17:27:53 +00003 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijono9033e312005-11-21 02:08:39 +00005 *
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>
Benny Prijono40f2f642006-01-30 18:40:05 +000026#include <pj/assert.h>
Benny Prijono9033e312005-11-21 02:08:39 +000027
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;
Benny Prijono0e1d35c2007-09-08 07:12:44 +000037 void *key;
Benny Prijono9033e312005-11-21 02:08:39 +000038 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
Benny Prijono42c5b9e2006-05-10 19:24:40 +000053PJ_DEF(pj_uint32_t) pj_hash_calc(pj_uint32_t hash, const void *key,
54 unsigned keylen)
Benny Prijono9033e312005-11-21 02:08:39 +000055{
56 PJ_CHECK_STACK();
57
58 if (keylen==PJ_HASH_KEY_STRING) {
Benny Prijonof260e462007-04-30 21:03:32 +000059 const pj_uint8_t *p = (const pj_uint8_t*)key;
Benny Prijono9033e312005-11-21 02:08:39 +000060 for ( ; *p; ++p ) {
Benny Prijono42c5b9e2006-05-10 19:24:40 +000061 hash = (hash * PJ_HASH_MULTIPLIER) + *p;
Benny Prijono9033e312005-11-21 02:08:39 +000062 }
Benny Prijono9033e312005-11-21 02:08:39 +000063 } else {
Benny Prijonof260e462007-04-30 21:03:32 +000064 const pj_uint8_t *p = (const pj_uint8_t*)key,
65 *end = p + keylen;
Benny Prijono9033e312005-11-21 02:08:39 +000066 for ( ; p!=end; ++p) {
Benny Prijono42c5b9e2006-05-10 19:24:40 +000067 hash = (hash * PJ_HASH_MULTIPLIER) + *p;
Benny Prijono9033e312005-11-21 02:08:39 +000068 }
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
Benny Prijonob6eab2c2006-07-03 22:08:47 +000079#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 if (c & 64)
83 result[i] = (char)(c | 32);
84 else
85 result[i] = (char)c;
86 hval = hval * PJ_HASH_MULTIPLIER + result[i];
87 }
88#else
Benny Prijono9033e312005-11-21 02:08:39 +000089 for (i=0; i<key->slen; ++i) {
90 result[i] = (char)pj_tolower(key->ptr[i]);
91 hval = hval * PJ_HASH_MULTIPLIER + result[i];
92 }
Benny Prijonob6eab2c2006-07-03 22:08:47 +000093#endif
Benny Prijono9033e312005-11-21 02:08:39 +000094
95 return hval;
96}
97
98
99PJ_DEF(pj_hash_table_t*) pj_hash_create(pj_pool_t *pool, unsigned size)
100{
101 pj_hash_table_t *h;
102 unsigned table_size;
103
Benny Prijono17e58ed2007-05-28 11:49:46 +0000104 /* Check that PJ_HASH_ENTRY_BUF_SIZE is correct. */
105 PJ_ASSERT_RETURN(sizeof(pj_hash_entry)<=PJ_HASH_ENTRY_BUF_SIZE, NULL);
Benny Prijono40f2f642006-01-30 18:40:05 +0000106
Benny Prijonof260e462007-04-30 21:03:32 +0000107 h = PJ_POOL_ALLOC_T(pool, pj_hash_table_t);
Benny Prijono9033e312005-11-21 02:08:39 +0000108 h->count = 0;
109
Benny Prijonoc81dfef2006-01-07 18:41:22 +0000110 PJ_LOG( 6, ("hashtbl", "hash table %p created from pool %s", h, pj_pool_getobjname(pool)));
Benny Prijono9033e312005-11-21 02:08:39 +0000111
112 /* size must be 2^n - 1.
113 round-up the size to this rule, except when size is 2^n, then size
114 will be round-down to 2^n-1.
115 */
116 table_size = 8;
117 do {
118 table_size <<= 1;
Benny Prijono3ae5f062006-10-03 17:13:22 +0000119 } while (table_size < size);
Benny Prijono9033e312005-11-21 02:08:39 +0000120 table_size -= 1;
121
122 h->rows = table_size;
Benny Prijonof260e462007-04-30 21:03:32 +0000123 h->table = (pj_hash_entry**)
124 pj_pool_calloc(pool, table_size+1, sizeof(pj_hash_entry*));
Benny Prijono9033e312005-11-21 02:08:39 +0000125 return h;
126}
127
128static pj_hash_entry **find_entry( pj_pool_t *pool, pj_hash_table_t *ht,
129 const void *key, unsigned keylen,
Benny Prijono40f2f642006-01-30 18:40:05 +0000130 void *val, pj_uint32_t *hval,
131 void *entry_buf)
Benny Prijono9033e312005-11-21 02:08:39 +0000132{
133 pj_uint32_t hash;
134 pj_hash_entry **p_entry, *entry;
135
Benny Prijono40f2f642006-01-30 18:40:05 +0000136 if (hval && *hval != 0) {
137 hash = *hval;
Benny Prijono07066b02007-04-11 07:48:03 +0000138 if (keylen==PJ_HASH_KEY_STRING) {
139 keylen = pj_ansi_strlen((const char*)key);
140 }
Benny Prijono9033e312005-11-21 02:08:39 +0000141 } else {
Benny Prijono40f2f642006-01-30 18:40:05 +0000142 /* This slightly differs with pj_hash_calc() because we need
143 * to get the keylen when keylen is PJ_HASH_KEY_STRING.
144 */
145 hash=0;
146 if (keylen==PJ_HASH_KEY_STRING) {
Benny Prijonof260e462007-04-30 21:03:32 +0000147 const pj_uint8_t *p = (const pj_uint8_t*)key;
Benny Prijono40f2f642006-01-30 18:40:05 +0000148 for ( ; *p; ++p ) {
149 hash = hash * PJ_HASH_MULTIPLIER + *p;
150 }
151 keylen = p - (const unsigned char*)key;
152 } else {
Benny Prijonof260e462007-04-30 21:03:32 +0000153 const pj_uint8_t *p = (const pj_uint8_t*)key,
154 *end = p + keylen;
Benny Prijono40f2f642006-01-30 18:40:05 +0000155 for ( ; p!=end; ++p) {
156 hash = hash * PJ_HASH_MULTIPLIER + *p;
157 }
Benny Prijono9033e312005-11-21 02:08:39 +0000158 }
Benny Prijono40f2f642006-01-30 18:40:05 +0000159
160 /* Report back the computed hash. */
161 if (hval)
162 *hval = hash;
Benny Prijono9033e312005-11-21 02:08:39 +0000163 }
164
165 /* scan the linked list */
166 for (p_entry = &ht->table[hash & ht->rows], entry=*p_entry;
167 entry;
168 p_entry = &entry->next, entry = *p_entry)
169 {
170 if (entry->hash==hash && entry->keylen==keylen &&
Benny Prijono40f2f642006-01-30 18:40:05 +0000171 pj_memcmp(entry->key, key, keylen)==0)
Benny Prijono9033e312005-11-21 02:08:39 +0000172 {
173 break;
174 }
175 }
176
177 if (entry || val==NULL)
178 return p_entry;
179
Benny Prijono40f2f642006-01-30 18:40:05 +0000180 /* Entry not found, create a new one.
181 * If entry_buf is specified, use it. Otherwise allocate from pool.
182 */
183 if (entry_buf) {
Benny Prijonof260e462007-04-30 21:03:32 +0000184 entry = (pj_hash_entry*)entry_buf;
Benny Prijono40f2f642006-01-30 18:40:05 +0000185 } else {
186 /* Pool must be specified! */
187 PJ_ASSERT_RETURN(pool != NULL, NULL);
188
Benny Prijonof260e462007-04-30 21:03:32 +0000189 entry = PJ_POOL_ALLOC_T(pool, pj_hash_entry);
Benny Prijono40f2f642006-01-30 18:40:05 +0000190 PJ_LOG(6, ("hashtbl",
191 "%p: New p_entry %p created, pool used=%u, cap=%u",
192 ht, entry, pj_pool_get_used_size(pool),
193 pj_pool_get_capacity(pool)));
194 }
Benny Prijono9033e312005-11-21 02:08:39 +0000195 entry->next = NULL;
196 entry->hash = hash;
Benny Prijono0e1d35c2007-09-08 07:12:44 +0000197 if (pool) {
198 entry->key = pj_pool_alloc(pool, keylen);
199 pj_memcpy(entry->key, key, keylen);
200 } else {
201 entry->key = (void*)key;
202 }
Benny Prijono9033e312005-11-21 02:08:39 +0000203 entry->keylen = keylen;
204 entry->value = val;
205 *p_entry = entry;
206
207 ++ht->count;
208
209 return p_entry;
210}
211
212PJ_DEF(void *) pj_hash_get( pj_hash_table_t *ht,
Benny Prijono40f2f642006-01-30 18:40:05 +0000213 const void *key, unsigned keylen,
214 pj_uint32_t *hval)
Benny Prijono9033e312005-11-21 02:08:39 +0000215{
216 pj_hash_entry *entry;
Benny Prijono40f2f642006-01-30 18:40:05 +0000217 entry = *find_entry( NULL, ht, key, keylen, NULL, hval, NULL);
Benny Prijono9033e312005-11-21 02:08:39 +0000218 return entry ? entry->value : NULL;
219}
220
221PJ_DEF(void) pj_hash_set( pj_pool_t *pool, pj_hash_table_t *ht,
Benny Prijono40f2f642006-01-30 18:40:05 +0000222 const void *key, unsigned keylen, pj_uint32_t hval,
Benny Prijono9033e312005-11-21 02:08:39 +0000223 void *value )
224{
225 pj_hash_entry **p_entry;
226
Benny Prijono40f2f642006-01-30 18:40:05 +0000227 p_entry = find_entry( pool, ht, key, keylen, value, &hval, NULL);
Benny Prijono9033e312005-11-21 02:08:39 +0000228 if (*p_entry) {
229 if (value == NULL) {
230 /* delete entry */
Benny Prijonoc81dfef2006-01-07 18:41:22 +0000231 PJ_LOG(6, ("hashtbl", "%p: p_entry %p deleted", ht, *p_entry));
Benny Prijono9033e312005-11-21 02:08:39 +0000232 *p_entry = (*p_entry)->next;
233 --ht->count;
234
235 } else {
236 /* overwrite */
237 (*p_entry)->value = value;
Benny Prijono40f2f642006-01-30 18:40:05 +0000238 PJ_LOG(6, ("hashtbl", "%p: p_entry %p value set to %p", ht,
239 *p_entry, value));
240 }
241 }
242}
243
244PJ_DEF(void) pj_hash_set_np( pj_hash_table_t *ht,
245 const void *key, unsigned keylen,
Benny Prijono17e58ed2007-05-28 11:49:46 +0000246 pj_uint32_t hval, pj_hash_entry_buf entry_buf,
247 void *value)
Benny Prijono40f2f642006-01-30 18:40:05 +0000248{
249 pj_hash_entry **p_entry;
250
Benny Prijono17e58ed2007-05-28 11:49:46 +0000251 p_entry = find_entry( NULL, ht, key, keylen, value, &hval,
252 (void*)entry_buf );
Benny Prijono40f2f642006-01-30 18:40:05 +0000253 if (*p_entry) {
254 if (value == NULL) {
255 /* delete entry */
256 PJ_LOG(6, ("hashtbl", "%p: p_entry %p deleted", ht, *p_entry));
257 *p_entry = (*p_entry)->next;
258 --ht->count;
259
260 } else {
261 /* overwrite */
262 (*p_entry)->value = value;
263 PJ_LOG(6, ("hashtbl", "%p: p_entry %p value set to %p", ht,
264 *p_entry, value));
Benny Prijono9033e312005-11-21 02:08:39 +0000265 }
266 }
267}
268
269PJ_DEF(unsigned) pj_hash_count( pj_hash_table_t *ht )
270{
271 return ht->count;
272}
273
274PJ_DEF(pj_hash_iterator_t*) pj_hash_first( pj_hash_table_t *ht,
275 pj_hash_iterator_t *it )
276{
277 it->index = 0;
278 it->entry = NULL;
279
Benny Prijonof1370372008-07-14 16:58:11 +0000280 for (; it->index <= ht->rows; ++it->index) {
Benny Prijono9033e312005-11-21 02:08:39 +0000281 it->entry = ht->table[it->index];
282 if (it->entry) {
283 break;
284 }
285 }
286
287 return it->entry ? it : NULL;
288}
289
290PJ_DEF(pj_hash_iterator_t*) pj_hash_next( pj_hash_table_t *ht,
291 pj_hash_iterator_t *it )
292{
293 it->entry = it->entry->next;
294 if (it->entry) {
295 return it;
296 }
297
Benny Prijonof1370372008-07-14 16:58:11 +0000298 for (++it->index; it->index <= ht->rows; ++it->index) {
Benny Prijono9033e312005-11-21 02:08:39 +0000299 it->entry = ht->table[it->index];
300 if (it->entry) {
301 break;
302 }
303 }
304
305 return it->entry ? it : NULL;
306}
307
308PJ_DEF(void*) pj_hash_this( pj_hash_table_t *ht, pj_hash_iterator_t *it )
309{
310 PJ_CHECK_STACK();
311 PJ_UNUSED_ARG(ht);
312 return it->entry->value;
313}
314
315#if 0
316void pj_hash_dump_collision( pj_hash_table_t *ht )
317{
318 unsigned min=0xFFFFFFFF, max=0;
319 unsigned i;
320 char line[120];
321 int len, totlen = 0;
322
Benny Prijonof1370372008-07-14 16:58:11 +0000323 for (i=0; i<=ht->rows; ++i) {
Benny Prijono9033e312005-11-21 02:08:39 +0000324 unsigned count = 0;
325 pj_hash_entry *entry = ht->table[i];
326 while (entry) {
327 ++count;
328 entry = entry->next;
329 }
330 if (count < min)
331 min = count;
332 if (count > max)
333 max = count;
334 len = pj_snprintf( line+totlen, sizeof(line)-totlen, "%3d:%3d ", i, count);
335 if (len < 1)
336 break;
337 totlen += len;
338
339 if ((i+1) % 10 == 0) {
340 line[totlen] = '\0';
341 PJ_LOG(4,(__FILE__, line));
342 }
343 }
344
345 PJ_LOG(4,(__FILE__,"Count: %d, min: %d, max: %d\n", ht->count, min, max));
346}
347#endif
348
349