source: mainline/uspace/lib/nic/src/nic_addr_db.c

Last change on this file was 0db0df2, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 3 months ago

Hash table improvements

Implement hash_table_foreach macro, analogous to list_foreach.

Remove superfluous argument to hash_table_find_next().
(If the user needs to recheck the part of the list already
checked by hash_table_find(), they can just rerun that function.)

Add hash argument to hash_table_ops_t::key_equal.
The big change here is that users with big keys can store the hash
value alongside key in their entries, and for the low low cost of
sizeof(size_t) bytes eliminate a bunch of expensive key comparisons.

Also added a hash function for strings and arbitrary data.
Found this one by asking ChatGPT, because the latency of accesses
to my book collection is currently a couple of hours.

+ Some drive-by unused #include removal.

  • Property mode set to 100644
File size: 6.4 KB
Line 
1/*
2 * Copyright (c) 2011 Radim Vansa
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/**
30 * @addtogroup libnic
31 * @{
32 */
33/**
34 * @file
35 * @brief Generic hash-set based database of addresses
36 */
37
38#include "nic_addr_db.h"
39#include <assert.h>
40#include <stdlib.h>
41#include <stdbool.h>
42#include <errno.h>
43#include <mem.h>
44#include <member.h>
45#include <adt/hash_table.h>
46#include <macros.h>
47#include <stdint.h>
48
49/**
50 * Helper structure for keeping the address in the hash set.
51 */
52typedef struct nic_addr_entry {
53 ht_link_t link;
54 size_t hash;
55 uint8_t len;
56 uint8_t addr[];
57} nic_addr_entry_t;
58
59/*
60 * Hash table helper functions
61 */
62typedef struct {
63 size_t hash;
64 size_t len;
65 const uint8_t *addr;
66} addr_key_t;
67
68static bool nic_addr_key_equal(const void *key_arg, size_t hash, const ht_link_t *item)
69{
70 const addr_key_t *key = key_arg;
71 nic_addr_entry_t *entry = member_to_inst(item, nic_addr_entry_t, link);
72 return entry->hash == hash && memcmp(entry->addr, key->addr, entry->len) == 0;
73}
74
75static size_t addr_hash(size_t len, const uint8_t *addr)
76{
77 size_t hash = 0;
78
79 for (size_t i = 0; i < len; ++i) {
80 hash = (hash << 5) ^ addr[i];
81 }
82
83 return hash;
84}
85
86static size_t nic_addr_key_hash(const void *k)
87{
88 const addr_key_t *key = k;
89 return key->hash ? key->hash : addr_hash(key->len, key->addr);
90}
91
92static size_t nic_addr_hash(const ht_link_t *item)
93{
94 nic_addr_entry_t *entry = member_to_inst(item, nic_addr_entry_t, link);
95 return entry->hash;
96}
97
98static void nic_addr_removed(ht_link_t *item)
99{
100 nic_addr_entry_t *entry = member_to_inst(item, nic_addr_entry_t, link);
101 free(entry);
102}
103
104static const hash_table_ops_t set_ops = {
105 .hash = nic_addr_hash,
106 .key_hash = nic_addr_key_hash,
107 .key_equal = nic_addr_key_equal,
108 .equal = NULL,
109 .remove_callback = nic_addr_removed
110};
111
112/**
113 * Initialize the database
114 *
115 * @param[in,out] db Uninitialized storage
116 * @param[in] addr_len Size of addresses in the db
117 *
118 * @return EOK If successfully initialized
119 * @return EINVAL If the address length is too big
120 * @return ENOMEM If there was not enough memory to initialize the storage
121 */
122errno_t nic_addr_db_init(nic_addr_db_t *db, size_t addr_len)
123{
124 assert(db);
125
126 if (addr_len > UCHAR_MAX)
127 return EINVAL;
128
129 if (!hash_table_create(&db->set, 0, 0, &set_ops))
130 return ENOMEM;
131
132 db->addr_len = addr_len;
133 return EOK;
134}
135
136/**
137 * Remove all records from the DB
138 *
139 * @param db
140 */
141void nic_addr_db_clear(nic_addr_db_t *db)
142{
143 assert(db);
144 hash_table_clear(&db->set);
145}
146
147/**
148 * Free the memory used by db, including all records...
149 *
150 * @param db
151 */
152void nic_addr_db_destroy(nic_addr_db_t *db)
153{
154 assert(db);
155 hash_table_destroy(&db->set);
156}
157
158/**
159 * Insert an address to the db
160 *
161 * @param db
162 * @param addr Inserted address. Length is implicitly concluded from the
163 * db's address length.
164 *
165 * @return EOK If the address was inserted
166 * @return ENOMEM If there was not enough memory
167 * @return EEXIST If this address already is in the db
168 */
169errno_t nic_addr_db_insert(nic_addr_db_t *db, const uint8_t *addr)
170{
171 assert(db && addr);
172
173 addr_key_t key = {
174 .len = db->addr_len,
175 .addr = addr,
176 .hash = addr_hash(db->addr_len, addr),
177 };
178
179 if (hash_table_find(&db->set, &key))
180 return EEXIST;
181
182 nic_addr_entry_t *entry = malloc(offsetof(nic_addr_entry_t, addr) + db->addr_len);
183 if (entry == NULL)
184 return ENOMEM;
185
186 entry->len = (uint8_t) db->addr_len;
187 memcpy(entry->addr, addr, db->addr_len);
188 entry->hash = key.hash;
189
190 hash_table_insert(&db->set, &entry->link);
191 return EOK;
192}
193
194/**
195 * Remove the address from the db
196 *
197 * @param db
198 * @param addr Removed address.
199 *
200 * @return EOK If the address was removed
201 * @return ENOENT If there is no address
202 */
203errno_t nic_addr_db_remove(nic_addr_db_t *db, const uint8_t *addr)
204{
205 assert(db && addr);
206
207 addr_key_t key = {
208 .len = db->addr_len,
209 .addr = addr
210 };
211
212 if (hash_table_remove(&db->set, &key))
213 return EOK;
214 else
215 return ENOENT;
216}
217
218/**
219 * Test if the address is contained in the db
220 *
221 * @param db
222 * @param addr Tested addresss
223 *
224 * @return true if the address is in the db, false otherwise
225 */
226bool nic_addr_db_contains(const nic_addr_db_t *db, const uint8_t *addr)
227{
228 assert(db && addr);
229
230 addr_key_t key = {
231 .len = db->addr_len,
232 .addr = addr
233 };
234
235 return 0 != hash_table_find(&db->set, &key);
236}
237
238/**
239 * Helper structure for nic_addr_db_foreach
240 */
241typedef struct {
242 void (*func)(const uint8_t *, void *);
243 void *arg;
244} nic_addr_db_fe_arg_t;
245
246/**
247 * Helper function for nic_addr_db_foreach
248 */
249static bool nic_addr_db_fe_helper(ht_link_t *item, void *arg)
250{
251 nic_addr_db_fe_arg_t *hs = (nic_addr_db_fe_arg_t *) arg;
252 nic_addr_entry_t *entry = member_to_inst(item, nic_addr_entry_t, link);
253 hs->func(entry->addr, hs->arg);
254 return true;
255}
256
257/**
258 * Executes a user-defined function on all addresses in the DB. The function
259 * must not change the addresses.
260 *
261 * @param db
262 * @param func User-defined function
263 * @param arg Custom argument passed to the function
264 */
265void nic_addr_db_foreach(const nic_addr_db_t *db,
266 void (*func)(const uint8_t *, void *), void *arg)
267{
268 nic_addr_db_fe_arg_t hs = { .func = func, .arg = arg };
269 hash_table_apply((hash_table_t *)&db->set, nic_addr_db_fe_helper, &hs);
270}
271
272/** @}
273 */
Note: See TracBrowser for help on using the repository browser.