Add the hashtable test module.
[bertos.git] / bertos / struct / hashtable.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2004, 2008 Develer S.r.l. (http://www.develer.com/)
30  * Copyright 2004 Giovanni Bajo
31  * -->
32  *
33  * \brief Portable hash table implementation
34  *
35  * Some rationales of our choices in implementation:
36  *
37  * \li For embedded systems, it is vital to allocate the table in static memory. To do
38  * so, it is necessary to expose the \c HashNode and \c HashTable structures in the header file.
39  * Nevertheless, they should be used as opaque types (that is, the users should not
40  * access the structure fields directly).
41  *
42  * \li To statically allocate the structures, a macro is provided. With this macro, we
43  * are hiding completely \c HashNode to the user (who only manipulates \c HashTable). Without
44  * the macro, the user would have had to define both the \c HashNode and the \c HashTable
45  * manually, and pass both of them to \c ht_init() (which would have created the link between
46  * the two). Instead, the link is created with a literal initialization.
47  *
48  * \li The hash table is created as power of two to remove the divisions from the code.
49  * Of course, hash functions work at their best when the table size is a prime number.
50  * When calculating the modulus to convert the hash value to an index, the actual operation
51  * becomes a bitwise AND: this is fast, but truncates the value losing bits. Thus, the higher
52  * bits are first "merged" with the lower bits through some XOR operations (see the last line of
53  * \c calc_hash()).
54  *
55  * \li To minimize the memory occupation, there is no flag to set for the empty node. An
56  * empty node is recognized by its data pointer set to NULL. It is then invalid to store
57  * NULL as data pointer in the table.
58  *
59  * \li The visiting interface through iterators is implemented with pass-by-value semantic.
60  * While this is overkill for medium-to-stupid compilers, it is the best designed from an
61  * user point of view. Moreover, being totally inlined (defined completely in the header),
62  * even a stupid compiler should be able to perform basic optimizations on it.
63  * We thought about using a pass-by-pointer semantic but it was much more awful to use, and
64  * the compiler is then forced to spill everything to the stack (unless it is *very* smart).
65  *
66  * \li The current implementation allows to either store the key internally (that is, copy
67  * the key within the hash table) or keep it external (that is, a hook is used to extract
68  * the key from the data in the node). The former is more memory-hungry of course, as it
69  * allocated static space to store the key copies. The overhead to keep both methods at
70  * the same time is minimal:
71  *    <ul>
72  *    <li>There is a run-time check in node_get_key which is execute per each node visited.</li>
73  *    <li>Theoretically, there is no memory overhead. In practice, there were no
74  *        flags in \c struct HashTable till now, so we had to add a first bit flag, but the
75  *        overhead will disappear if a second flag is added for a different reason later.</li>
76  *    <li>There is a little interface overhead, since we have two different versions of
77  *        \c ht_insert(), one with the key passed as parameter and one without, but in
78  *        the common case (external keys) both can be used.</li>
79  *    </ul>
80  *
81  * \author Giovanni Bajo <rasky@develer.com>
82  */
83
84 #include "hashtable.h"
85
86 #include "cfg/cfg_hashtable.h"
87 #include <cfg/debug.h>
88 #include <cfg/compiler.h>
89 #include <cfg/macros.h> //ROTL(), ROTR();
90
91 #include <string.h>
92
93
94 typedef const void** HashNodePtr;
95 #define NODE_EMPTY(node)               (!*(node))
96 #define HT_HAS_INTERNAL_KEY(ht)        (CONFIG_HT_OPTIONAL_INTERNAL_KEY && ht->flags.key_internal)
97
98 /** For hash tables with internal keys, compute the pointer to the internal key for a given \a node. */
99 INLINE uint8_t *key_internal_get_ptr(struct HashTable *ht, HashNodePtr node)
100 {
101         uint8_t* key_buf = ht->key_data.mem;
102         size_t index;
103
104         // Compute the index of the node and use it to move within the whole key buffer
105         index = node - &ht->mem[0];
106         ASSERT(index < (size_t)(1 << ht->max_elts_log2));
107         key_buf += index * (INTERNAL_KEY_MAX_LENGTH + 1);
108
109         return key_buf;
110 }
111
112
113 INLINE void node_get_key(struct HashTable* ht, HashNodePtr node, const void** key, uint8_t* key_length)
114 {
115         if (HT_HAS_INTERNAL_KEY(ht))
116         {
117                 uint8_t* k = key_internal_get_ptr(ht, node);
118
119                 // Key has its length stored in the first byte
120                 *key_length = *k++;
121                 *key = k;
122         }
123         else
124                 *key = ht->key_data.hook(*node, key_length);
125 }
126
127
128 INLINE bool node_key_match(struct HashTable* ht, HashNodePtr node, const void* key, uint8_t key_length)
129 {
130         const void* key2;
131         uint8_t key2_length;
132
133         node_get_key(ht, node, &key2, &key2_length);
134
135         return (key_length == key2_length && memcmp(key, key2, key_length) == 0);
136 }
137
138
139 static uint16_t calc_hash(const void* _key, uint8_t key_length)
140 {
141         const char* key = (const char*)_key;
142         uint16_t hash = key_length;
143         int i;
144         int len = (int)key_length;
145
146         for (i = 0; i < len; ++i)
147                 hash = ROTL(hash, 4) ^ key[i];
148
149         return hash ^ (hash >> 6) ^ (hash >> 13);
150 }
151
152
153 static HashNodePtr perform_lookup(struct HashTable* ht,
154                                   const void* key, uint8_t key_length)
155 {
156         uint16_t hash = calc_hash(key, key_length);
157         uint16_t mask = ((1 << ht->max_elts_log2) - 1);
158         uint16_t index = hash & mask;
159         uint16_t first_index = index;
160         uint16_t step;
161         HashNodePtr node;
162
163         // Fast-path optimization: we check immediately if the current node
164         //  is the one we were looking for, so we save the computation of the
165         //  increment step in the common case.
166         node = &ht->mem[index];
167         if (NODE_EMPTY(node)
168                 || node_key_match(ht, node, key, key_length))
169                 return node;
170
171         // Increment while going through the hash table in case of collision.
172         //  This implements the double-hash technique: we use the higher part
173         //  of the hash as a step increment instead of just going to the next
174         //  element, to minimize the collisions.
175         // Notice that the number must be odd to be sure that the whole table
176         //  is traversed. Actually MCD(table_size, step) must be 1, but
177         //  table_size is always a power of 2, so we just ensure that step is
178         //  never a multiple of 2.
179         step = (ROTR(hash, ht->max_elts_log2) & mask) | 1;
180
181         do
182         {
183                 index += step;
184                 index &= mask;
185
186                 node = &ht->mem[index];
187                 if (NODE_EMPTY(node)
188                         || node_key_match(ht, node, key, key_length))
189                         return node;
190
191                 // The check is done after the key compare. This actually causes
192                 //  one more compare in the case the table is full (since the first
193                 //  element was compared at the very start, and then at the end),
194                 //  but it makes faster the common path where we enter this loop
195                 //  for the first time, and index will not match first_index for
196                 //  sure.
197         } while (index != first_index);
198
199         return NULL;
200 }
201
202
203 void ht_init(struct HashTable* ht)
204 {
205         memset(ht->mem, 0, sizeof(ht->mem[0]) * (1 << ht->max_elts_log2));
206 }
207
208
209 static bool insert(struct HashTable* ht, const void* key, uint8_t key_length, const void* data)
210 {
211         HashNodePtr node;
212
213         if (!data)
214                 return false;
215
216         if (HT_HAS_INTERNAL_KEY(ht))
217                 key_length = MIN(key_length, (uint8_t)INTERNAL_KEY_MAX_LENGTH);
218
219         node = perform_lookup(ht, key, key_length);
220         if (!node)
221                 return false;
222
223         if (HT_HAS_INTERNAL_KEY(ht))
224         {
225                 uint8_t* k = key_internal_get_ptr(ht, node);
226                 *k++ = key_length;
227                 memcpy(k, key, key_length);
228         }
229
230         *node = data;
231         return true;
232 }
233
234
235 bool ht_insert_with_key(struct HashTable* ht, const void* key, uint8_t key_length, const void* data)
236 {
237 #ifdef _DEBUG
238         if (!HT_HAS_INTERNAL_KEY(ht))
239         {
240                 // Construct a fake node and use it to match the key
241                 HashNodePtr node = &data;
242                 if (!node_key_match(ht, node, key, key_length))
243                 {
244                         ASSERT2(0, "parameter key is different from the external key");
245                         return false;
246                 }
247         }
248 #endif
249
250         return insert(ht, key, key_length, data);
251 }
252
253
254 bool ht_insert(struct HashTable* ht, const void* data)
255 {
256         const void* key;
257         uint8_t key_length;
258
259 #ifdef _DEBUG
260         if (HT_HAS_INTERNAL_KEY(ht))
261         {
262                 ASSERT("parameter cannot be a hash table with internal keys - use ht_insert_with_key()"
263                        && 0);
264                 return false;
265         }
266 #endif
267
268         key = ht->key_data.hook(data, &key_length);
269
270         return insert(ht, key, key_length, data);
271 }
272
273
274 const void* ht_find(struct HashTable* ht, const void* key, uint8_t key_length)
275 {
276         HashNodePtr node;
277
278         if (HT_HAS_INTERNAL_KEY(ht))
279                 key_length = MIN(key_length, (uint8_t)INTERNAL_KEY_MAX_LENGTH);
280
281         node = perform_lookup(ht, key, key_length);
282
283         if (!node || NODE_EMPTY(node))
284                 return NULL;
285
286         return *node;
287 }