blob: 12e19627e8be0e52d02ebd28cc7a119be90f0ae4 [file] [log] [blame]
Shad Ansari2f7f9be2017-06-07 13:34:53 -07001/*
2<:copyright-BRCM:2016:DUAL/GPL:standard
3
4 Broadcom Proprietary and Confidential.(c) 2016 Broadcom
5 All Rights Reserved
6
7Unless you and Broadcom execute a separate written software license
8agreement governing use of this software, this software is licensed
9to you under the terms of the GNU General Public License version 2
10(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
11with the following added to such license:
12
13 As a special exception, the copyright holders of this software give
14 you permission to link this software with independent modules, and
15 to copy and distribute the resulting executable under terms of your
16 choice, provided that you also meet, for each linked independent
17 module, the terms and conditions of the license of that module.
18 An independent module is a module which is not derived from this
19 software. The special exception does not apply to any modifications
20 of the software.
21
22Not withstanding the above, under no circumstances may you combine
23this software in any way with any other Broadcom software provided
24under a license other than the GPL, without Broadcom's express prior
25written consent.
26
27:>
28 */
29#include "bcmos_hash_table.h"
30
31#define ht_overhead sizeof(void *)
32
33/** Create a hash table using a block pool
34 * \param[in] pool_parm pointer to block pool parameters
35 * \param[in] max_data_entries Maximum entries the hash table needs to hold
36 * \param[in] entry_size Size of each entry in bytes
37 * \param[in] key_size Size of each key in bytes
38 * \return pointer to newly created hash table
39 */
40static hash_table *hash_table_create_in_pool(bcmos_blk_pool_parm *pool_parm,
41 uint32_t max_data_entries,
42 uint16_t entry_size,
43 uint8_t key_size)
44{
45 uint32_t lookup_table_entries = max_data_entries + (max_data_entries / 4);
46 hash_table *ht;
47 bcmos_errno err;
48
49 if (pool_parm == NULL)
50 {
51 BUG();
52 return NULL;
53 }
54
55 entry_size += key_size;
56
57 ht = bcmos_alloc(sizeof(hash_table));
58 if (ht == NULL)
59 {
60 BUG();
61 return NULL;
62 }
63
64 ht->obj_len = entry_size;
65 ht->key_len = key_size;
66 ht->data_offset = ht_overhead;
67 ht->ht_lookup_tbl_entries = lookup_table_entries;
68 ht->ht_max_data_entries = max_data_entries;
69 ht->look_up_entries_tbl = bcmos_alloc(lookup_table_entries * sizeof(ht_block));
70
71 if (ht->look_up_entries_tbl == NULL)
72 {
73 bcmos_free(ht);
74 BUG();
75 return NULL;
76 }
77
78 ht->ht_cur_entries = 0;
79 err = bcmos_blk_pool_create(&ht->key_data_pool, pool_parm);
80
81 if (err != BCM_ERR_OK)
82 {
83 bcmos_free(ht->look_up_entries_tbl);
84 bcmos_free(ht);
85 BUG();
86 return NULL;
87 }
88
89 if (ht->ht_max_data_entries > ht->ht_lookup_tbl_entries)
90 {
91 BUG();
92 }
93
94 ht->obj_len -= ht->key_len;
95
96 memset(ht->look_up_entries_tbl, 0, lookup_table_entries * sizeof(ht_block));
97 return ht;
98}
99
100hash_table *hash_table_create(uint32_t max_data_entries,
101 uint16_t entry_size,
102 uint8_t key_size,
103 char *pool_name)
104{
105 bcmos_blk_pool_parm parm = {0};
106
107 parm.name = pool_name;
108 parm.blk_size = entry_size + key_size + ht_overhead;
109 parm.num_blks = max_data_entries;
110
111 return hash_table_create_in_pool(&parm, max_data_entries, entry_size, key_size);
112}
113
114/** Hash a length of bytes into a uint32_t
115 * \param[in] key Bytes to hash
116 * \param[in] len Number of bytes in key
117 * \return The hash as a uint32_t
118 */
119static uint32_t get_hash_for_key(const uint8_t *key, uint8_t len)
120{
121 uint32_t hash = 5381;
122 uint8_t i;
123 const uint8_t *tmp = key;
124 for (i = 0; i < len; tmp++, i++)
125 {
126 hash = ((hash << 5) + hash) + (*tmp);
127 }
128
129 return hash;
130}
131
132/** Gets a hash key used for the keyDataPool in the HashTable
133 * \param[in] ht Associated hashtable we are getting hash for key for
134 * \param[in] key Key we are getting hash for
135 * \return An index into the keyDataPool
136 */
137static uint32_t get_hash_val_for_key(const hash_table *ht, const uint8_t *key)
138{
139 return get_hash_for_key(key, ht->key_len) % ht->ht_lookup_tbl_entries;
140}
141
142/** Returns the location of the key within an HtBlock
143 * \param[in] ht Hash table in question
144 * \param[in] block HtBlock in question
145 * \return pointer to the data within the block
146 */
147static uint8_t *get_key_from_block_in_table(const hash_table *ht, ht_block *block)
148{
149 return(uint8_t *) block + ht->data_offset + ht->obj_len;
150}
151
152/** Gets and populates a HtBlock with all of its data
153 * \param[in] next Next block in this buckets chain
154 * \param[in] ht Hash table in question
155 * \param[in] key This blocks key
156 * \param[in] val This blocks data
157 * \return The block that we allocated and returned
158 */
159static ht_block *fill_ht_block(ht_block *next,
160 hash_table *ht,
161 const uint8_t *key,
162 const void *val)
163{
164 ht_block *dest_block = bcmos_blk_pool_alloc(&ht->key_data_pool);
165
166 if (dest_block != NULL)
167 {
168 /* storage is nextobj ptr, hash obj,
169 key which keeps all uint32_t aligned. */
170 dest_block->next_chain = next;
171
172 if (val != NULL)
173 {
174 memcpy((uint8_t *) dest_block + ht->data_offset, val, ht->obj_len);
175 }
176 else
177 {
178 memset((uint8_t *) dest_block + ht->data_offset,0,ht->obj_len);
179 }
180
181 /* Need to put key in after obj */
182 memcpy(
183 (uint8_t *) dest_block + ht->data_offset + ht->obj_len,
184 key,
185 ht->key_len);
186 }
187
188 return dest_block;
189}
190
191/** Determine whether two keys in a particular hash table match
192 * \param[in] ht Hashtable
193 * \param[in] key_a first key to compare
194 * \param[in] key_b second key to compare
195 * \return whether they are the same
196 */
197static bcmos_bool is_key_match(const hash_table *ht, const uint8_t *key_a, const uint8_t *key_b)
198{
199 return memcmp(key_a, key_b, ht->key_len) == 0;
200}
201
202/** Searches a chained bucket looking for an instance of the key. If found returns the block if found the key in.
203 * Prev guy is set to the block in the chain before the block we returned (except in the case * where there is no
204 * block before the one we returned.
205 * \param[in] ht HashTable in question
206 * \param[in] key_to_find Key we wonder if exists
207 * \param[in] chain_start Where to start looking in the chain
208 * \param[in] prev_block The previous guy before the block we returned (if exists)
209 * \return The block that matches doesExists (if exists)
210 */
211static ht_block *get_key_loc_in_chain(
212 const hash_table *ht,
213 const uint8_t *key_to_find,
214 ht_block *chain_start,
215 ht_block **prev_block)
216{
217 *prev_block = NULL;
218 while (chain_start != NULL)
219 {
220 if (is_key_match(ht, key_to_find, get_key_from_block_in_table(ht, chain_start)))
221 {
222 return chain_start;
223 }
224 *prev_block = chain_start;
225 chain_start = chain_start->next_chain;
226 }
227 return NULL;
228}
229
230bcmos_bool hash_table_remove(hash_table *ht, const uint8_t *key)
231{
232 uint32_t hash_val = get_hash_val_for_key(ht, key);
233 ht_block *prev_entry;
234 ht_block *entry = get_key_loc_in_chain(
235 ht,
236 key,
237 ht->look_up_entries_tbl[hash_val].next_chain,
238 &prev_entry);
239
240 if (entry == NULL)
241 {
242 /* No one to delete */
243 return BCMOS_FALSE;
244 }
245 else
246 {
247 ht->ht_cur_entries--;
248 if (prev_entry == NULL)
249 {
250 /* last entry */
251 ht->look_up_entries_tbl[hash_val].next_chain = entry->next_chain;
252 }
253 else
254 {
255 prev_entry->next_chain = entry->next_chain;
256 }
257 bcmos_blk_pool_free(entry);
258 return BCMOS_TRUE;
259 }
260}
261
262/** Returns a pointer to the data within the HT
263 * \param[in] ht Hashtable in question
264 * \param[in] block_ptr HtBlock that we wonder where its data is
265 */
266static void *get_ht_data_ptr(const hash_table *ht, ht_block *block_ptr)
267{
268 return(uint8_t*)block_ptr + ht->data_offset;
269}
270
271
272/** Get an entry in the hash table
273 * \param[in] ht pointer to hash table
274 * \param[in] key pointer to key data
275 * \param[in] hash_val hash value of key
276 * \return pointer to hash table entry
277 */
278static inline void *ht_get_internal(const hash_table *ht,
279 const uint8_t *key,
280 uint32_t hash_val)
281{
282 ht_block *tmp;
283 ht_block *ret;
284 ret = get_key_loc_in_chain(
285 ht,
286 key,
287 ht->look_up_entries_tbl[hash_val].next_chain,
288 &tmp);
289 if (ret != NULL)
290 {
291 return get_ht_data_ptr(ht,ret);
292 }
293 else
294 {
295 return ret;
296 }
297}
298
299void *hash_table_get(const hash_table *ht, const uint8_t *key)
300{
301 uint32_t hashVal = get_hash_val_for_key(ht, key);
302 return ht_get_internal(ht,key,hashVal);
303}
304
305void *hash_table_put(hash_table *ht, const uint8_t *key, const void *val)
306{
307 void *ret_block;
308 uint32_t hash_val;
309 if (ht->ht_cur_entries >= ht->ht_max_data_entries)
310 {
311 return NULL;
312 }
313 hash_val = get_hash_val_for_key(ht, key);
314
315 ret_block = ht_get_internal((const hash_table *) ht, key, hash_val);
316 if (ret_block != NULL)
317 {
318 /* replace existing value with new value */
319 if (val != NULL)
320 {
321 memcpy(ret_block, val, ht->obj_len);
322 }
323 else
324 {
325 memset(ret_block, 0, ht->obj_len);
326 }
327 return ret_block;
328 }
329 else
330 {
331 ht_block *new_block=fill_ht_block(
332 ht->look_up_entries_tbl[hash_val].next_chain, ht, key, val);
333 if (new_block != NULL)
334 {
335 ht->look_up_entries_tbl[hash_val].next_chain = new_block;
336 ht->ht_cur_entries++;
337 return get_ht_data_ptr(ht,new_block);
338 }
339 else
340 {
341 return NULL;
342 }
343 }
344
345}
346
347ht_iterator ht_iterator_get(const hash_table *ht)
348{
349 ht_iterator to_ret;
350 to_ret.ht = ht;
351 to_ret.cur_idx = 0;
352 to_ret.removed_at = BCMOS_FALSE;
353 to_ret.still_valid = BCMOS_TRUE;
354 to_ret.cur_block = NULL;
355 return to_ret;
356}
357
358ht_iterator ht_iterator_get_by_key(const hash_table *ht, const uint8_t *key)
359{
360 ht_block *tmp;
361 uint32_t hash_val = get_hash_val_for_key(ht, key);
362 ht_iterator to_ret = {};
363 to_ret.ht = ht;
364 to_ret.removed_at = BCMOS_FALSE;
365 to_ret.cur_block = get_key_loc_in_chain(ht, key, ht->look_up_entries_tbl[hash_val].next_chain, &tmp);
366 to_ret.still_valid = (to_ret.cur_block != NULL);
367 to_ret.cur_idx = hash_val;
368
369 return to_ret;
370}
371
372/** Advance linear scan iterator
373 * \param[in] it Iterator to advance
374 */
375static void ht_iterator_scan_adv(ht_iterator *it)
376{
377 if (it->cur_block != NULL)
378 {
379 it->cur_block = it->cur_block->next_chain;
380 if (it->cur_block != NULL)
381 {
382 it->still_valid = BCMOS_TRUE;
383 return;
384 }
385 else
386 {
387 it->cur_idx++;
388 }
389 }
390 /* find non null entry */
391 while (it->cur_idx < it->ht->ht_lookup_tbl_entries)
392 {
393 if (it->ht->look_up_entries_tbl[it->cur_idx].next_chain != NULL)
394 {
395 it->cur_block = it->ht->look_up_entries_tbl[it->cur_idx].next_chain;
396 it->still_valid = BCMOS_TRUE;
397 return;
398 }
399 else
400 {
401 it->cur_idx++;
402 }
403 }
404 it->still_valid = BCMOS_FALSE;
405}
406
407void ht_iterator_deref(const ht_iterator *hti, uint8_t **key, void **obj)
408{
409 if (!hti->still_valid)
410 {
411 BCMOS_TRACE_ERR("%s: Invalid deref\n", __FUNCTION__);
412 }
413 else
414 {
415 *key = get_key_from_block_in_table(hti->ht, hti->cur_block);
416 *obj = get_ht_data_ptr(hti->ht, hti->cur_block); /* to data */
417 }
418}
419
420void ht_iterator_remove_at(hash_table *ht, ht_iterator *it)
421{
422 if (ht != it->ht)
423 {
424 BCMOS_TRACE_ERR("%s: Incorrect writeable hash table pointer\n", __FUNCTION__);
425 }
426 else if (it->removed_at)
427 {
428 BCMOS_TRACE_ERR("%s: No delete twice\n", __FUNCTION__);
429 }
430 else
431 {
432 uint8_t *key;
433 uint8_t *obj;
434 ht_iterator_deref(it, &key, (void **) &obj);
435 it->removed_at = BCMOS_TRUE;
436 it->still_valid = ht_iterator_next(it);
437 if (!hash_table_remove(ht, key))
438 {
439 BCMOS_TRACE_ERR("%s: Remove error\n", __FUNCTION__);
440 }
441 }
442}
443
444bcmos_bool ht_iterator_next(ht_iterator *it)
445{
446 if (it->still_valid)
447 {
448 if (it->removed_at)
449 {
450 it->removed_at = BCMOS_FALSE;
451 }
452 else
453 {
454 ht_iterator_scan_adv(it);
455 }
456 }
457 return it->still_valid; /* No entry found */
458}
459
460void hash_table_clear(hash_table *ht)
461{
462 ht_iterator it = ht_iterator_get(ht);
463 while (ht_iterator_next(&it))
464 {
465 ht_iterator_remove_at(ht, &it);
466 }
467}