suricata
util-hash.c
Go to the documentation of this file.
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 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  HashTable *ht = NULL;
38 
39  if (size == 0) {
40  goto error;
41  }
42 
43  if (Hash == NULL) {
44  //printf("ERROR: HashTableInit no Hash function\n");
45  goto error;
46  }
47 
48  /* setup the filter */
49  ht = SCCalloc(1, sizeof(HashTable));
50  if (unlikely(ht == NULL))
51  goto error;
52  ht->array_size = size;
53  ht->Hash = Hash;
54  ht->Free = Free;
55 
56  if (Compare != NULL)
57  ht->Compare = Compare;
58  else
60 
61  /* setup the bitarray */
62  ht->array = SCCalloc(ht->array_size, sizeof(HashTableBucket *));
63  if (ht->array == NULL)
64  goto error;
65 
66  return ht;
67 
68 error:
69  if (ht != NULL) {
70  if (ht->array != NULL)
71  SCFree(ht->array);
72 
73  SCFree(ht);
74  }
75  return NULL;
76 }
77 
79 {
80  uint32_t i = 0;
81 
82  if (ht == NULL)
83  return;
84 
85  /* free the buckets */
86  for (i = 0; i < ht->array_size; i++) {
87  HashTableBucket *hashbucket = ht->array[i];
88  while (hashbucket != NULL) {
89  HashTableBucket *next_hashbucket = hashbucket->next;
90  if (ht->Free != NULL)
91  ht->Free(hashbucket->data);
92  SCFree(hashbucket);
93  hashbucket = next_hashbucket;
94  }
95  }
96 
97  /* free the array */
98  if (ht->array != NULL)
99  SCFree(ht->array);
100 
101  SCFree(ht);
102 }
103 
104 int HashTableAdd(HashTable *ht, void *data, uint16_t datalen)
105 {
106  if (ht == NULL || data == NULL)
107  return -1;
108 
109  uint32_t hash = ht->Hash(ht, data, datalen);
110 
111  HashTableBucket *hb = SCCalloc(1, sizeof(HashTableBucket));
112  if (unlikely(hb == NULL))
113  goto error;
114  hb->data = data;
115  hb->size = datalen;
116  hb->next = NULL;
117 
118  if (hash >= ht->array_size) {
119  SCLogWarning("attempt to insert element out of hash array\n");
120  goto error;
121  }
122 
123  if (ht->array[hash] == NULL) {
124  ht->array[hash] = hb;
125  } else {
126  hb->next = ht->array[hash];
127  ht->array[hash] = hb;
128  }
129 
130 #ifdef UNITTESTS
131  ht->count++;
132 #endif
133 
134  return 0;
135 
136 error:
137  if (hb != NULL)
138  SCFree(hb);
139  return -1;
140 }
141 
142 int HashTableRemove(HashTable *ht, void *data, uint16_t datalen)
143 {
144  uint32_t hash = ht->Hash(ht, data, datalen);
145 
146  if (ht->array[hash] == NULL) {
147  return -1;
148  }
149 
150  if (ht->array[hash]->next == NULL) {
151  if (ht->Free != NULL)
152  ht->Free(ht->array[hash]->data);
153  SCFree(ht->array[hash]);
154  ht->array[hash] = NULL;
155  return 0;
156  }
157 
158  HashTableBucket *hashbucket = ht->array[hash], *prev_hashbucket = NULL;
159  do {
160  if (ht->Compare(hashbucket->data,hashbucket->size,data,datalen) == 1) {
161  if (prev_hashbucket == NULL) {
162  /* root bucket */
163  ht->array[hash] = hashbucket->next;
164  } else {
165  /* child bucket */
166  prev_hashbucket->next = hashbucket->next;
167  }
168 
169  /* remove this */
170  if (ht->Free != NULL)
171  ht->Free(hashbucket->data);
172  SCFree(hashbucket);
173  return 0;
174  }
175 
176  prev_hashbucket = hashbucket;
177  hashbucket = hashbucket->next;
178  } while (hashbucket != NULL);
179 
180  return -1;
181 }
182 
183 void *HashTableLookup(HashTable *ht, void *data, uint16_t datalen)
184 {
185  uint32_t hash = 0;
186 
187  if (ht == NULL)
188  return NULL;
189 
190  hash = ht->Hash(ht, data, datalen);
191 
192  if (hash >= ht->array_size) {
193  SCLogWarning("attempt to access element out of hash array\n");
194  return NULL;
195  }
196 
197  if (ht->array[hash] == NULL)
198  return NULL;
199 
200  HashTableBucket *hashbucket = ht->array[hash];
201  do {
202  if (ht->Compare(hashbucket->data, hashbucket->size, data, datalen) == 1)
203  return hashbucket->data;
204 
205  hashbucket = hashbucket->next;
206  } while (hashbucket != NULL);
207 
208  return NULL;
209 }
210 
211 uint32_t HashTableGenericHash(HashTable *ht, void *data, uint16_t datalen)
212 {
213  uint8_t *d = (uint8_t *)data;
214  uint32_t i;
215  uint32_t hash = 0;
216 
217  for (i = 0; i < datalen; i++) {
218  if (i == 0) hash += (((uint32_t)*d++));
219  else if (i == 1) hash += (((uint32_t)*d++) * datalen);
220  else hash *= (((uint32_t)*d++) * i) + datalen + i;
221  }
222 
223  hash *= datalen;
224  hash %= ht->array_size;
225  return hash;
226 }
227 
228 char HashTableDefaultCompare(void *data1, uint16_t len1, void *data2, uint16_t len2)
229 {
230  if (len1 != len2)
231  return 0;
232 
233  if (SCMemcmp(data1,data2,len1) != 0)
234  return 0;
235 
236  return 1;
237 }
238 
239 /*
240  * ONLY TESTS BELOW THIS COMMENT
241  */
242 
243 #ifdef UNITTESTS
244 static int HashTableTestInit01 (void)
245 {
246  HashTable *ht = HashTableInit(1024, HashTableGenericHash, NULL, NULL);
247  if (ht == NULL)
248  return 0;
249 
250  HashTableFree(ht);
251  return 1;
252 }
253 
254 /* no hash function, so it should fail */
255 static int HashTableTestInit02 (void)
256 {
257  HashTable *ht = HashTableInit(1024, NULL, NULL, NULL);
258  if (ht == NULL)
259  return 1;
260 
261  HashTableFree(ht);
262  return 0;
263 }
264 
265 static int HashTableTestInit03 (void)
266 {
267  int result = 0;
268  HashTable *ht = HashTableInit(1024, HashTableGenericHash, NULL, NULL);
269  if (ht == NULL)
270  return 0;
271 
272  if (ht->Hash == HashTableGenericHash)
273  result = 1;
274 
275  HashTableFree(ht);
276  return result;
277 }
278 
279 static int HashTableTestInit04 (void)
280 {
281  HashTable *ht = HashTableInit(0, HashTableGenericHash, NULL, NULL);
282  if (ht == NULL)
283  return 1;
284 
285  HashTableFree(ht);
286  return 0;
287 }
288 
289 static int HashTableTestInit05 (void)
290 {
291  int result = 0;
292  HashTable *ht = HashTableInit(1024, HashTableGenericHash, NULL, NULL);
293  if (ht == NULL)
294  return 0;
295 
296  if (ht->Compare == HashTableDefaultCompare)
297  result = 1;
298 
299  HashTableFree(ht);
300  return result;
301 }
302 
303 static char HashTableDefaultCompareTest(void *data1, uint16_t len1, void *data2, uint16_t len2)
304 {
305  if (len1 != len2)
306  return 0;
307 
308  if (SCMemcmp(data1,data2,len1) != 0)
309  return 0;
310 
311  return 1;
312 }
313 
314 static int HashTableTestInit06 (void)
315 {
316  int result = 0;
317  HashTable *ht = HashTableInit(1024, HashTableGenericHash, HashTableDefaultCompareTest, NULL);
318  if (ht == NULL)
319  return 0;
320 
321  if (ht->Compare == HashTableDefaultCompareTest)
322  result = 1;
323 
324  HashTableFree(ht);
325  return result;
326 }
327 
328 static int HashTableTestAdd01 (void)
329 {
330  int result = 0;
331  HashTable *ht = HashTableInit(32, HashTableGenericHash, NULL, NULL);
332  if (ht == NULL)
333  goto end;
334 
335  int r = HashTableAdd(ht, (char *)"test", 0);
336  if (r != 0)
337  goto end;
338 
339  /* all is good! */
340  result = 1;
341 end:
342  if (ht != NULL) HashTableFree(ht);
343  return result;
344 }
345 
346 static int HashTableTestAdd02 (void)
347 {
348  int result = 0;
349  HashTable *ht = HashTableInit(32, HashTableGenericHash, NULL, NULL);
350  if (ht == NULL)
351  goto end;
352 
353  int r = HashTableAdd(ht, NULL, 4);
354  if (r == 0)
355  goto end;
356 
357  /* all is good! */
358  result = 1;
359 end:
360  if (ht != NULL) HashTableFree(ht);
361  return result;
362 }
363 
364 static int HashTableTestFull01 (void)
365 {
366  int result = 0;
367  HashTable *ht = HashTableInit(32, HashTableGenericHash, NULL, NULL);
368  if (ht == NULL)
369  goto end;
370 
371  int r = HashTableAdd(ht, (char *)"test", 4);
372  if (r != 0)
373  goto end;
374 
375  char *rp = HashTableLookup(ht, (char *)"test", 4);
376  if (rp == NULL)
377  goto end;
378 
379  r = HashTableRemove(ht, (char *)"test", 4);
380  if (r != 0)
381  goto end;
382 
383  /* all is good! */
384  result = 1;
385 end:
386  if (ht != NULL) HashTableFree(ht);
387  return result;
388 }
389 
390 static int HashTableTestFull02 (void)
391 {
392  int result = 0;
393  HashTable *ht = HashTableInit(32, HashTableGenericHash, NULL, NULL);
394  if (ht == NULL)
395  goto end;
396 
397  int r = HashTableAdd(ht, (char *)"test", 4);
398  if (r != 0)
399  goto end;
400 
401  char *rp = HashTableLookup(ht, (char *)"test", 4);
402  if (rp == NULL)
403  goto end;
404 
405  r = HashTableRemove(ht, (char *)"test2", 5);
406  if (r == 0)
407  goto end;
408 
409  /* all is good! */
410  result = 1;
411 end:
412  if (ht != NULL) HashTableFree(ht);
413  return result;
414 }
415 #endif
416 
418 {
419 #ifdef UNITTESTS
420  UtRegisterTest("HashTableTestInit01", HashTableTestInit01);
421  UtRegisterTest("HashTableTestInit02", HashTableTestInit02);
422  UtRegisterTest("HashTableTestInit03", HashTableTestInit03);
423  UtRegisterTest("HashTableTestInit04", HashTableTestInit04);
424  UtRegisterTest("HashTableTestInit05", HashTableTestInit05);
425  UtRegisterTest("HashTableTestInit06", HashTableTestInit06);
426 
427  UtRegisterTest("HashTableTestAdd01", HashTableTestAdd01);
428  UtRegisterTest("HashTableTestAdd02", HashTableTestAdd02);
429 
430  UtRegisterTest("HashTableTestFull01", HashTableTestFull01);
431  UtRegisterTest("HashTableTestFull02", HashTableTestFull02);
432 #endif
433 }
434 
HashTableDefaultCompare
char HashTableDefaultCompare(void *data1, uint16_t len1, void *data2, uint16_t len2)
Definition: util-hash.c:228
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:103
util-hash.h
HashTable_
Definition: util-hash.h:35
util-unittest.h
HashTableBucket_::size
uint16_t size
Definition: util-hash.h:30
HashTableFree
void HashTableFree(HashTable *ht)
Definition: util-hash.c:78
HashTable_::array_size
uint32_t array_size
Definition: util-hash.h:37
util-memcmp.h
HashTableBucket_
Definition: util-hash.h:28
util-debug.h
HashTable_::Compare
char(* Compare)(void *, uint16_t, void *, uint16_t)
Definition: util-hash.h:42
HashTableRegisterTests
void HashTableRegisterTests(void)
Definition: util-hash.c:417
HashTableLookup
void * HashTableLookup(HashTable *ht, void *data, uint16_t datalen)
Definition: util-hash.c:183
HashTableRemove
int HashTableRemove(HashTable *ht, void *data, uint16_t datalen)
Definition: util-hash.c:142
SCLogWarning
#define SCLogWarning(...)
Macro used to log WARNING messages.
Definition: util-debug.h:249
HashTableAdd
int HashTableAdd(HashTable *ht, void *data, uint16_t datalen)
Definition: util-hash.c:104
HashTable_::array
HashTableBucket ** array
Definition: util-hash.h:36
HashTableBucket_::next
struct HashTableBucket_ * next
Definition: util-hash.h:31
suricata-common.h
HashTable_::count
uint32_t count
Definition: util-hash.h:39
SCFree
#define SCFree(p)
Definition: util-mem.h:61
HashTableInit
HashTable * HashTableInit(uint32_t size, uint32_t(*Hash)(struct HashTable_ *, void *, uint16_t), char(*Compare)(void *, uint16_t, void *, uint16_t), void(*Free)(void *))
Definition: util-hash.c:35
HashTableBucket_::data
void * data
Definition: util-hash.h:29
HashTableGenericHash
uint32_t HashTableGenericHash(HashTable *ht, void *data, uint16_t datalen)
Definition: util-hash.c:211
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
SCMemcmp
#define SCMemcmp(a, b, c)
Definition: util-memcmp.h:290
HashTable_::Free
void(* Free)(void *)
Definition: util-hash.h:43
HashTable_::Hash
uint32_t(* Hash)(struct HashTable_ *, void *, uint16_t)
Definition: util-hash.h:41