Coverage Report

Created: 2025-10-12 06:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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