From a40b421d39a64c285a4ac23304538690aaa90739 Mon Sep 17 00:00:00 2001 From: Garrett D'Amore Date: Sat, 4 Jan 2025 14:25:44 -0800 Subject: api: Promote idhash supplemental API to core --- docs/ref/api/id_map.md | 3 - docs/ref/migrate/nng1.md | 1 + include/nng/nng.h | 14 ++ include/nng/supplemental/util/idhash.h | 36 ---- src/core/CMakeLists.txt | 3 +- src/core/idhash_test.c | 355 ++++++++++++++++++++++++++++++++ src/nng.c | 55 +++++ src/supplemental/util/CMakeLists.txt | 4 +- src/supplemental/util/idhash.c | 68 ------- src/supplemental/util/idhash_test.c | 357 --------------------------------- 10 files changed, 428 insertions(+), 468 deletions(-) delete mode 100644 include/nng/supplemental/util/idhash.h create mode 100644 src/core/idhash_test.c delete mode 100644 src/supplemental/util/idhash.c delete mode 100644 src/supplemental/util/idhash_test.c diff --git a/docs/ref/api/id_map.md b/docs/ref/api/id_map.md index 0e5c1eac..fcbae848 100644 --- a/docs/ref/api/id_map.md +++ b/docs/ref/api/id_map.md @@ -9,9 +9,6 @@ include file to list of includes. ## ID Map Structure ```c -#include -#include - typedef struct nng_id_map_s nng_id_map; ``` diff --git a/docs/ref/migrate/nng1.md b/docs/ref/migrate/nng1.md index e6d6d820..cfb40a90 100644 --- a/docs/ref/migrate/nng1.md +++ b/docs/ref/migrate/nng1.md @@ -41,6 +41,7 @@ Simply remove any references to them. - `nng/protocol/survey0/respond.h` - `nng/protocol/survey0/survey.h` - `nng/supplemental/tls/tls.h` +- `nng/supplemental/util/idhash.h` - `nng/supplemental/util/platform.h` - `nng/transport/inproc/inproc.h` - `nng/transport/ipc/ipc.h` diff --git a/include/nng/nng.h b/include/nng/nng.h index 43824d61..980e527a 100644 --- a/include/nng/nng.h +++ b/include/nng/nng.h @@ -1604,6 +1604,20 @@ NNG_DECL const char *nng_tls_engine_description(void); // nng_tls_engine_fips_mode returns true if the engine is in FIPS 140 mode. NNG_DECL bool nng_tls_engine_fips_mode(void); +// Public ID map support. +typedef struct nng_id_map_s nng_id_map; + +#define NNG_MAP_RANDOM 1 + +NNG_DECL int nng_id_map_alloc( + nng_id_map **map, uint64_t lo, uint64_t hi, int flags); +NNG_DECL void nng_id_map_free(nng_id_map *map); +NNG_DECL void *nng_id_get(nng_id_map *, uint64_t); +NNG_DECL int nng_id_set(nng_id_map *, uint64_t, void *); +NNG_DECL int nng_id_alloc(nng_id_map *, uint64_t *, void *); +NNG_DECL int nng_id_remove(nng_id_map *, uint64_t); +NNG_DECL bool nng_id_visit(nng_id_map *, uint64_t *, void **, uint32_t *); + // Protocol specific values. These were formerly located in protocol specific // headers, but we are bringing them here for ease of use. diff --git a/include/nng/supplemental/util/idhash.h b/include/nng/supplemental/util/idhash.h deleted file mode 100644 index f231a554..00000000 --- a/include/nng/supplemental/util/idhash.h +++ /dev/null @@ -1,36 +0,0 @@ -// -// Copyright 2024 Staysail Systems, Inc. -// -// This software is supplied under the terms of the MIT License, a -// copy of which should be located in the distribution where this -// file was obtained (LICENSE.txt). A copy of the license may also be -// found online at https://opensource.org/licenses/MIT. -// - -#ifndef NNG_SUPPLEMENTAL_UTIL_IDHASH_H -#define NNG_SUPPLEMENTAL_UTIL_IDHASH_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -typedef struct nng_id_map_s nng_id_map; - -#define NNG_MAP_RANDOM 1 - -NNG_DECL int nng_id_map_alloc( - nng_id_map **map, uint64_t lo, uint64_t hi, int flags); -NNG_DECL void nng_id_map_free(nng_id_map *map); -NNG_DECL void *nng_id_get(nng_id_map *, uint64_t); -NNG_DECL int nng_id_set(nng_id_map *, uint64_t, void *); -NNG_DECL int nng_id_alloc(nng_id_map *, uint64_t *, void *); -NNG_DECL int nng_id_remove(nng_id_map *, uint64_t); -NNG_DECL bool nng_id_visit(nng_id_map *, uint64_t *, void **, uint32_t *); - -#ifdef __cplusplus -} -#endif - -#endif // NNG_SUPPLEMENTAL_IDHASH_IDHASH_H diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index ba8b1ca8..9f5b4a95 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2024 Staysail Systems, Inc. +# Copyright 2025 Staysail Systems, Inc. # # This software is supplied under the terms of the MIT License, a # copy of which should be located in the distribution where this @@ -84,6 +84,7 @@ nng_test(aio_test) nng_test(buf_size_test) nng_test(errors_test) nng_test(id_test) +nng_test(idhash_test) nng_test(init_test) nng_test(list_test) nng_test(log_test) diff --git a/src/core/idhash_test.c b/src/core/idhash_test.c new file mode 100644 index 00000000..2bf65dae --- /dev/null +++ b/src/core/idhash_test.c @@ -0,0 +1,355 @@ +// +// Copyright 2025 Staysail Systems, Inc. +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#include + +void +test_id_basic(void) +{ + nng_id_map *m; + char *five = "five"; + char *four = "four"; + + NUTS_PASS(nng_id_map_alloc(&m, 0, 0, 0)); + + // insert it + NUTS_PASS(nng_id_set(m, 5, five)); + // retrieve it + NUTS_TRUE(nng_id_get(m, 5) == five); + + // change it + NUTS_PASS(nng_id_set(m, 5, four)); + NUTS_TRUE(nng_id_get(m, 5) == four); + + // delete + NUTS_PASS(nng_id_remove(m, 5)); + + nng_id_map_free(m); +} + +void +test_id_random(void) +{ + int i; + uint64_t id; + for (i = 0; i < 2; i++) { + nng_id_map *m; + NUTS_PASS(nng_id_map_alloc(&m, 0, 0, NNG_MAP_RANDOM)); + NUTS_PASS(nng_id_alloc(m, &id, &id)); + nng_id_map_free(m); + NUTS_TRUE(id != 0); + if (id != 1) { + break; + } + // one chance in 4 billion, but try again + } + + NUTS_TRUE(id != 1); + NUTS_TRUE(i < 2); +} + +void +test_id_collision(void) +{ + nng_id_map *m; + char *five = "five"; + char *four = "four"; + + NUTS_PASS(nng_id_map_alloc(&m, 0, 0, 0)); + + // Carefully crafted -- 13 % 8 == 5. + NUTS_PASS(nng_id_set(m, 5, five)); + NUTS_PASS(nng_id_set(m, 13, four)); + NUTS_TRUE(nng_id_get(m, 5) == five); + NUTS_TRUE(nng_id_get(m, 13) == four); + + // Delete the intermediate + NUTS_PASS(nng_id_remove(m, 5)); + NUTS_TRUE(nng_id_get(m, 13) == four); + + nng_id_map_free(m); +} + +void +test_id_empty(void) +{ + nng_id_map *m; + + NUTS_PASS(nng_id_map_alloc(&m, 0, 0, 0)); + + NUTS_TRUE(nng_id_get(m, 42) == NULL); + NUTS_FAIL(nng_id_remove(m, 42), NNG_ENOENT); + NUTS_FAIL(nng_id_remove(m, 1), NNG_ENOENT); + nng_id_map_free(m); +} + +void +test_id_not_found(void) +{ + nng_id_map *m; + uint64_t id; + + NUTS_PASS(nng_id_map_alloc(&m, 0, 0, 0)); + + NUTS_PASS(nng_id_alloc(m, &id, &id)); + NUTS_FAIL(nng_id_remove(m, 42), NNG_ENOENT); + NUTS_FAIL(nng_id_remove(m, 2), NNG_ENOENT); + NUTS_PASS(nng_id_remove(m, id)); + nng_id_map_free(m); +} + +void +test_id_resize(void) +{ + nng_id_map *m; + int rv; + int i; + int expect[1024]; + + for (i = 0; i < 1024; i++) { + expect[i] = i; + } + + NUTS_PASS(nng_id_map_alloc(&m, 0, 0, 0)); + + for (i = 0; i < 1024; i++) { + if ((rv = nng_id_set(m, i, &expect[i])) != 0) { + NUTS_PASS(rv); + } + } + + for (i = 0; i < 1024; i++) { + if ((rv = nng_id_remove(m, i)) != 0) { + NUTS_PASS(rv); + } + } + nng_id_map_free(m); +} + +void +test_id_dynamic(void) +{ + nng_id_map *m; + int expect[5]; + uint64_t id; + + NUTS_PASS(nng_id_map_alloc(&m, 10, 13, 0)); + + // We can fill the table. + NUTS_PASS(nng_id_alloc(m, &id, &expect[0])); + NUTS_TRUE(id == 10); + NUTS_PASS(nng_id_alloc(m, &id, &expect[1])); + NUTS_TRUE(id == 11); + NUTS_PASS(nng_id_alloc(m, &id, &expect[2])); + NUTS_TRUE(id == 12); + NUTS_PASS(nng_id_alloc(m, &id, &expect[3])); + NUTS_TRUE(id == 13); + + // Adding another fails. + NUTS_FAIL(nng_id_alloc(m, &id, &expect[4]), NNG_ENOMEM); + + // Delete one. + NUTS_PASS(nng_id_remove(m, 11)); + + // And now we can allocate one. + NUTS_PASS(nng_id_alloc(m, &id, &expect[4])); + NUTS_TRUE(id == 11); + nng_id_map_free(m); +} + +void +test_id_set_out_of_range(void) +{ + nng_id_map *m; + int x; + uint64_t id; + + NUTS_PASS(nng_id_map_alloc(&m, 10, 13, 0)); + + // We can insert outside the range forcibly. + NUTS_PASS(nng_id_set(m, 1, &x)); + NUTS_PASS(nng_id_set(m, 100, &x)); + NUTS_PASS(nng_id_alloc(m, &id, &x)); + NUTS_TRUE(id == 10); + nng_id_map_free(m); +} + +void +test_id_visit(void) +{ + nng_id_map *m; + int x, y; + uint64_t id1; + uint64_t id2; + int *v1; + int *v2; + uint32_t cursor = 0; + + NUTS_PASS(nng_id_map_alloc(&m, 10, 13, 0)); + + // We can insert outside the range forcibly. + NUTS_PASS(nng_id_set(m, 1, &x)); + NUTS_PASS(nng_id_set(m, 100, &y)); + NUTS_TRUE(nng_id_visit(m, &id1, (void **) &v1, &cursor)); + NUTS_ASSERT(id1 == 1 || id1 == 100); + NUTS_ASSERT(v1 == &x || v1 == &y); + NUTS_TRUE(nng_id_visit(m, &id2, (void **) &v2, &cursor)); + NUTS_ASSERT(id2 == 1 || id2 == 100); + NUTS_ASSERT(v2 == &x || v2 == &y); + NUTS_ASSERT(id1 != id2); + NUTS_ASSERT(v1 != v2); + NUTS_TRUE(!nng_id_visit(m, &id2, (void **) &v2, &cursor)); + nng_id_map_free(m); +} + +void +test_id_visit_out_of_range(void) +{ + nng_id_map *m; + int x, y; + uint64_t id1; + int *v1; + uint32_t cursor = 1000; + + NUTS_PASS(nng_id_map_alloc(&m, 10, 13, 0)); + + // We can insert outside the range forcibly. + NUTS_PASS(nng_id_set(m, 1, &x)); + NUTS_PASS(nng_id_set(m, 100, &y)); + NUTS_TRUE(!nng_id_visit(m, &id1, (void **) &v1, &cursor)); + nng_id_map_free(m); +} + +#define STRESS_LOAD 50000 +#define NUM_VALUES 1000 + +void +test_id_stress(void) +{ + void *values[NUM_VALUES]; + nng_id_map *m; + size_t i; + int rv; + void *x; + int v; + + NUTS_PASS(nng_id_map_alloc(&m, 0, 0, 0)); + for (i = 0; i < NUM_VALUES; i++) { + values[i] = NULL; + } + + for (i = 0; i < STRESS_LOAD; i++) { + v = rand() % NUM_VALUES; // Keep it constrained + + switch (rand() & 3) { + case 0: + x = &values[rand() % NUM_VALUES]; + values[v] = x; + if ((rv = nng_id_set(m, v, x)) != 0) { + NUTS_PASS(rv); + goto out; + } + break; + + case 1: + rv = nng_id_remove(m, v); + if (values[v] == NULL) { + if (rv != NNG_ENOENT) { + NUTS_FAIL(rv, NNG_ENOENT); + goto out; + } + } else { + values[v] = NULL; + if (rv != 0) { + NUTS_PASS(rv); + goto out; + } + } + break; + case 2: + x = nng_id_get(m, v); + if (x != values[v]) { + NUTS_TRUE(x == values[v]); + goto out; + } + break; + } + } +out: + NUTS_TRUE(i == STRESS_LOAD); + + // Post stress check. + for (i = 0; i < NUM_VALUES; i++) { + x = nng_id_get(m, (uint32_t) i); + if (x != values[i]) { + NUTS_TRUE(x == values[i]); + break; + } + + // We only use the test macros if we know they are going + // to fail. Otherwise, there will be too many errors reported. + rv = nng_id_remove(m, (uint32_t) i); + if ((x == NULL) && (rv != NNG_ENOENT)) { + NUTS_FAIL(rv, NNG_ENOENT); + } else if ((x != NULL) && (rv != 0)) { + NUTS_PASS(rv); + } + } + NUTS_TRUE(i == NUM_VALUES); + + nng_id_map_free(m); +} + +void +test_id_alloc_long_long(void) +{ +#define TEST_IDS 100 + nng_id_map *m; + int x; + uint64_t ids[TEST_IDS]; + + NUTS_PASS(nng_id_map_alloc(&m, 1ULL << 32, (int64_t) -1, 0)); + + // We can insert outside the range forcibly - making sure we are + // choosing numbers above 64 bits. + for (int i = 0; i < TEST_IDS; i++) { + NUTS_PASS(nng_id_alloc(m, &ids[i], &x)); + NUTS_ASSERT(ids[i] > 0xFFFFFFFFULL); + } + for (int i = 0; i < TEST_IDS; i++) { + bool matched = false; + for (int j = 0; j < i; j++) { + // only dump the assertion on failure + // otherwise it is too noisy + if (ids[i] == ids[j]) { + matched = true; + break; + } + } + NUTS_ASSERT(!matched); + } + nng_id_map_free(m); +#undef TEST_IDS +} + +NUTS_TESTS = { + { "id basic", test_id_basic }, + { "id random", test_id_random }, + { "id collision", test_id_collision }, + { "id empty", test_id_empty }, + { "not found", test_id_not_found }, + { "id resize", test_id_resize }, + { "id dynamic", test_id_dynamic }, + { "id set out of range", test_id_set_out_of_range }, + { "id visit", test_id_visit }, + { "id visit out of range", test_id_visit_out_of_range }, + { "id stress", test_id_stress }, + { "id alloc long long", test_id_alloc_long_long }, + { NULL, NULL }, +}; diff --git a/src/nng.c b/src/nng.c index d51d781c..345f47d9 100644 --- a/src/nng.c +++ b/src/nng.c @@ -2179,3 +2179,58 @@ nng_udp_multicast_membership(nng_udp *udp, nng_sockaddr *sa, bool join) return ( nni_plat_udp_multicast_membership((nni_plat_udp *) udp, sa, join)); } + +struct nng_id_map_s { + nni_id_map m; +}; + +int +nng_id_map_alloc(nng_id_map **map, uint64_t lo, uint64_t hi, int flags) +{ + nng_id_map *m; + + if ((m = NNI_ALLOC_STRUCT(m)) == NULL) { + return (NNG_ENOMEM); + } + nni_id_map_init( + &m->m, lo, hi, (flags & NNG_MAP_RANDOM) ? true : false); + *map = m; + return (0); +} + +void +nng_id_map_free(nng_id_map *map) +{ + nni_id_map_fini(&map->m); + NNI_FREE_STRUCT(map); +} + +void * +nng_id_get(nng_id_map *map, uint64_t id) +{ + return (nni_id_get(&map->m, id)); +} + +int +nng_id_set(nng_id_map *map, uint64_t id, void *val) +{ + return (nni_id_set(&map->m, id, val)); +} + +int +nng_id_remove(nng_id_map *map, uint64_t id) +{ + return (nni_id_remove(&map->m, id)); +} + +int +nng_id_alloc(nng_id_map *map, uint64_t *id, void *val) +{ + return (nni_id_alloc(&map->m, id, val)); +} + +bool +nng_id_visit(nng_id_map *map, uint64_t *id, void **valp, uint32_t *cursor) +{ + return (nni_id_visit(&map->m, id, valp, cursor)); +} diff --git a/src/supplemental/util/CMakeLists.txt b/src/supplemental/util/CMakeLists.txt index 69afd9e0..d7541363 100644 --- a/src/supplemental/util/CMakeLists.txt +++ b/src/supplemental/util/CMakeLists.txt @@ -7,9 +7,7 @@ # found online at https://opensource.org/licenses/MIT. # -nng_sources(idhash.c options.c) +nng_sources(options.c) nng_headers( - nng/supplemental/util/idhash.h nng/supplemental/util/options.h) -nng_test(idhash_test) nng_test(options_test) diff --git a/src/supplemental/util/idhash.c b/src/supplemental/util/idhash.c deleted file mode 100644 index 4ededa31..00000000 --- a/src/supplemental/util/idhash.c +++ /dev/null @@ -1,68 +0,0 @@ -// -// Copyright 2024 Staysail Systems, Inc. -// -// This software is supplied under the terms of the MIT License, a -// copy of which should be located in the distribution where this -// file was obtained (LICENSE.txt). A copy of the license may also be -// found online at https://opensource.org/licenses/MIT. -// - -#include -#include - -#include "core/nng_impl.h" - -struct nng_id_map_s { - nni_id_map m; -}; - -int -nng_id_map_alloc(nng_id_map **map, uint64_t lo, uint64_t hi, int flags) -{ - nng_id_map *m; - - if ((m = NNI_ALLOC_STRUCT(m)) == NULL) { - return (NNG_ENOMEM); - } - nni_id_map_init( - &m->m, lo, hi, (flags & NNG_MAP_RANDOM) ? true : false); - *map = m; - return (0); -} - -void -nng_id_map_free(nng_id_map *map) -{ - nni_id_map_fini(&map->m); - NNI_FREE_STRUCT(map); -} - -void * -nng_id_get(nng_id_map *map, uint64_t id) -{ - return (nni_id_get(&map->m, id)); -} - -int -nng_id_set(nng_id_map *map, uint64_t id, void *val) -{ - return (nni_id_set(&map->m, id, val)); -} - -int -nng_id_remove(nng_id_map *map, uint64_t id) -{ - return (nni_id_remove(&map->m, id)); -} - -int -nng_id_alloc(nng_id_map *map, uint64_t *id, void *val) -{ - return (nni_id_alloc(&map->m, id, val)); -} - -bool -nng_id_visit(nng_id_map *map, uint64_t *id, void **valp, uint32_t *cursor) -{ - return (nni_id_visit(&map->m, id, valp, cursor)); -} diff --git a/src/supplemental/util/idhash_test.c b/src/supplemental/util/idhash_test.c deleted file mode 100644 index e0d472a0..00000000 --- a/src/supplemental/util/idhash_test.c +++ /dev/null @@ -1,357 +0,0 @@ -// -// Copyright 2024 Staysail Systems, Inc. -// -// This software is supplied under the terms of the MIT License, a -// copy of which should be located in the distribution where this -// file was obtained (LICENSE.txt). A copy of the license may also be -// found online at https://opensource.org/licenses/MIT. -// - -#include - -#include - -void -test_id_basic(void) -{ - nng_id_map *m; - char *five = "five"; - char *four = "four"; - - NUTS_PASS(nng_id_map_alloc(&m, 0, 0, 0)); - - // insert it - NUTS_PASS(nng_id_set(m, 5, five)); - // retrieve it - NUTS_TRUE(nng_id_get(m, 5) == five); - - // change it - NUTS_PASS(nng_id_set(m, 5, four)); - NUTS_TRUE(nng_id_get(m, 5) == four); - - // delete - NUTS_PASS(nng_id_remove(m, 5)); - - nng_id_map_free(m); -} - -void -test_id_random(void) -{ - int i; - uint64_t id; - for (i = 0; i < 2; i++) { - nng_id_map *m; - NUTS_PASS(nng_id_map_alloc(&m, 0, 0, NNG_MAP_RANDOM)); - NUTS_PASS(nng_id_alloc(m, &id, &id)); - nng_id_map_free(m); - NUTS_TRUE(id != 0); - if (id != 1) { - break; - } - // one chance in 4 billion, but try again - } - - NUTS_TRUE(id != 1); - NUTS_TRUE(i < 2); -} - -void -test_id_collision(void) -{ - nng_id_map *m; - char *five = "five"; - char *four = "four"; - - NUTS_PASS(nng_id_map_alloc(&m, 0, 0, 0)); - - // Carefully crafted -- 13 % 8 == 5. - NUTS_PASS(nng_id_set(m, 5, five)); - NUTS_PASS(nng_id_set(m, 13, four)); - NUTS_TRUE(nng_id_get(m, 5) == five); - NUTS_TRUE(nng_id_get(m, 13) == four); - - // Delete the intermediate - NUTS_PASS(nng_id_remove(m, 5)); - NUTS_TRUE(nng_id_get(m, 13) == four); - - nng_id_map_free(m); -} - -void -test_id_empty(void) -{ - nng_id_map *m; - - NUTS_PASS(nng_id_map_alloc(&m, 0, 0, 0)); - - NUTS_TRUE(nng_id_get(m, 42) == NULL); - NUTS_FAIL(nng_id_remove(m, 42), NNG_ENOENT); - NUTS_FAIL(nng_id_remove(m, 1), NNG_ENOENT); - nng_id_map_free(m); -} - -void -test_id_not_found(void) -{ - nng_id_map *m; - uint64_t id; - - NUTS_PASS(nng_id_map_alloc(&m, 0, 0, 0)); - - NUTS_PASS(nng_id_alloc(m, &id, &id)); - NUTS_FAIL(nng_id_remove(m, 42), NNG_ENOENT); - NUTS_FAIL(nng_id_remove(m, 2), NNG_ENOENT); - NUTS_PASS(nng_id_remove(m, id)); - nng_id_map_free(m); -} - -void -test_id_resize(void) -{ - nng_id_map *m; - int rv; - int i; - int expect[1024]; - - for (i = 0; i < 1024; i++) { - expect[i] = i; - } - - NUTS_PASS(nng_id_map_alloc(&m, 0, 0, 0)); - - for (i = 0; i < 1024; i++) { - if ((rv = nng_id_set(m, i, &expect[i])) != 0) { - NUTS_PASS(rv); - } - } - - for (i = 0; i < 1024; i++) { - if ((rv = nng_id_remove(m, i)) != 0) { - NUTS_PASS(rv); - } - } - nng_id_map_free(m); -} - -void -test_id_dynamic(void) -{ - nng_id_map *m; - int expect[5]; - uint64_t id; - - NUTS_PASS(nng_id_map_alloc(&m, 10, 13, 0)); - - // We can fill the table. - NUTS_PASS(nng_id_alloc(m, &id, &expect[0])); - NUTS_TRUE(id == 10); - NUTS_PASS(nng_id_alloc(m, &id, &expect[1])); - NUTS_TRUE(id == 11); - NUTS_PASS(nng_id_alloc(m, &id, &expect[2])); - NUTS_TRUE(id == 12); - NUTS_PASS(nng_id_alloc(m, &id, &expect[3])); - NUTS_TRUE(id == 13); - - // Adding another fails. - NUTS_FAIL(nng_id_alloc(m, &id, &expect[4]), NNG_ENOMEM); - - // Delete one. - NUTS_PASS(nng_id_remove(m, 11)); - - // And now we can allocate one. - NUTS_PASS(nng_id_alloc(m, &id, &expect[4])); - NUTS_TRUE(id == 11); - nng_id_map_free(m); -} - -void -test_id_set_out_of_range(void) -{ - nng_id_map *m; - int x; - uint64_t id; - - NUTS_PASS(nng_id_map_alloc(&m, 10, 13, 0)); - - // We can insert outside the range forcibly. - NUTS_PASS(nng_id_set(m, 1, &x)); - NUTS_PASS(nng_id_set(m, 100, &x)); - NUTS_PASS(nng_id_alloc(m, &id, &x)); - NUTS_TRUE(id == 10); - nng_id_map_free(m); -} - -void -test_id_visit(void) -{ - nng_id_map *m; - int x, y; - uint64_t id1; - uint64_t id2; - int *v1; - int *v2; - uint32_t cursor = 0; - - NUTS_PASS(nng_id_map_alloc(&m, 10, 13, 0)); - - // We can insert outside the range forcibly. - NUTS_PASS(nng_id_set(m, 1, &x)); - NUTS_PASS(nng_id_set(m, 100, &y)); - NUTS_TRUE(nng_id_visit(m, &id1, (void **) &v1, &cursor)); - NUTS_ASSERT(id1 == 1 || id1 == 100); - NUTS_ASSERT(v1 == &x || v1 == &y); - NUTS_TRUE(nng_id_visit(m, &id2, (void **) &v2, &cursor)); - NUTS_ASSERT(id2 == 1 || id2 == 100); - NUTS_ASSERT(v2 == &x || v2 == &y); - NUTS_ASSERT(id1 != id2); - NUTS_ASSERT(v1 != v2); - NUTS_TRUE(!nng_id_visit(m, &id2, (void **) &v2, &cursor)); - nng_id_map_free(m); -} - -void -test_id_visit_out_of_range(void) -{ - nng_id_map *m; - int x, y; - uint64_t id1; - int *v1; - uint32_t cursor = 1000; - - NUTS_PASS(nng_id_map_alloc(&m, 10, 13, 0)); - - // We can insert outside the range forcibly. - NUTS_PASS(nng_id_set(m, 1, &x)); - NUTS_PASS(nng_id_set(m, 100, &y)); - NUTS_TRUE(!nng_id_visit(m, &id1, (void **) &v1, &cursor)); - nng_id_map_free(m); -} - -#define STRESS_LOAD 50000 -#define NUM_VALUES 1000 - -void -test_id_stress(void) -{ - void *values[NUM_VALUES]; - nng_id_map *m; - size_t i; - int rv; - void *x; - int v; - - NUTS_PASS(nng_id_map_alloc(&m, 0, 0, 0)); - for (i = 0; i < NUM_VALUES; i++) { - values[i] = NULL; - } - - for (i = 0; i < STRESS_LOAD; i++) { - v = rand() % NUM_VALUES; // Keep it constrained - - switch (rand() & 3) { - case 0: - x = &values[rand() % NUM_VALUES]; - values[v] = x; - if ((rv = nng_id_set(m, v, x)) != 0) { - NUTS_PASS(rv); - goto out; - } - break; - - case 1: - rv = nng_id_remove(m, v); - if (values[v] == NULL) { - if (rv != NNG_ENOENT) { - NUTS_FAIL(rv, NNG_ENOENT); - goto out; - } - } else { - values[v] = NULL; - if (rv != 0) { - NUTS_PASS(rv); - goto out; - } - } - break; - case 2: - x = nng_id_get(m, v); - if (x != values[v]) { - NUTS_TRUE(x == values[v]); - goto out; - } - break; - } - } -out: - NUTS_TRUE(i == STRESS_LOAD); - - // Post stress check. - for (i = 0; i < NUM_VALUES; i++) { - x = nng_id_get(m, (uint32_t) i); - if (x != values[i]) { - NUTS_TRUE(x == values[i]); - break; - } - - // We only use the test macros if we know they are going - // to fail. Otherwise, there will be too many errors reported. - rv = nng_id_remove(m, (uint32_t) i); - if ((x == NULL) && (rv != NNG_ENOENT)) { - NUTS_FAIL(rv, NNG_ENOENT); - } else if ((x != NULL) && (rv != 0)) { - NUTS_PASS(rv); - } - } - NUTS_TRUE(i == NUM_VALUES); - - nng_id_map_free(m); -} - -void -test_id_alloc_long_long(void) -{ -#define TEST_IDS 100 - nng_id_map *m; - int x; - uint64_t ids[TEST_IDS]; - - NUTS_PASS(nng_id_map_alloc(&m, 1ULL << 32, (int64_t) -1, 0)); - - // We can insert outside the range forcibly - making sure we are - // choosing numbers above 64 bits. - for (int i = 0; i < TEST_IDS; i++) { - NUTS_PASS(nng_id_alloc(m, &ids[i], &x)); - NUTS_ASSERT(ids[i] > 0xFFFFFFFFULL); - } - for (int i = 0; i < TEST_IDS; i++) { - bool matched = false; - for (int j = 0; j < i; j++) { - // only dump the assertion on failure - // otherwise it is too noisy - if (ids[i] == ids[j]) { - matched = true; - break; - } - } - NUTS_ASSERT(!matched); - } - nng_id_map_free(m); -#undef TEST_IDS -} - -NUTS_TESTS = { - { "id basic", test_id_basic }, - { "id random", test_id_random }, - { "id collision", test_id_collision }, - { "id empty", test_id_empty }, - { "not found", test_id_not_found }, - { "id resize", test_id_resize }, - { "id dynamic", test_id_dynamic }, - { "id set out of range", test_id_set_out_of_range }, - { "id visit", test_id_visit }, - { "id visit out of range", test_id_visit_out_of_range }, - { "id stress", test_id_stress }, - { "id alloc long long", test_id_alloc_long_long }, - { NULL, NULL }, -}; -- cgit v1.2.3-70-g09d2