1 /*****
2 *
3 * Copyright (C) 2003-2015 CS-SI. All Rights Reserved.
4 * Author: Nicolas Delon <nicolas.delon@prelude-ids.com>
5 *
6 * This file is part of the Prelude library.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 *****/
23 
24 #include "config.h"
25 
26 #include <string.h>
27 #include <stdlib.h>
28 
29 #include "prelude-log.h"
30 #include "prelude-list.h"
31 #include "prelude-error.h"
32 #include "prelude-hash.h"
33 
34 
35 #define HASH_DEFAULT_SIZE       128
36 
37 
38 
39 typedef struct hash_elem {
40         prelude_list_t list;
41         void *key;
42         void *value;
43 } hash_elem_t;
44 
45 
46 struct prelude_hash {
47         size_t lists_size;
48         prelude_list_t *lists;
49 
50         unsigned int (*hash_func)(const void *key);
51         int (*key_cmp_func)(const void *key1, const void *key2);
52         void (*key_destroy_func)(void *key);
53         void (*value_destroy_func)(void *value);
54 };
55 
56 
57 
58 /*
59  * This function's code was taken from glib
60  */
default_hash_func(const void * key)61 static unsigned int default_hash_func(const void *key)
62 {
63         const char *ptr = key;
64         unsigned int hv = *ptr;
65 
66         if ( hv )
67                 for ( ptr += 1; *ptr; ptr++ )
68                         hv = (hv << 5) - hv + *ptr;
69 
70         return hv;
71 
72 }
73 
74 
75 
default_key_cmp_func(const void * key1,const void * key2)76 static int default_key_cmp_func(const void *key1, const void *key2)
77 {
78         return strcmp((const char *) key1, (const char *) key2);
79 }
80 
81 
82 
hash_value_calc(prelude_hash_t * hash,const void * key)83 static unsigned int hash_value_calc(prelude_hash_t *hash, const void *key)
84 {
85         return (hash->hash_func(key) % hash->lists_size);
86 }
87 
88 
89 
hash_elem_get(prelude_hash_t * hash,const void * key)90 static hash_elem_t *hash_elem_get(prelude_hash_t *hash, const void *key)
91 {
92         prelude_list_t *list;
93         prelude_list_t *ptr;
94         hash_elem_t *hash_elem;
95 
96         list = hash->lists + hash_value_calc(hash, key);
97 
98         prelude_list_for_each(list, ptr) {
99                 hash_elem = prelude_list_entry(ptr, hash_elem_t, list);
100                 if ( hash->key_cmp_func(key, hash_elem->key) == 0 )
101                         return hash_elem;
102         }
103 
104         return NULL;
105 }
106 
107 
108 
hash_elem_key_destroy(prelude_hash_t * hash,hash_elem_t * hash_elem)109 static  void hash_elem_key_destroy(prelude_hash_t *hash, hash_elem_t *hash_elem)
110 {
111         if ( hash->key_destroy_func )
112                 hash->key_destroy_func(hash_elem->key);
113 }
114 
115 
116 
hash_elem_value_destroy(prelude_hash_t * hash,hash_elem_t * hash_elem)117 static void hash_elem_value_destroy(prelude_hash_t *hash, hash_elem_t *hash_elem)
118 {
119         if ( hash->value_destroy_func )
120                 hash->value_destroy_func(hash_elem->value);
121 }
122 
123 
124 
prelude_hash_new(prelude_hash_t ** nhash,unsigned int (* hash_func)(const void *),int (* key_cmp_func)(const void *,const void *),void (* key_destroy_func)(void *),void (* value_destroy_func)(void *))125 int prelude_hash_new(prelude_hash_t **nhash,
126                      unsigned int (*hash_func)(const void *),
127                      int (*key_cmp_func)(const void *, const void *),
128                      void (*key_destroy_func)(void *),
129                      void (*value_destroy_func)(void *))
130 {
131         return prelude_hash_new2(nhash, HASH_DEFAULT_SIZE, hash_func, key_cmp_func, key_destroy_func, value_destroy_func);
132 }
133 
134 
135 
prelude_hash_new2(prelude_hash_t ** nhash,size_t size,unsigned int (* hash_func)(const void *),int (* key_cmp_func)(const void *,const void *),void (* key_destroy_func)(void *),void (* value_destroy_func)(void *))136 int prelude_hash_new2(prelude_hash_t **nhash, size_t size,
137                       unsigned int (*hash_func)(const void *),
138                       int (*key_cmp_func)(const void *, const void *),
139                       void (*key_destroy_func)(void *),
140                       void (*value_destroy_func)(void *))
141 {
142         size_t i;
143         prelude_hash_t *hash;
144 
145         *nhash = hash = calloc(1, sizeof (*hash));
146         if ( ! hash )
147                 return prelude_error_from_errno(errno);
148 
149         hash->lists_size = size;
150 
151         hash->lists = malloc(hash->lists_size * sizeof(*hash->lists));
152         if ( ! hash->lists ) {
153                 free(hash);
154                 return prelude_error_from_errno(errno);
155         }
156 
157         hash->hash_func = hash_func ? hash_func : default_hash_func;
158         hash->key_cmp_func = key_cmp_func ? key_cmp_func : default_key_cmp_func;
159         hash->key_destroy_func = key_destroy_func;
160         hash->value_destroy_func = value_destroy_func;
161 
162         for ( i = 0; i < hash->lists_size; i++ )
163                 prelude_list_init(hash->lists + i);
164 
165         return 0;
166 }
167 
168 
169 
prelude_hash_destroy(prelude_hash_t * hash)170 void prelude_hash_destroy(prelude_hash_t *hash)
171 {
172         size_t cnt;
173         prelude_list_t *list;
174         prelude_list_t *ptr;
175         prelude_list_t *tmp;
176         hash_elem_t *hash_elem;
177 
178         for ( cnt = 0; cnt < hash->lists_size; cnt++ ) {
179                 list = hash->lists + cnt;
180 
181                 prelude_list_for_each_safe(list, ptr, tmp) {
182                         hash_elem = prelude_list_entry(ptr, hash_elem_t, list);
183 
184                         hash_elem_key_destroy(hash, hash_elem);
185                         hash_elem_value_destroy(hash, hash_elem);
186                         prelude_list_del(&hash_elem->list);
187                         free(hash_elem);
188                 }
189         }
190 
191         free(hash->lists);
192         free(hash);
193 }
194 
195 
196 
prelude_hash_add(prelude_hash_t * hash,void * key,void * value)197 int prelude_hash_add(prelude_hash_t *hash, void *key, void *value)
198 {
199         hash_elem_t *hash_elem;
200         prelude_list_t *list;
201 
202         hash_elem = calloc(1, sizeof(*hash_elem));
203         if ( ! hash_elem )
204                 return prelude_error_from_errno(errno);
205 
206         hash_elem->key = key;
207         hash_elem->value = value;
208 
209         list = hash->lists + hash_value_calc(hash, key);
210         prelude_list_add(list, &hash_elem->list);
211 
212         return 1;
213 }
214 
215 
216 
prelude_hash_set(prelude_hash_t * hash,void * key,void * value)217 int prelude_hash_set(prelude_hash_t *hash, void *key, void *value)
218 {
219         hash_elem_t *hash_elem = hash_elem_get(hash, key);
220 
221         if ( hash_elem ) {
222                 hash_elem_key_destroy(hash, hash_elem);
223                 hash_elem_value_destroy(hash, hash_elem);
224                 hash_elem->key = key;
225                 hash_elem->value = value;
226                 return 0;
227         }
228 
229         return prelude_hash_add(hash, key, value);
230 }
231 
232 
233 
prelude_hash_get(prelude_hash_t * hash,const void * key)234 void *prelude_hash_get(prelude_hash_t *hash, const void *key)
235 {
236         hash_elem_t *hash_elem;
237 
238         return (hash_elem = hash_elem_get(hash, key)) ? hash_elem->value : NULL;
239 }
240 
241 
242 
prelude_hash_elem_destroy(prelude_hash_t * hash,const void * key)243 int prelude_hash_elem_destroy(prelude_hash_t *hash, const void *key)
244 {
245         hash_elem_t *hash_elem;
246 
247         hash_elem = hash_elem_get(hash, key);
248 
249         if ( ! hash_elem )
250                 return -1;
251 
252         hash_elem_key_destroy(hash, hash_elem);
253         hash_elem_value_destroy(hash, hash_elem);
254         prelude_list_del(&hash_elem->list);
255         free(hash_elem);
256 
257         return 0;
258 }
259 
260 
261 
prelude_hash_iterate(prelude_hash_t * hash,void (* cb)(void * data))262 void prelude_hash_iterate(prelude_hash_t *hash, void (*cb)(void *data))
263 {
264         unsigned int i;
265         prelude_list_t *tmp;
266         hash_elem_t *hash_elem;
267 
268         for ( i = 0; i < hash->lists_size; i++ ) {
269                 prelude_list_for_each(&hash->lists[i], tmp) {
270                         hash_elem = prelude_list_entry(tmp, hash_elem_t, list);
271                         cb(hash_elem->value);
272                 }
273         }
274 }
275