blob: babaaeec726088a4d88d2ed4f58895a16dd7159c [file] [log] [blame]
Ed Warnickecb9cada2015-12-08 15:45:58 -07001/*
2 * Copyright (c) 2015 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15/*
16 Copyright (c) 2010 Eliot Dresselhaus
17
18 Permission is hereby granted, free of charge, to any person obtaining
19 a copy of this software and associated documentation files (the
20 "Software"), to deal in the Software without restriction, including
21 without limitation the rights to use, copy, modify, merge, publish,
22 distribute, sublicense, and/or sell copies of the Software, and to
23 permit persons to whom the Software is furnished to do so, subject to
24 the following conditions:
25
26 The above copyright notice and this permission notice shall be
27 included in all copies or substantial portions of the Software.
28
29 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
32 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
33 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
34 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
35 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36*/
37
38#include <vppinfra/mhash.h>
39
40always_inline u32
Dave Barachc3799992016-08-15 11:12:27 -040041load_partial_u32 (void *d, uword n)
Ed Warnickecb9cada2015-12-08 15:45:58 -070042{
43 if (n == 4)
44 return ((u32 *) d)[0];
45 if (n == 3)
46 return ((u16 *) d)[0] | (((u8 *) d)[2] << 16);
47 if (n == 2)
48 return ((u16 *) d)[0];
49 if (n == 1)
50 return ((u8 *) d)[0];
51 ASSERT (0);
52 return 0;
53}
54
55always_inline u32
Dave Barachc3799992016-08-15 11:12:27 -040056mhash_key_sum_inline (void *data, uword n_data_bytes, u32 seed)
Ed Warnickecb9cada2015-12-08 15:45:58 -070057{
Dave Barachc3799992016-08-15 11:12:27 -040058 u32 *d32 = data;
Ed Warnickecb9cada2015-12-08 15:45:58 -070059 u32 a, b, c, n_left;
60
61 a = b = c = seed;
62 n_left = n_data_bytes;
63 a ^= n_data_bytes;
64
65 while (n_left > 12)
66 {
67 a += d32[0];
68 b += d32[1];
69 c += d32[2];
70 hash_v3_mix32 (a, b, c);
71 n_left -= 12;
72 d32 += 3;
73 }
74
75 if (n_left > 8)
76 {
77 c += load_partial_u32 (d32 + 2, n_left - 8);
78 n_left = 8;
79 }
80 if (n_left > 4)
81 {
82 b += load_partial_u32 (d32 + 1, n_left - 4);
83 n_left = 4;
84 }
85 if (n_left > 0)
86 a += load_partial_u32 (d32 + 0, n_left - 0);
87
88 hash_v3_finalize32 (a, b, c);
89
90 return c;
91}
92
93#define foreach_mhash_key_size \
94 _ (2) _ (3) _ (4) _ (5) _ (6) _ (7) \
95 _ (8) _ (12) _ (16) _ (20) \
96 _ (24) _ (28) _ (32) _ (36) \
97 _ (40) _ (44) _ (48) _ (52) \
98 _ (56) _ (60) _ (64)
99
100#define _(N_KEY_BYTES) \
101 static uword \
102 mhash_key_sum_##N_KEY_BYTES (hash_t * h, uword key) \
103 { \
104 mhash_t * hv = uword_to_pointer (h->user, mhash_t *); \
105 return mhash_key_sum_inline (mhash_key_to_mem (hv, key), \
106 (N_KEY_BYTES), \
107 hv->hash_seed); \
108 } \
109 \
110 static uword \
111 mhash_key_equal_##N_KEY_BYTES (hash_t * h, uword key1, uword key2) \
112 { \
113 mhash_t * hv = uword_to_pointer (h->user, mhash_t *); \
114 void * k1 = mhash_key_to_mem (hv, key1); \
115 void * k2 = mhash_key_to_mem (hv, key2); \
116 return ! memcmp (k1, k2, (N_KEY_BYTES)); \
117 }
118
119foreach_mhash_key_size
Ed Warnickecb9cada2015-12-08 15:45:58 -0700120#undef _
Ed Warnickecb9cada2015-12-08 15:45:58 -0700121static uword
122mhash_key_sum_c_string (hash_t * h, uword key)
123{
Dave Barachc3799992016-08-15 11:12:27 -0400124 mhash_t *hv = uword_to_pointer (h->user, mhash_t *);
125 void *k = mhash_key_to_mem (hv, key);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700126 return mhash_key_sum_inline (k, strlen (k), hv->hash_seed);
127}
128
129static uword
130mhash_key_equal_c_string (hash_t * h, uword key1, uword key2)
131{
Dave Barachc3799992016-08-15 11:12:27 -0400132 mhash_t *hv = uword_to_pointer (h->user, mhash_t *);
133 void *k1 = mhash_key_to_mem (hv, key1);
134 void *k2 = mhash_key_to_mem (hv, key2);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700135 return strcmp (k1, k2) == 0;
136}
137
138static uword
139mhash_key_sum_vec_string (hash_t * h, uword key)
140{
Dave Barachc3799992016-08-15 11:12:27 -0400141 mhash_t *hv = uword_to_pointer (h->user, mhash_t *);
142 void *k = mhash_key_to_mem (hv, key);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700143 return mhash_key_sum_inline (k, vec_len (k), hv->hash_seed);
144}
145
146static uword
147mhash_key_equal_vec_string (hash_t * h, uword key1, uword key2)
148{
Dave Barachc3799992016-08-15 11:12:27 -0400149 mhash_t *hv = uword_to_pointer (h->user, mhash_t *);
150 void *k1 = mhash_key_to_mem (hv, key1);
151 void *k2 = mhash_key_to_mem (hv, key2);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700152 return vec_len (k1) == vec_len (k2) && memcmp (k1, k2, vec_len (k1)) == 0;
153}
154
155/* The CLIB hash user pointer must always point to a valid mhash_t.
156 Now, the address of mhash_t can change (think vec_resize).
157 So we must always be careful that it points to the correct
158 address. */
159always_inline void
160mhash_sanitize_hash_user (mhash_t * mh)
161{
Dave Barachc3799992016-08-15 11:12:27 -0400162 uword *hash = mh->hash;
163 hash_t *h = hash_header (hash);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700164 h->user = pointer_to_uword (mh);
165}
166
Vladislav Grishenko70328922023-12-14 17:48:28 +0100167static u8 *mhash_format_pair_default (u8 *s, va_list *args);
168
Damjan Mariondae1c7e2020-10-17 13:32:25 +0200169__clib_export void
Dave Barachc3799992016-08-15 11:12:27 -0400170mhash_init (mhash_t * h, uword n_value_bytes, uword n_key_bytes)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700171{
Dave Barachc3799992016-08-15 11:12:27 -0400172 static struct
173 {
174 hash_key_sum_function_t *key_sum;
175 hash_key_equal_function_t *key_equal;
176 } t[] =
177 {
Ed Warnickecb9cada2015-12-08 15:45:58 -0700178#define _(N_KEY_BYTES) \
179 [N_KEY_BYTES] = { \
180 .key_sum = mhash_key_sum_##N_KEY_BYTES, \
181 .key_equal = mhash_key_equal_##N_KEY_BYTES, \
182 },
183
184 foreach_mhash_key_size
Ed Warnickecb9cada2015-12-08 15:45:58 -0700185#undef _
Dave Barachc3799992016-08-15 11:12:27 -0400186 [MHASH_C_STRING_KEY] =
187 {
188 .key_sum = mhash_key_sum_c_string,.key_equal = mhash_key_equal_c_string,},
189 [MHASH_VEC_STRING_KEY] =
190 {
191 .key_sum = mhash_key_sum_vec_string,.key_equal =
192 mhash_key_equal_vec_string,},};
Ed Warnickecb9cada2015-12-08 15:45:58 -0700193
194 if (mhash_key_vector_is_heap (h))
195 heap_free (h->key_vector_or_heap);
196 else
197 vec_free (h->key_vector_or_heap);
198 vec_free (h->key_vector_free_indices);
199 {
200 int i;
201 for (i = 0; i < vec_len (h->key_tmps); i++)
202 vec_free (h->key_tmps[i]);
203 }
204 vec_free (h->key_tmps);
205 hash_free (h->hash);
206
Dave Barachb7b92992018-10-17 10:38:51 -0400207 clib_memset (h, 0, sizeof (h[0]));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700208 h->n_key_bytes = n_key_bytes;
209
Dave Barach2180bac2019-05-10 15:25:10 -0400210 vec_validate (h->key_tmps, os_get_nthreads () - 1);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700211
212 ASSERT (n_key_bytes < ARRAY_LEN (t));
Vladislav Grishenko70328922023-12-14 17:48:28 +0100213 h->hash = hash_create2 (/* elts */ 0,
Ed Warnickecb9cada2015-12-08 15:45:58 -0700214 /* user */ pointer_to_uword (h),
215 /* value_bytes */ n_value_bytes,
Dave Barachc3799992016-08-15 11:12:27 -0400216 t[n_key_bytes].key_sum, t[n_key_bytes].key_equal,
Ed Warnickecb9cada2015-12-08 15:45:58 -0700217 /* format pair/arg */
Vladislav Grishenko70328922023-12-14 17:48:28 +0100218 mhash_format_pair_default, 0);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700219}
220
Dave Barachc3799992016-08-15 11:12:27 -0400221static uword
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200222mhash_set_tmp_key (mhash_t * h, const void *key)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700223{
Dave Barachc3799992016-08-15 11:12:27 -0400224 u8 *key_tmp;
Damjan Marionf55f9b82017-05-10 21:06:28 +0200225 int my_cpu = os_get_thread_index ();
Ed Warnickecb9cada2015-12-08 15:45:58 -0700226
Ed Warnickecb9cada2015-12-08 15:45:58 -0700227 key_tmp = h->key_tmps[my_cpu];
228
229 vec_reset_length (key_tmp);
230
231 if (mhash_key_vector_is_heap (h))
232 {
233 uword is_c_string = h->n_key_bytes == MHASH_C_STRING_KEY;
234
235 if (is_c_string)
236 vec_add (key_tmp, key, strlen (key) + 1);
237 else
238 vec_add (key_tmp, key, vec_len (key));
239 }
240 else
241 vec_add (key_tmp, key, h->n_key_bytes);
242
243 h->key_tmps[my_cpu] = key_tmp;
244
245 return ~0;
246}
247
Damjan Mariondae1c7e2020-10-17 13:32:25 +0200248__clib_export hash_pair_t *
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200249mhash_get_pair (mhash_t * h, const void *key)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700250{
251 uword ikey;
252 mhash_sanitize_hash_user (h);
253 ikey = mhash_set_tmp_key (h, key);
254 return hash_get_pair (h->hash, ikey);
255}
256
Dave Barachc3799992016-08-15 11:12:27 -0400257typedef struct
258{
Ed Warnickecb9cada2015-12-08 15:45:58 -0700259 u32 heap_handle;
260
Paul Vinciguerraec11b132018-09-24 05:25:00 -0700261 /* Must coincide with vec_header. */
Ed Warnickecb9cada2015-12-08 15:45:58 -0700262 vec_header_t vec;
263} mhash_string_key_t;
264
Damjan Mariondae1c7e2020-10-17 13:32:25 +0200265__clib_export uword
Dave Barachc3799992016-08-15 11:12:27 -0400266mhash_set_mem (mhash_t * h, void *key, uword * new_value, uword * old_value)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700267{
Dave Barachc3799992016-08-15 11:12:27 -0400268 u8 *k;
269 uword ikey, i, l = 0, n_key_bytes, old_n_elts, key_alloc_from_free_list = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700270
271 mhash_sanitize_hash_user (h);
272
273 if (mhash_key_vector_is_heap (h))
274 {
Dave Barachc3799992016-08-15 11:12:27 -0400275 mhash_string_key_t *sk;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700276 uword is_c_string = h->n_key_bytes == MHASH_C_STRING_KEY;
277 uword handle;
278
279 n_key_bytes = is_c_string ? (strlen (key) + 1) : vec_len (key);
Dave Barachc3799992016-08-15 11:12:27 -0400280 i =
281 heap_alloc (h->key_vector_or_heap, n_key_bytes + sizeof (sk[0]),
282 handle);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700283
284 sk = (void *) (h->key_vector_or_heap + i);
285 sk->heap_handle = handle;
286 sk->vec.len = n_key_bytes;
Dave Barach178cf492018-11-13 16:34:13 -0500287 clib_memcpy_fast (sk->vec.vector_data, key, n_key_bytes);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700288
289 /* Advance key past vector header. */
290 i += sizeof (sk[0]);
291 }
292 else
293 {
Dave Barachc3799992016-08-15 11:12:27 -0400294 key_alloc_from_free_list = (l =
295 vec_len (h->key_vector_free_indices)) > 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700296 if (key_alloc_from_free_list)
297 {
298 i = h->key_vector_free_indices[l - 1];
299 k = vec_elt_at_index (h->key_vector_or_heap, i);
Damjan Marion8bea5892022-04-04 22:40:45 +0200300 vec_set_len (h->key_vector_free_indices, l - 1);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700301 }
302 else
303 {
304 vec_add2 (h->key_vector_or_heap, k, h->n_key_bytes);
305 i = k - h->key_vector_or_heap;
306 }
307
308 n_key_bytes = h->n_key_bytes;
Dave Barach178cf492018-11-13 16:34:13 -0500309 clib_memcpy_fast (k, key, n_key_bytes);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700310 }
311 ikey = i;
312
313 old_n_elts = hash_elts (h->hash);
314 h->hash = _hash_set3 (h->hash, ikey, new_value, old_value);
315
316 /* If element already existed remove duplicate key. */
317 if (hash_elts (h->hash) == old_n_elts)
318 {
Dave Barachc3799992016-08-15 11:12:27 -0400319 hash_pair_t *p;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700320
321 /* Fetch old key for return value. */
322 p = hash_get_pair (h->hash, ikey);
323 ikey = p->key;
324
325 /* Remove duplicate key. */
326 if (mhash_key_vector_is_heap (h))
327 {
Dave Barachc3799992016-08-15 11:12:27 -0400328 mhash_string_key_t *sk;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700329 sk = (void *) (h->key_vector_or_heap + i - sizeof (sk[0]));
330 heap_dealloc (h->key_vector_or_heap, sk->heap_handle);
331 }
332 else
333 {
334 if (key_alloc_from_free_list)
335 {
Vladislav Grishenko70328922023-12-14 17:48:28 +0100336 vec_set_len (h->key_vector_free_indices, l);
337 h->key_vector_free_indices[l - 1] = i;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700338 }
339 else
Damjan Marion8bea5892022-04-04 22:40:45 +0200340 vec_dec_len (h->key_vector_or_heap, h->n_key_bytes);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700341 }
342 }
343
344 return ikey;
345}
346
Damjan Mariondae1c7e2020-10-17 13:32:25 +0200347__clib_export uword
Dave Barachc3799992016-08-15 11:12:27 -0400348mhash_unset (mhash_t * h, void *key, uword * old_value)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700349{
Dave Barachc3799992016-08-15 11:12:27 -0400350 hash_pair_t *p;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700351 uword i;
352
353 mhash_sanitize_hash_user (h);
354 i = mhash_set_tmp_key (h, key);
355
356 p = hash_get_pair (h->hash, i);
Dave Barachc3799992016-08-15 11:12:27 -0400357 if (!p)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700358 return 0;
359
360 ASSERT (p->key != ~0);
361 i = p->key;
362
363 if (mhash_key_vector_is_heap (h))
364 {
Dave Barachc3799992016-08-15 11:12:27 -0400365 mhash_string_key_t *sk;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700366 sk = (void *) (h->key_vector_or_heap + i) - sizeof (sk[0]);
367 heap_dealloc (h->key_vector_or_heap, sk->heap_handle);
368 }
369 else
370 vec_add1 (h->key_vector_free_indices, i);
371
372 hash_unset3 (h->hash, i, old_value);
373 return 1;
374}
375
Vladislav Grishenko70328922023-12-14 17:48:28 +0100376__clib_export u8 *
377format_mhash_key (u8 *s, va_list *va)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700378{
Dave Barachc3799992016-08-15 11:12:27 -0400379 mhash_t *h = va_arg (*va, mhash_t *);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700380 u32 ki = va_arg (*va, u32);
Dave Barachc3799992016-08-15 11:12:27 -0400381 void *k = mhash_key_to_mem (h, ki);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700382
383 if (mhash_key_vector_is_heap (h))
384 {
385 uword is_c_string = h->n_key_bytes == MHASH_C_STRING_KEY;
386 u32 l = is_c_string ? strlen (k) : vec_len (k);
387 vec_add (s, k, l);
388 }
389 else if (h->format_key)
390 s = format (s, "%U", h->format_key, k);
391 else
Vladislav Grishenko70328922023-12-14 17:48:28 +0100392 s = format (s, "0x%U", format_hex_bytes, k, h->n_key_bytes);
393
394 return s;
395}
396
397static u8 *
398mhash_format_pair_default (u8 *s, va_list *args)
399{
400 void *CLIB_UNUSED (user_arg) = va_arg (*args, void *);
401 void *v = va_arg (*args, void *);
402 hash_pair_t *p = va_arg (*args, hash_pair_t *);
403 hash_t *h = hash_header (v);
404 mhash_t *mh = uword_to_pointer (h->user, mhash_t *);
405
406 s = format (s, "%U", format_mhash_key, mh, (u32) p->key);
407 if (hash_value_bytes (h) > 0)
408 s = format (s, " -> 0x%8U", format_hex_bytes, &p->value[0],
409 hash_value_bytes (h));
410 return s;
411}
412
413__clib_export u8 *
414format_mhash (u8 *s, va_list *va)
415{
416 mhash_t *h = va_arg (*va, mhash_t *);
417 int verbose = va_arg (*va, int);
418
419 s = format (s, "mhash %p, %wd elts, \n", h, mhash_elts (h));
420 if (mhash_key_vector_is_heap (h))
421 s = format (s, " %U", format_heap, h->key_vector_or_heap, verbose);
422 else
423 s = format (s, " keys %wd elts, %wd size, %wd free, %wd bytes used\n",
424 vec_len (h->key_vector_or_heap) / h->n_key_bytes,
425 h->n_key_bytes, vec_len (h->key_vector_free_indices),
426 vec_bytes (h->key_vector_or_heap) +
427 vec_bytes (h->key_vector_free_indices));
428 s = format (s, " %U", format_hash, h->hash, verbose);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700429
430 return s;
431}
Dave Barachc3799992016-08-15 11:12:27 -0400432
433/*
434 * fd.io coding-style-patch-verification: ON
435 *
436 * Local Variables:
437 * eval: (c-set-style "gnu")
438 * End:
439 */