Coverage Report

Created: 2026-03-31 07:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/util-hashlist.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-hashlist.h"
31
#include "util-unittest.h"
32
#include "util-debug.h"
33
#include "util-memcmp.h"
34
35
HashListTable *HashListTableInit(uint32_t size,
36
        uint32_t (*Hash)(struct HashListTable_ *, void *, uint16_t),
37
        char (*Compare)(void *, uint16_t, void *, uint16_t), void (*Free)(void *))
38
2.87M
{
39
2.87M
    sc_errno = SC_OK;
40
2.87M
    HashListTable *ht = NULL;
41
42
2.87M
    if (size == 0) {
43
0
        sc_errno = SC_EINVAL;
44
0
        goto error;
45
0
    }
46
47
2.87M
    if (Hash == NULL) {
48
0
        sc_errno = SC_EINVAL;
49
0
        goto error;
50
0
    }
51
52
    /* setup the filter */
53
2.87M
    ht = SCMalloc(sizeof(HashListTable));
54
2.87M
    if (unlikely(ht == NULL)) {
55
0
        sc_errno = SC_ENOMEM;
56
0
        goto error;
57
0
    }
58
2.87M
    memset(ht,0,sizeof(HashListTable));
59
2.87M
    ht->array_size = size;
60
2.87M
    ht->Hash = Hash;
61
2.87M
    ht->Free = Free;
62
63
2.87M
    if (Compare != NULL)
64
2.87M
        ht->Compare = Compare;
65
0
    else
66
0
        ht->Compare = HashListTableDefaultCompare;
67
68
    /* setup the bitarray */
69
2.87M
    ht->array = SCMalloc(ht->array_size * sizeof(HashListTableBucket *));
70
2.87M
    if (ht->array == NULL) {
71
0
        sc_errno = SC_ENOMEM;
72
0
        goto error;
73
0
    }
74
2.87M
    memset(ht->array,0,ht->array_size * sizeof(HashListTableBucket *));
75
76
2.87M
    ht->listhead = NULL;
77
2.87M
    ht->listtail = NULL;
78
2.87M
    return ht;
79
80
0
error:
81
0
    if (ht != NULL) {
82
0
        if (ht->array != NULL)
83
0
            SCFree(ht->array);
84
85
0
        SCFree(ht);
86
0
    }
87
0
    return NULL;
88
2.87M
}
89
90
void HashListTableFree(HashListTable *ht)
91
2.99M
{
92
2.99M
    uint32_t i = 0;
93
94
2.99M
    if (ht == NULL)
95
121k
        return;
96
97
    /* free the buckets */
98
11.1G
    for (i = 0; i < ht->array_size; i++) {
99
11.1G
        HashListTableBucket *hashbucket = ht->array[i];
100
11.3G
        while (hashbucket != NULL) {
101
118M
            HashListTableBucket *next_hashbucket = hashbucket->bucknext;
102
118M
            if (ht->Free != NULL)
103
78.3M
                ht->Free(hashbucket->data);
104
118M
            SCFree(hashbucket);
105
118M
            hashbucket = next_hashbucket;
106
118M
        }
107
11.1G
    }
108
109
    /* free the array */
110
2.87M
    if (ht->array != NULL)
111
2.87M
        SCFree(ht->array);
112
113
2.87M
    SCFree(ht);
114
2.87M
}
115
116
void HashListTablePrint(HashListTable *ht)
117
0
{
118
0
    printf("\n----------- Hash Table Stats ------------\n");
119
0
    printf("Buckets:               %" PRIu32 "\n", ht->array_size);
120
0
    printf("Hash function pointer: %p\n", ht->Hash);
121
0
    printf("-----------------------------------------\n");
122
0
}
123
124
int HashListTableAdd(HashListTable *ht, void *data, uint16_t datalen)
125
118M
{
126
118M
    if (ht == NULL || data == NULL)
127
6
        return -1;
128
129
118M
    uint32_t hash = ht->Hash(ht, data, datalen);
130
131
118M
    SCLogDebug("ht %p hash %"PRIu32"", ht, hash);
132
133
118M
    HashListTableBucket *hb = SCMalloc(sizeof(HashListTableBucket));
134
118M
    if (unlikely(hb == NULL))
135
0
        goto error;
136
118M
    memset(hb, 0, sizeof(HashListTableBucket));
137
118M
    hb->data = data;
138
118M
    hb->size = datalen;
139
118M
    hb->bucknext = NULL;
140
118M
    hb->listnext = NULL;
141
118M
    hb->listprev = NULL;
142
143
118M
    if (ht->array[hash] == NULL) {
144
78.2M
        ht->array[hash] = hb;
145
78.2M
    } else {
146
40.7M
        hb->bucknext = ht->array[hash];
147
40.7M
        ht->array[hash] = hb;
148
40.7M
    }
149
150
118M
    if (ht->listtail == NULL) {
151
1.34M
        ht->listhead = hb;
152
1.34M
        ht->listtail = hb;
153
117M
    } else {
154
117M
        hb->listprev = ht->listtail;
155
117M
        ht->listtail->listnext = hb;
156
117M
        ht->listtail = hb;
157
117M
    }
158
159
118M
    return 0;
160
161
0
error:
162
0
    return -1;
163
118M
}
164
165
int HashListTableRemove(HashListTable *ht, void *data, uint16_t datalen)
166
729k
{
167
729k
    uint32_t hash = ht->Hash(ht, data, datalen);
168
169
729k
    SCLogDebug("ht %p hash %"PRIu32"", ht, hash);
170
171
729k
    if (ht->array[hash] == NULL) {
172
85.8k
        SCLogDebug("ht->array[hash] NULL");
173
85.8k
        return -1;
174
85.8k
    }
175
176
    /* fast track for just one data part */
177
643k
    if (ht->array[hash]->bucknext == NULL) {
178
602k
        HashListTableBucket *hb = ht->array[hash];
179
180
602k
        if (ht->Compare(hb->data,hb->size,data,datalen) == 1) {
181
            /* remove from the list */
182
601k
            if (hb->listprev == NULL) {
183
218k
                ht->listhead = hb->listnext;
184
383k
            } else {
185
383k
                hb->listprev->listnext = hb->listnext;
186
383k
            }
187
601k
            if (hb->listnext == NULL) {
188
377k
                ht->listtail = hb->listprev;
189
377k
            } else {
190
223k
                hb->listnext->listprev = hb->listprev;
191
223k
            }
192
193
601k
            if (ht->Free != NULL)
194
601k
                ht->Free(hb->data);
195
196
601k
            SCFree(ht->array[hash]);
197
601k
            ht->array[hash] = NULL;
198
601k
            return 0;
199
601k
        }
200
201
1.29k
        SCLogDebug("fast track default case");
202
1.29k
        return -1;
203
602k
    }
204
205
    /* more data in this bucket */
206
40.6k
    HashListTableBucket *hashbucket = ht->array[hash], *prev_hashbucket = NULL;
207
66.8k
    do {
208
66.8k
        if (ht->Compare(hashbucket->data,hashbucket->size,data,datalen) == 1) {
209
210
            /* remove from the list */
211
40.6k
            if (hashbucket->listprev == NULL) {
212
13.0k
                ht->listhead = hashbucket->listnext;
213
27.5k
            } else {
214
27.5k
                hashbucket->listprev->listnext = hashbucket->listnext;
215
27.5k
            }
216
40.6k
            if (hashbucket->listnext == NULL) {
217
5.85k
                ht->listtail = hashbucket->listprev;
218
34.7k
            } else {
219
34.7k
                hashbucket->listnext->listprev = hashbucket->listprev;
220
34.7k
            }
221
222
40.6k
            if (prev_hashbucket == NULL) {
223
                /* root bucket */
224
18.5k
                ht->array[hash] = hashbucket->bucknext;
225
22.0k
            } else {
226
                /* child bucket */
227
22.0k
                prev_hashbucket->bucknext = hashbucket->bucknext;
228
22.0k
            }
229
230
            /* remove this */
231
40.6k
            if (ht->Free != NULL)
232
40.6k
                ht->Free(hashbucket->data);
233
40.6k
            SCFree(hashbucket);
234
40.6k
            return 0;
235
40.6k
        }
236
237
26.2k
        prev_hashbucket = hashbucket;
238
26.2k
        hashbucket = hashbucket->bucknext;
239
26.2k
    } while (hashbucket != NULL);
240
241
18
    SCLogDebug("slow track default case");
242
18
    return -1;
243
40.6k
}
244
245
char HashListTableDefaultCompare(void *data1, uint16_t len1, void *data2, uint16_t len2)
246
0
{
247
0
    if (len1 != len2)
248
0
        return 0;
249
250
0
    if (SCMemcmp(data1,data2,len1) != 0)
251
0
        return 0;
252
253
0
    return 1;
254
0
}
255
256
void *HashListTableLookup(HashListTable *ht, void *data, uint16_t datalen)
257
117M
{
258
259
117M
    if (ht == NULL) {
260
279
        SCLogDebug("Hash List table is NULL");
261
279
        return NULL;
262
279
    }
263
264
117M
    uint32_t hash = ht->Hash(ht, data, datalen);
265
266
117M
    if (ht->array[hash] == NULL) {
267
28.6M
        return NULL;
268
28.6M
    }
269
270
88.5M
    HashListTableBucket *hashbucket = ht->array[hash];
271
135M
    do {
272
135M
        if (ht->Compare(hashbucket->data,hashbucket->size,data,datalen) == 1)
273
71.4M
            return hashbucket->data;
274
275
64.2M
        hashbucket = hashbucket->bucknext;
276
64.2M
    } while (hashbucket != NULL);
277
278
17.0M
    return NULL;
279
88.5M
}
280
281
uint32_t HashListTableGenericHash(HashListTable *ht, void *data, uint16_t datalen)
282
0
{
283
0
     uint8_t *d = (uint8_t *)data;
284
0
     uint32_t i;
285
0
     uint32_t hash = 0;
286
287
0
     for (i = 0; i < datalen; i++) {
288
0
         if (i == 0)      hash += (((uint32_t)*d++));
289
0
         else if (i == 1) hash += (((uint32_t)*d++) * datalen);
290
0
         else             hash *= (((uint32_t)*d++) * i) + datalen + i;
291
0
     }
292
293
0
     hash *= datalen;
294
0
     hash %= ht->array_size;
295
0
     return hash;
296
0
}
297
298
HashListTableBucket *HashListTableGetListHead(HashListTable *ht)
299
1.99M
{
300
1.99M
    return ht->listhead;
301
1.99M
}
302
303
/*
304
 * ONLY TESTS BELOW THIS COMMENT
305
 */
306
307
#ifdef UNITTESTS
308
static int HashListTableTestInit01 (void)
309
{
310
    HashListTable *ht = HashListTableInit(1024, HashListTableGenericHash, NULL, NULL);
311
    if (ht == NULL)
312
        return 0;
313
314
    HashListTableFree(ht);
315
    return 1;
316
}
317
318
/* no hash function, so it should fail */
319
static int HashListTableTestInit02 (void)
320
{
321
    HashListTable *ht = HashListTableInit(1024, NULL, NULL, NULL);
322
    if (ht == NULL)
323
        return 1;
324
325
    HashListTableFree(ht);
326
    return 0;
327
}
328
329
static int HashListTableTestInit03 (void)
330
{
331
    int result = 0;
332
    HashListTable *ht = HashListTableInit(1024, HashListTableGenericHash, NULL, NULL);
333
    if (ht == NULL)
334
        return 0;
335
336
    if (ht->Hash == HashListTableGenericHash)
337
        result = 1;
338
339
    HashListTableFree(ht);
340
    return result;
341
}
342
343
static int HashListTableTestInit04 (void)
344
{
345
    HashListTable *ht = HashListTableInit(0, HashListTableGenericHash, NULL, NULL);
346
    if (ht == NULL)
347
        return 1;
348
349
    HashListTableFree(ht);
350
    return 0;
351
}
352
353
static int HashListTableTestAdd01 (void)
354
{
355
    int result = 0;
356
    HashListTable *ht = HashListTableInit(32, HashListTableGenericHash, NULL, NULL);
357
    if (ht == NULL)
358
        goto end;
359
360
    int r = HashListTableAdd(ht, (char *)"test", 0);
361
    if (r != 0)
362
        goto end;
363
364
    /* all is good! */
365
    result = 1;
366
end:
367
    if (ht != NULL) HashListTableFree(ht);
368
    return result;
369
}
370
371
static int HashListTableTestAdd02 (void)
372
{
373
    int result = 0;
374
    HashListTable *ht = HashListTableInit(32, HashListTableGenericHash, NULL, NULL);
375
    if (ht == NULL)
376
        goto end;
377
378
    int r = HashListTableAdd(ht, NULL, 4);
379
    if (r == 0)
380
        goto end;
381
382
    /* all is good! */
383
    result = 1;
384
end:
385
    if (ht != NULL) HashListTableFree(ht);
386
    return result;
387
}
388
389
static int HashListTableTestAdd03 (void)
390
{
391
    int result = 0;
392
    HashListTable *ht = HashListTableInit(32, HashListTableGenericHash, NULL, NULL);
393
    if (ht == NULL)
394
        goto end;
395
396
    int r = HashListTableAdd(ht, (char *)"test", 0);
397
    if (r != 0)
398
        goto end;
399
400
    if (ht->listhead == NULL) {
401
        printf("ht->listhead == NULL: ");
402
        goto end;
403
    }
404
405
    if (ht->listtail == NULL) {
406
        printf("ht->listtail == NULL: ");
407
        goto end;
408
    }
409
410
    /* all is good! */
411
    result = 1;
412
end:
413
    if (ht != NULL) HashListTableFree(ht);
414
    return result;
415
}
416
417
static int HashListTableTestAdd04 (void)
418
{
419
    int result = 0;
420
    HashListTable *ht = HashListTableInit(32, HashListTableGenericHash, NULL, NULL);
421
    if (ht == NULL)
422
        goto end;
423
424
    int r = HashListTableAdd(ht, (char *)"test", 4);
425
    if (r != 0)
426
        goto end;
427
428
    char *rp = HashListTableLookup(ht, (char *)"test", 4);
429
    if (rp == NULL)
430
        goto end;
431
432
    HashListTableBucket *htb = HashListTableGetListHead(ht);
433
    if (htb == NULL) {
434
        printf("htb == NULL: ");
435
        goto end;
436
    }
437
438
    char *rp2 = HashListTableGetListData(htb);
439
    if (rp2 == NULL) {
440
        printf("rp2 == NULL: ");
441
        goto end;
442
    }
443
444
    if (rp != rp2) {
445
        printf("rp != rp2: ");
446
        goto end;
447
    }
448
449
    /* all is good! */
450
    result = 1;
451
end:
452
    if (ht != NULL) HashListTableFree(ht);
453
    return result;
454
}
455
456
static int HashListTableTestFull01 (void)
457
{
458
    int result = 0;
459
    HashListTable *ht = HashListTableInit(32, HashListTableGenericHash, NULL, NULL);
460
    if (ht == NULL)
461
        goto end;
462
463
    int r = HashListTableAdd(ht, (char *)"test", 4);
464
    if (r != 0)
465
        goto end;
466
467
    char *rp = HashListTableLookup(ht, (char *)"test", 4);
468
    if (rp == NULL)
469
        goto end;
470
471
    r = HashListTableRemove(ht, (char *)"test", 4);
472
    if (r != 0)
473
        goto end;
474
475
    /* all is good! */
476
    result = 1;
477
end:
478
    if (ht != NULL) HashListTableFree(ht);
479
    return result;
480
}
481
482
static int HashListTableTestFull02 (void)
483
{
484
    int result = 0;
485
    HashListTable *ht = HashListTableInit(32, HashListTableGenericHash, NULL, NULL);
486
    if (ht == NULL)
487
        goto end;
488
489
    int r = HashListTableAdd(ht, (char *)"test", 4);
490
    if (r != 0)
491
        goto end;
492
493
    char *rp = HashListTableLookup(ht, (char *)"test", 4);
494
    if (rp == NULL)
495
        goto end;
496
497
    r = HashListTableRemove(ht, (char *)"test2", 5);
498
    if (r == 0)
499
        goto end;
500
501
    /* all is good! */
502
    result = 1;
503
end:
504
    if (ht != NULL) HashListTableFree(ht);
505
    return result;
506
}
507
#endif /* UNITTESTS */
508
509
void HashListTableRegisterTests(void)
510
0
{
511
#ifdef UNITTESTS
512
    UtRegisterTest("HashListTableTestInit01", HashListTableTestInit01);
513
    UtRegisterTest("HashListTableTestInit02", HashListTableTestInit02);
514
    UtRegisterTest("HashListTableTestInit03", HashListTableTestInit03);
515
    UtRegisterTest("HashListTableTestInit04", HashListTableTestInit04);
516
517
    UtRegisterTest("HashListTableTestAdd01", HashListTableTestAdd01);
518
    UtRegisterTest("HashListTableTestAdd02", HashListTableTestAdd02);
519
    UtRegisterTest("HashListTableTestAdd03", HashListTableTestAdd03);
520
    UtRegisterTest("HashListTableTestAdd04", HashListTableTestAdd04);
521
522
    UtRegisterTest("HashListTableTestFull01", HashListTableTestFull01);
523
    UtRegisterTest("HashListTableTestFull02", HashListTableTestFull02);
524
#endif /* UNITTESTS */
525
0
}
526