| /* |
| * Copyright (c) 2015 Cisco and/or its affiliates. |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at: |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| /* |
| Copyright (c) 2001, 2002, 2003 Eliot Dresselhaus |
| |
| Permission is hereby granted, free of charge, to any person obtaining |
| a copy of this software and associated documentation files (the |
| "Software"), to deal in the Software without restriction, including |
| without limitation the rights to use, copy, modify, merge, publish, |
| distribute, sublicense, and/or sell copies of the Software, and to |
| permit persons to whom the Software is furnished to do so, subject to |
| the following conditions: |
| |
| The above copyright notice and this permission notice shall be |
| included in all copies or substantial portions of the Software. |
| |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
| LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
| OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
| WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| |
| #ifdef CLIB_LINUX_KERNEL |
| #include <linux/unistd.h> |
| #endif |
| |
| #ifdef CLIB_UNIX |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <vppinfra/time.h> |
| #endif |
| |
| #include <vppinfra/random.h> |
| #include <vppinfra/mem.h> |
| #include <vppinfra/hash.h> |
| #include <vppinfra/error.h> |
| #include <vppinfra/format.h> |
| #include <vppinfra/bitmap.h> |
| |
| static int verbose; |
| #define if_verbose(format,args...) \ |
| if (verbose) { clib_warning(format, ## args); } |
| |
| typedef struct |
| { |
| int n_iterations; |
| |
| int n_iterations_per_print; |
| |
| /* Number of pairs to insert into hash. */ |
| int n_pairs; |
| |
| /* True to validate correctness of hash functions. */ |
| int n_iterations_per_validate; |
| |
| /* Non-zero if hash table size is to be fixed. */ |
| int fixed_hash_size; |
| |
| /* Verbosity level for hash formats. */ |
| int verbose; |
| |
| /* Random number seed. */ |
| u32 seed; |
| } hash_test_t; |
| |
| static clib_error_t * |
| hash_next_test (word * h) |
| { |
| hash_next_t hn = { 0 }; |
| hash_pair_t *p0, *p1; |
| clib_error_t *error = 0; |
| |
| hash_foreach_pair (p0, h, { |
| p1 = hash_next (h, &hn); |
| error = CLIB_ERROR_ASSERT (p0 == p1); |
| if (error) |
| break; |
| }); |
| |
| if (!error) |
| error = CLIB_ERROR_ASSERT (!hash_next (h, &hn)); |
| |
| return error; |
| } |
| |
| static u8 * |
| test1_format (u8 * s, va_list * args) |
| { |
| void *CLIB_UNUSED (user_arg) = va_arg (*args, void *); |
| void *v = va_arg (*args, void *); |
| hash_pair_t *p = va_arg (*args, hash_pair_t *); |
| hash_t *h = hash_header (v); |
| |
| return format (s, "0x%8U -> 0x%8U", |
| format_hex_bytes, &p->key, sizeof (p->key), |
| format_hex_bytes, &p->value[0], hash_value_bytes (h)); |
| } |
| |
| static clib_error_t * |
| test_word_key (hash_test_t * ht) |
| { |
| word *h = 0; |
| word i, j; |
| |
| word *keys = 0, *vals = 0; |
| uword *is_inserted = 0; |
| |
| clib_error_t *error = 0; |
| |
| vec_resize (keys, ht->n_pairs); |
| vec_resize (vals, vec_len (keys)); |
| |
| h = hash_create (ht->fixed_hash_size, sizeof (vals[0])); |
| |
| hash_set_pair_format (h, test1_format, 0); |
| if (ht->fixed_hash_size) |
| hash_set_flags (h, HASH_FLAG_NO_AUTO_GROW | HASH_FLAG_NO_AUTO_SHRINK); |
| |
| { |
| uword *unique = 0; |
| u32 k; |
| |
| for (i = 0; i < vec_len (keys); i++) |
| { |
| do |
| { |
| k = random_u32 (&ht->seed) & 0xfffff; |
| } |
| while (clib_bitmap_get (unique, k)); |
| unique = clib_bitmap_ori (unique, k); |
| keys[i] = k; |
| vals[i] = i; |
| } |
| |
| clib_bitmap_free (unique); |
| } |
| |
| for (i = 0; i < ht->n_iterations; i++) |
| { |
| u32 vi = random_u32 (&ht->seed) % vec_len (keys); |
| |
| if (clib_bitmap_get (is_inserted, vi)) |
| hash_unset (h, keys[vi]); |
| else |
| hash_set (h, keys[vi], vals[vi]); |
| |
| is_inserted = clib_bitmap_xori (is_inserted, vi); |
| |
| if (ht->n_iterations_per_print > 0 |
| && ((i + 1) % ht->n_iterations_per_print) == 0) |
| if_verbose ("iteration %d\n %U", i + 1, format_hash, h, ht->verbose); |
| |
| if (ht->n_iterations_per_validate == 0 |
| || (i + 1) % ht->n_iterations_per_validate) |
| continue; |
| |
| { |
| hash_pair_t *p; |
| uword ki; |
| |
| hash_foreach_pair (p, h, { |
| ki = p->value[0]; |
| ASSERT (keys[ki] == p->key); |
| }); |
| } |
| |
| if ((error = hash_validate (h))) |
| goto done; |
| |
| for (j = 0; j < vec_len (keys); j++) |
| { |
| uword *v; |
| v = hash_get (h, keys[j]); |
| if ((error = |
| CLIB_ERROR_ASSERT (clib_bitmap_get (is_inserted, j) == |
| (v != 0)))) |
| goto done; |
| if (v) |
| { |
| if ((error = CLIB_ERROR_ASSERT (v[0] == vals[j]))) |
| goto done; |
| } |
| } |
| } |
| |
| if ((error = hash_next_test (h))) |
| goto done; |
| |
| if_verbose ("%U", format_hash, h, ht->verbose); |
| |
| for (i = 0; i < vec_len (keys); i++) |
| { |
| if (!clib_bitmap_get (is_inserted, i)) |
| continue; |
| |
| hash_unset (h, keys[i]); |
| is_inserted = clib_bitmap_xori (is_inserted, i); |
| |
| if (ht->n_iterations_per_validate == 0 |
| || (i + 1) % ht->n_iterations_per_validate) |
| continue; |
| |
| if ((error = hash_validate (h))) |
| goto done; |
| |
| for (j = 0; j < vec_len (keys); j++) |
| { |
| uword *v; |
| v = hash_get (h, keys[j]); |
| if ((error = |
| CLIB_ERROR_ASSERT (clib_bitmap_get (is_inserted, j) == |
| (v != 0)))) |
| goto done; |
| if (v) |
| { |
| if ((error = CLIB_ERROR_ASSERT (v[0] == vals[j]))) |
| goto done; |
| } |
| } |
| } |
| |
| done: |
| hash_free (h); |
| vec_free (keys); |
| vec_free (vals); |
| clib_bitmap_free (is_inserted); |
| |
| if (verbose) |
| fformat (stderr, "%U\n", format_clib_mem_usage, /* verbose */ 0); |
| |
| return error; |
| } |
| |
| static u8 * |
| test2_format (u8 * s, va_list * args) |
| { |
| void *CLIB_UNUSED (user_arg) = va_arg (*args, void *); |
| void *v = va_arg (*args, void *); |
| hash_pair_t *p = va_arg (*args, hash_pair_t *); |
| hash_t *h = hash_header (v); |
| |
| return format (s, "0x%8U <- %v", |
| format_hex_bytes, &p->value[0], hash_value_bytes (h), |
| p->key); |
| } |
| |
| static clib_error_t * |
| test_string_key (hash_test_t * ht) |
| { |
| word i, j; |
| |
| u8 **keys = 0; |
| word *vals = 0; |
| uword *is_inserted = 0; |
| |
| word *h = 0; |
| |
| clib_error_t *error = 0; |
| |
| vec_resize (keys, ht->n_pairs); |
| vec_resize (vals, vec_len (keys)); |
| |
| h = |
| hash_create_vec (ht->fixed_hash_size, sizeof (keys[0][0]), |
| sizeof (uword)); |
| hash_set_pair_format (h, test2_format, 0); |
| if (ht->fixed_hash_size) |
| hash_set_flags (h, HASH_FLAG_NO_AUTO_SHRINK | HASH_FLAG_NO_AUTO_GROW); |
| |
| for (i = 0; i < vec_len (keys); i++) |
| { |
| keys[i] = random_string (&ht->seed, 5 + (random_u32 (&ht->seed) & 0xf)); |
| keys[i] = format (keys[i], "%x", i); |
| vals[i] = random_u32 (&ht->seed); |
| } |
| |
| for (i = 0; i < ht->n_iterations; i++) |
| { |
| u32 vi = random_u32 (&ht->seed) % vec_len (keys); |
| |
| if (clib_bitmap_get (is_inserted, vi)) |
| hash_unset_mem (h, keys[vi]); |
| else |
| hash_set_mem (h, keys[vi], vals[vi]); |
| |
| is_inserted = clib_bitmap_xori (is_inserted, vi); |
| |
| if (ht->n_iterations_per_print > 0 |
| && ((i + 1) % ht->n_iterations_per_print) == 0) |
| if_verbose ("iteration %d\n %U", i + 1, format_hash, h, ht->verbose); |
| |
| if (ht->n_iterations_per_validate == 0 |
| || (i + 1) % ht->n_iterations_per_validate) |
| continue; |
| |
| if ((error = hash_validate (h))) |
| goto done; |
| |
| for (j = 0; j < vec_len (keys); j++) |
| { |
| uword *v; |
| v = hash_get_mem (h, keys[j]); |
| if ((error = |
| CLIB_ERROR_ASSERT (clib_bitmap_get (is_inserted, j) == |
| (v != 0)))) |
| goto done; |
| if (v) |
| { |
| if ((error = CLIB_ERROR_ASSERT (v[0] == vals[j]))) |
| goto done; |
| } |
| } |
| } |
| |
| if ((error = hash_next_test (h))) |
| goto done; |
| |
| if_verbose ("%U", format_hash, h, ht->verbose); |
| |
| for (i = 0; i < vec_len (keys); i++) |
| { |
| if (!clib_bitmap_get (is_inserted, i)) |
| continue; |
| |
| hash_unset_mem (h, keys[i]); |
| is_inserted = clib_bitmap_xori (is_inserted, i); |
| |
| if (ht->n_iterations_per_validate == 0 |
| || (i + 1) % ht->n_iterations_per_validate) |
| continue; |
| |
| if ((error = hash_validate (h))) |
| goto done; |
| |
| for (j = 0; j < vec_len (keys); j++) |
| { |
| uword *v; |
| v = hash_get_mem (h, keys[j]); |
| if ((error = |
| CLIB_ERROR_ASSERT (clib_bitmap_get (is_inserted, j) == |
| (v != 0)))) |
| goto done; |
| if (v) |
| { |
| if ((error = CLIB_ERROR_ASSERT (v[0] == vals[j]))) |
| goto done; |
| } |
| } |
| } |
| |
| done: |
| hash_free (h); |
| vec_free (vals); |
| clib_bitmap_free (is_inserted); |
| |
| for (i = 0; i < vec_len (keys); i++) |
| vec_free (keys[i]); |
| vec_free (keys); |
| |
| if (verbose) |
| fformat (stderr, "%U\n", format_clib_mem_usage, /* verbose */ 0); |
| |
| return error; |
| } |
| |
| int |
| test_hash_main (unformat_input_t * input) |
| { |
| hash_test_t _ht = { 0 }, *ht = &_ht; |
| clib_error_t *error; |
| |
| ht->n_iterations = 100; |
| ht->n_pairs = 10; |
| ht->fixed_hash_size = 0; /* zero means non-fixed size */ |
| |
| while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) |
| { |
| if (0 == unformat (input, "iter %d", &ht->n_iterations) |
| && 0 == unformat (input, "print %d", &ht->n_iterations_per_print) |
| && 0 == unformat (input, "elts %d", &ht->n_pairs) |
| && 0 == unformat (input, "size %d", &ht->fixed_hash_size) |
| && 0 == unformat (input, "seed %d", &ht->seed) |
| && 0 == unformat (input, "verbose %=", &ht->verbose, 1) |
| && 0 == unformat (input, "valid %d", |
| &ht->n_iterations_per_validate)) |
| { |
| clib_warning ("unknown input `%U'", format_unformat_error, input); |
| return 1; |
| } |
| } |
| |
| if (!ht->seed) |
| ht->seed = random_default_seed (); |
| |
| if_verbose ("testing %d iterations, seed %d", ht->n_iterations, ht->seed); |
| |
| error = test_word_key (ht); |
| if (error) |
| clib_error_report (error); |
| |
| error = test_string_key (ht); |
| if (error) |
| clib_error_report (error); |
| |
| return 0; |
| } |
| |
| #ifdef CLIB_UNIX |
| int |
| main (int argc, char *argv[]) |
| { |
| unformat_input_t i; |
| int ret; |
| |
| clib_mem_init (0, 3ULL << 30); |
| |
| verbose = (argc > 1); |
| unformat_init_command_line (&i, argv); |
| ret = test_hash_main (&i); |
| unformat_free (&i); |
| |
| return ret; |
| } |
| #endif /* CLIB_UNIX */ |
| |
| /* |
| * fd.io coding-style-patch-verification: ON |
| * |
| * Local Variables: |
| * eval: (c-set-style "gnu") |
| * End: |
| */ |