Coverage Report

Created: 2026-02-14 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/util-hash.c
Line
Count
Source
1
/* Copyright (C) 2007-2010 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17
18
/**
19
 * \file
20
 *
21
 * \author Victor Julien <victor@inliniac.net>
22
 *
23
 * Chained hash table implementation
24
 *
25
 * The 'Free' pointer can be used to have the API free your
26
 * hashed data. If it's NULL it's the callers responsibility
27
 */
28
29
#include "suricata-common.h"
30
#include "util-hash.h"
31
#include "util-unittest.h"
32
#include "util-memcmp.h"
33
#include "util-debug.h"
34
35
452k
HashTable* HashTableInit(uint32_t size, uint32_t (*Hash)(struct HashTable_ *, void *, uint16_t), char (*Compare)(void *, uint16_t, void *, uint16_t), void (*Free)(void *)) {
36
37
452k
    HashTable *ht = NULL;
38
39
452k
    if (size == 0) {
40
0
        goto error;
41
0
    }
42
43
452k
    if (Hash == NULL) {
44
        //printf("ERROR: HashTableInit no Hash function\n");
45
0
        goto error;
46
0
    }
47
48
    /* setup the filter */
49
452k
    ht = SCMalloc(sizeof(HashTable));
50
452k
    if (unlikely(ht == NULL))
51
0
    goto error;
52
452k
    memset(ht,0,sizeof(HashTable));
53
452k
    ht->array_size = size;
54
452k
    ht->Hash = Hash;
55
452k
    ht->Free = Free;
56
57
452k
    if (Compare != NULL)
58
452k
        ht->Compare = Compare;
59
0
    else
60
0
        ht->Compare = HashTableDefaultCompare;
61
62
    /* setup the bitarray */
63
452k
    ht->array = SCMalloc(ht->array_size * sizeof(HashTableBucket *));
64
452k
    if (ht->array == NULL)
65
0
        goto error;
66
452k
    memset(ht->array,0,ht->array_size * sizeof(HashTableBucket *));
67
68
452k
    return ht;
69
70
0
error:
71
0
    if (ht != NULL) {
72
0
        if (ht->array != NULL)
73
0
            SCFree(ht->array);
74
75
0
        SCFree(ht);
76
0
    }
77
0
    return NULL;
78
452k
}
79
80
void HashTableFree(HashTable *ht)
81
202k
{
82
202k
    uint32_t i = 0;
83
84
202k
    if (ht == NULL)
85
0
        return;
86
87
    /* free the buckets */
88
294M
    for (i = 0; i < ht->array_size; i++) {
89
293M
        HashTableBucket *hashbucket = ht->array[i];
90
294M
        while (hashbucket != NULL) {
91
47.7k
            HashTableBucket *next_hashbucket = hashbucket->next;
92
47.7k
            if (ht->Free != NULL)
93
47.7k
                ht->Free(hashbucket->data);
94
47.7k
            SCFree(hashbucket);
95
47.7k
            hashbucket = next_hashbucket;
96
47.7k
        }
97
293M
    }
98
99
    /* free the array */
100
202k
    if (ht->array != NULL)
101
202k
        SCFree(ht->array);
102
103
202k
    SCFree(ht);
104
202k
}
105
106
void HashTablePrint(HashTable *ht)
107
0
{
108
0
    printf("\n----------- Hash Table Stats ------------\n");
109
0
    printf("Buckets:               %" PRIu32 "\n", ht->array_size);
110
0
    printf("Hash function pointer: %p\n", ht->Hash);
111
0
    printf("-----------------------------------------\n");
112
0
}
113
114
int HashTableAdd(HashTable *ht, void *data, uint16_t datalen)
115
238k
{
116
238k
    if (ht == NULL || data == NULL)
117
0
        return -1;
118
119
238k
    uint32_t hash = ht->Hash(ht, data, datalen);
120
121
238k
    HashTableBucket *hb = SCMalloc(sizeof(HashTableBucket));
122
238k
    if (unlikely(hb == NULL))
123
0
        goto error;
124
238k
    memset(hb, 0, sizeof(HashTableBucket));
125
238k
    hb->data = data;
126
238k
    hb->size = datalen;
127
238k
    hb->next = NULL;
128
129
238k
    if (hash >= ht->array_size) {
130
0
        SCLogWarning("attempt to insert element out of hash array\n");
131
0
        goto error;
132
0
    }
133
134
238k
    if (ht->array[hash] == NULL) {
135
118k
        ht->array[hash] = hb;
136
119k
    } else {
137
119k
        hb->next = ht->array[hash];
138
119k
        ht->array[hash] = hb;
139
119k
    }
140
141
#ifdef UNITTESTS
142
    ht->count++;
143
#endif
144
145
238k
    return 0;
146
147
0
error:
148
0
    if (hb != NULL)
149
0
        SCFree(hb);
150
0
    return -1;
151
238k
}
152
153
int HashTableRemove(HashTable *ht, void *data, uint16_t datalen)
154
0
{
155
0
    uint32_t hash = ht->Hash(ht, data, datalen);
156
157
0
    if (ht->array[hash] == NULL) {
158
0
        return -1;
159
0
    }
160
161
0
    if (ht->array[hash]->next == NULL) {
162
0
        if (ht->Free != NULL)
163
0
            ht->Free(ht->array[hash]->data);
164
0
        SCFree(ht->array[hash]);
165
0
        ht->array[hash] = NULL;
166
0
        return 0;
167
0
    }
168
169
0
    HashTableBucket *hashbucket = ht->array[hash], *prev_hashbucket = NULL;
170
0
    do {
171
0
        if (ht->Compare(hashbucket->data,hashbucket->size,data,datalen) == 1) {
172
0
            if (prev_hashbucket == NULL) {
173
                /* root bucket */
174
0
                ht->array[hash] = hashbucket->next;
175
0
            } else {
176
                /* child bucket */
177
0
                prev_hashbucket->next = hashbucket->next;
178
0
            }
179
180
            /* remove this */
181
0
            if (ht->Free != NULL)
182
0
                ht->Free(hashbucket->data);
183
0
            SCFree(hashbucket);
184
0
            return 0;
185
0
        }
186
187
0
        prev_hashbucket = hashbucket;
188
0
        hashbucket = hashbucket->next;
189
0
    } while (hashbucket != NULL);
190
191
0
    return -1;
192
0
}
193
194
void *HashTableLookup(HashTable *ht, void *data, uint16_t datalen)
195
1.11M
{
196
1.11M
    uint32_t hash = 0;
197
198
1.11M
    if (ht == NULL)
199
2
        return NULL;
200
201
1.11M
    hash = ht->Hash(ht, data, datalen);
202
203
1.11M
    if (hash >= ht->array_size) {
204
0
        SCLogWarning("attempt to access element out of hash array\n");
205
0
        return NULL;
206
0
    }
207
208
1.11M
    if (ht->array[hash] == NULL)
209
101k
        return NULL;
210
211
1.00M
    HashTableBucket *hashbucket = ht->array[hash];
212
1.12M
    do {
213
1.12M
        if (ht->Compare(hashbucket->data, hashbucket->size, data, datalen) == 1)
214
1.00M
            return hashbucket->data;
215
216
115k
        hashbucket = hashbucket->next;
217
115k
    } while (hashbucket != NULL);
218
219
4.62k
    return NULL;
220
1.00M
}
221
222
uint32_t HashTableGenericHash(HashTable *ht, void *data, uint16_t datalen)
223
0
{
224
0
     uint8_t *d = (uint8_t *)data;
225
0
     uint32_t i;
226
0
     uint32_t hash = 0;
227
228
0
     for (i = 0; i < datalen; i++) {
229
0
         if (i == 0)      hash += (((uint32_t)*d++));
230
0
         else if (i == 1) hash += (((uint32_t)*d++) * datalen);
231
0
         else             hash *= (((uint32_t)*d++) * i) + datalen + i;
232
0
     }
233
234
0
     hash *= datalen;
235
0
     hash %= ht->array_size;
236
0
     return hash;
237
0
}
238
239
char HashTableDefaultCompare(void *data1, uint16_t len1, void *data2, uint16_t len2)
240
0
{
241
0
    if (len1 != len2)
242
0
        return 0;
243
244
0
    if (SCMemcmp(data1,data2,len1) != 0)
245
0
        return 0;
246
247
0
    return 1;
248
0
}
249
250
/*
251
 * ONLY TESTS BELOW THIS COMMENT
252
 */
253
254
#ifdef UNITTESTS
255
static int HashTableTestInit01 (void)
256
{
257
    HashTable *ht = HashTableInit(1024, HashTableGenericHash, NULL, NULL);
258
    if (ht == NULL)
259
        return 0;
260
261
    HashTableFree(ht);
262
    return 1;
263
}
264
265
/* no hash function, so it should fail */
266
static int HashTableTestInit02 (void)
267
{
268
    HashTable *ht = HashTableInit(1024, NULL, NULL, NULL);
269
    if (ht == NULL)
270
        return 1;
271
272
    HashTableFree(ht);
273
    return 0;
274
}
275
276
static int HashTableTestInit03 (void)
277
{
278
    int result = 0;
279
    HashTable *ht = HashTableInit(1024, HashTableGenericHash, NULL, NULL);
280
    if (ht == NULL)
281
        return 0;
282
283
    if (ht->Hash == HashTableGenericHash)
284
        result = 1;
285
286
    HashTableFree(ht);
287
    return result;
288
}
289
290
static int HashTableTestInit04 (void)
291
{
292
    HashTable *ht = HashTableInit(0, HashTableGenericHash, NULL, NULL);
293
    if (ht == NULL)
294
        return 1;
295
296
    HashTableFree(ht);
297
    return 0;
298
}
299
300
static int HashTableTestInit05 (void)
301
{
302
    int result = 0;
303
    HashTable *ht = HashTableInit(1024, HashTableGenericHash, NULL, NULL);
304
    if (ht == NULL)
305
        return 0;
306
307
    if (ht->Compare == HashTableDefaultCompare)
308
        result = 1;
309
310
    HashTableFree(ht);
311
    return result;
312
}
313
314
static char HashTableDefaultCompareTest(void *data1, uint16_t len1, void *data2, uint16_t len2)
315
{
316
    if (len1 != len2)
317
        return 0;
318
319
    if (SCMemcmp(data1,data2,len1) != 0)
320
        return 0;
321
322
    return 1;
323
}
324
325
static int HashTableTestInit06 (void)
326
{
327
    int result = 0;
328
    HashTable *ht = HashTableInit(1024, HashTableGenericHash, HashTableDefaultCompareTest, NULL);
329
    if (ht == NULL)
330
        return 0;
331
332
    if (ht->Compare == HashTableDefaultCompareTest)
333
        result = 1;
334
335
    HashTableFree(ht);
336
    return result;
337
}
338
339
static int HashTableTestAdd01 (void)
340
{
341
    int result = 0;
342
    HashTable *ht = HashTableInit(32, HashTableGenericHash, NULL, NULL);
343
    if (ht == NULL)
344
        goto end;
345
346
    int r = HashTableAdd(ht, (char *)"test", 0);
347
    if (r != 0)
348
        goto end;
349
350
    /* all is good! */
351
    result = 1;
352
end:
353
    if (ht != NULL) HashTableFree(ht);
354
    return result;
355
}
356
357
static int HashTableTestAdd02 (void)
358
{
359
    int result = 0;
360
    HashTable *ht = HashTableInit(32, HashTableGenericHash, NULL, NULL);
361
    if (ht == NULL)
362
        goto end;
363
364
    int r = HashTableAdd(ht, NULL, 4);
365
    if (r == 0)
366
        goto end;
367
368
    /* all is good! */
369
    result = 1;
370
end:
371
    if (ht != NULL) HashTableFree(ht);
372
    return result;
373
}
374
375
static int HashTableTestFull01 (void)
376
{
377
    int result = 0;
378
    HashTable *ht = HashTableInit(32, HashTableGenericHash, NULL, NULL);
379
    if (ht == NULL)
380
        goto end;
381
382
    int r = HashTableAdd(ht, (char *)"test", 4);
383
    if (r != 0)
384
        goto end;
385
386
    char *rp = HashTableLookup(ht, (char *)"test", 4);
387
    if (rp == NULL)
388
        goto end;
389
390
    r = HashTableRemove(ht, (char *)"test", 4);
391
    if (r != 0)
392
        goto end;
393
394
    /* all is good! */
395
    result = 1;
396
end:
397
    if (ht != NULL) HashTableFree(ht);
398
    return result;
399
}
400
401
static int HashTableTestFull02 (void)
402
{
403
    int result = 0;
404
    HashTable *ht = HashTableInit(32, HashTableGenericHash, NULL, NULL);
405
    if (ht == NULL)
406
        goto end;
407
408
    int r = HashTableAdd(ht, (char *)"test", 4);
409
    if (r != 0)
410
        goto end;
411
412
    char *rp = HashTableLookup(ht, (char *)"test", 4);
413
    if (rp == NULL)
414
        goto end;
415
416
    r = HashTableRemove(ht, (char *)"test2", 5);
417
    if (r == 0)
418
        goto end;
419
420
    /* all is good! */
421
    result = 1;
422
end:
423
    if (ht != NULL) HashTableFree(ht);
424
    return result;
425
}
426
#endif
427
428
void HashTableRegisterTests(void)
429
0
{
430
#ifdef UNITTESTS
431
    UtRegisterTest("HashTableTestInit01", HashTableTestInit01);
432
    UtRegisterTest("HashTableTestInit02", HashTableTestInit02);
433
    UtRegisterTest("HashTableTestInit03", HashTableTestInit03);
434
    UtRegisterTest("HashTableTestInit04", HashTableTestInit04);
435
    UtRegisterTest("HashTableTestInit05", HashTableTestInit05);
436
    UtRegisterTest("HashTableTestInit06", HashTableTestInit06);
437
438
    UtRegisterTest("HashTableTestAdd01", HashTableTestAdd01);
439
    UtRegisterTest("HashTableTestAdd02", HashTableTestAdd02);
440
441
    UtRegisterTest("HashTableTestFull01", HashTableTestFull01);
442
    UtRegisterTest("HashTableTestFull02", HashTableTestFull02);
443
#endif
444
0
}
445