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