From a3ed0ef9c4ef1c12afffd6b6cd19cc1eb62711a2 Mon Sep 17 00:00:00 2001 From: Neil Henning Date: Sun, 19 Mar 2023 20:07:20 +0000 Subject: [PATCH] Cleanup the testing, the readme, and use a different mix function in the hashmap. --- README.md | 51 +++++--- hashmap.h | 21 ++-- test/main.c | 46 ------- test/test.c | 251 +------------------------------------ test/test.cpp | 225 +-------------------------------- test/test.inc | 312 ++++++++++++++++++++++++++++++++++++++++++++++ test/test11.cpp | 234 +--------------------------------- test/test_sse42.c | 211 +------------------------------ 8 files changed, 365 insertions(+), 986 deletions(-) create mode 100644 test/test.inc diff --git a/README.md b/README.md index 8aff36c..ec7c9b2 100644 --- a/README.md +++ b/README.md @@ -16,17 +16,9 @@ The current supported platforms are Linux, macOS and Windows. ### Fundamental Design -The hashmap is made to work with UTF-8 string slices - sections of strings that -are passed with a pointer and an explicit length. The reason for this design -choice was that the hashmap is being used, by the author, to map symbols that -are already resident in memory from a source file of a programming language. To -save from causing millions of additional allocations to move these UTF-8 string -slices into null-terminated strings, an explicit length is always passed. - -Note also that while the API passes char* pointers as the key - these keys are -never used with the C API string functions. Instead `memcmp` is used to compare -keys. This allows us to use UTF-8 strings in place of regular ASCII strings with -no additional code. +The hashmap is made to work with any arbitrary data keys - you just provide a +pointer and size, and it'll hash that data. Comparison is done using `memcmp`, +so zeroing out padding data is advised in structs. ### Create a Hashmap @@ -41,8 +33,26 @@ if (0 != hashmap_create(initial_size, &hashmap)) { ``` The `initial_size` parameter only sets the initial size of the hashmap - which -can grow if multiple keys hit the same hash entry. The initial size must be a -power of two, and creation will fail if it is not. +can grow if multiple keys hit the same hash entry. The size of the hashmap is +rounded up to the nearest power of two above the provided `initial_size`. + +There is also an extended creation function `hashmap_create_ex`: + +```c +struct hashmap_s hashmap; +struct hashmap_create_options_s options; +memset(&options, 0, sizeof(options)); + +// You can set a custom hasher that the hashmap should use. +options.hasher = &my_hasher; + +// You can also specify the initial capacity of the hashmap. +options.initial_capacity = 42; + +if (0 != hashmap_create_ex(options, &hashmap)) { + // error! +} +``` ### Put Something in a Hashmap @@ -169,6 +179,15 @@ To get the number of entries that have been put into a hashmap use the unsigned num_entries = hashmap_num_entries(&hashmap); ``` +### Get the Capcity of a Hashmap + +To get the actual number of buckets allocated in the hashmap (the capacity) use +the `hashmap_capacity` function: + +```c +unsigned num_entries = hashmap_capacity(&hashmap); +``` + ### Destroy a Hashmap To destroy a hashmap when you are finished with it use the `hashmap_destroy` @@ -189,10 +208,10 @@ by Elliott Back. The authors have applied the following further changes: external projects). - Used an explicitly public domain license for the code - the [unlicense](https://unlicense.org/). -- Changed the API to take string slices (pointer & length) instead of null - terminated strings. +- Changed the API to take arbitrary data pointers and length (it was originally + solely for UTF-8 string slices). - Did a pass to clean up the comments and function signatures. -- Added second iterator, tests and documentation. (Samuel D. Crow) +- Added second iterator, tests and documentation. (Samuel D. Crow) ## License diff --git a/hashmap.h b/hashmap.h index feb0d25..792132b 100644 --- a/hashmap.h +++ b/hashmap.h @@ -270,8 +270,8 @@ HASHMAP_ALWAYS_INLINE hashmap_uint32_t hashmap_clz(const hashmap_uint32_t x); #define HASHMAP_PTR_CAST(type, x) reinterpret_cast(x) #define HASHMAP_NULL NULL #else -#define HASHMAP_CAST(type, x) ((type)x) -#define HASHMAP_PTR_CAST(type, x) ((type)x) +#define HASHMAP_CAST(type, x) ((type)(x)) +#define HASHMAP_PTR_CAST(type, x) ((type)(x)) #define HASHMAP_NULL 0 #endif @@ -569,15 +569,14 @@ hashmap_uint32_t hashmap_crc32_hasher(const hashmap_uint32_t seed, } #endif - /* Robert Jenkins' 32 bit Mix Function */ - crc32val += (crc32val << 12); - crc32val ^= (crc32val >> 22); - crc32val += (crc32val << 4); - crc32val ^= (crc32val >> 9); - crc32val += (crc32val << 10); - crc32val ^= (crc32val >> 2); - crc32val += (crc32val << 7); - crc32val ^= (crc32val >> 12); + // Use the mix function from murmur3. + crc32val ^= len; + + crc32val ^= crc32val >> 16; + crc32val *= 0x85ebca6b; + crc32val ^= crc32val >> 13; + crc32val *= 0xc2b2ae35; + crc32val ^= crc32val >> 16; return crc32val; } diff --git a/test/main.c b/test/main.c index c93a958..f6c50cc 100644 --- a/test/main.c +++ b/test/main.c @@ -24,51 +24,5 @@ // For more information, please refer to #include "utest.h" -#include "hashmap.h" UTEST_MAIN() - -UTEST(main, one_byte) { - unsigned char data[256]; - int i; - struct hashmap_s hashmap; - - for (i = 0; i < 256; i++) { - data[i] = (unsigned char)i; - } - - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - - for (i = 0; i < 256; i++) { - ASSERT_EQ(0, hashmap_put(&hashmap, &data[i], 1, NULL)); - } - - ASSERT_EQ(hashmap_num_entries(&hashmap), 256u); - ASSERT_LE(hashmap_capacity(&hashmap), 2048u); - - hashmap_destroy(&hashmap); -} - -UTEST(main, two_bytes) { - unsigned short *data; - int i; - struct hashmap_s hashmap; - - data = (unsigned short *)malloc(sizeof(unsigned short) * 16384); - - for (i = 0; i < 16384; i++) { - data[i] = (unsigned short)i; - } - - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - - for (i = 0; i < 16384; i++) { - ASSERT_EQ(0, hashmap_put(&hashmap, &data[i], 2, NULL)); - } - - ASSERT_EQ(hashmap_num_entries(&hashmap), 16384u); - ASSERT_LE(hashmap_capacity(&hashmap), 65536u); - - hashmap_destroy(&hashmap); - free(data); -} diff --git a/test/test.c b/test/test.c index 91d05aa..5440670 100644 --- a/test/test.c +++ b/test/test.c @@ -1,253 +1,6 @@ #include "hashmap.h" #include "utest.h" -UTEST(c, create) { - struct hashmap_s hashmap; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - hashmap_destroy(&hashmap); -} +#define MY_TEST_WRAPPER(name) UTEST(c, name) -UTEST(c, create_zero) { - struct hashmap_s hashmap; - ASSERT_EQ(0, hashmap_create(0, &hashmap)); - ASSERT_LT(0u, hashmap_capacity(&hashmap)); - hashmap_destroy(&hashmap); -} - -UTEST(c, create_not_power_of_two) { - struct hashmap_s hashmap; - ASSERT_EQ(0, hashmap_create(3, &hashmap)); - ASSERT_LE(3u, hashmap_capacity(&hashmap)); - hashmap_destroy(&hashmap); -} - -static int set_context(void *const context, void *const element) { - *(int *)context = *(int *)element; - return 1; -} - -UTEST(c, put) { - struct hashmap_s hashmap; - int x = 42; - int y = 13; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - ASSERT_EQ(0, hashmap_put(&hashmap, "foo", (unsigned)strlen("foo"), &x)); - ASSERT_EQ(0, hashmap_iterate(&hashmap, set_context, &y)); - ASSERT_EQ(x, y); - hashmap_destroy(&hashmap); -} - -UTEST(c, put_twice) { - struct hashmap_s hashmap; - int x = 42; - int y = 13; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - ASSERT_EQ(0, hashmap_put(&hashmap, "foo", (unsigned)strlen("foo"), &x)); - ASSERT_EQ(0, hashmap_put(&hashmap, "foo", (unsigned)strlen("foo"), &y)); - ASSERT_EQ(hashmap_num_entries(&hashmap), 1u); - hashmap_destroy(&hashmap); -} - -UTEST(c, get_exists) { - struct hashmap_s hashmap; - int x = 42; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - ASSERT_EQ(0, hashmap_put(&hashmap, "foo", (unsigned)strlen("foo"), &x)); - ASSERT_EQ(&x, hashmap_get(&hashmap, "foo", (unsigned)strlen("foo"))); - hashmap_destroy(&hashmap); -} - -UTEST(c, get_does_not_exists) { - struct hashmap_s hashmap; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - ASSERT_EQ(NULL, hashmap_get(&hashmap, "foo", (unsigned)strlen("foo"))); - hashmap_destroy(&hashmap); -} - -UTEST(c, remove) { - struct hashmap_s hashmap; - int x = 42; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - ASSERT_EQ(0, hashmap_put(&hashmap, "foo", (unsigned)strlen("foo"), &x)); - ASSERT_EQ(0, hashmap_remove(&hashmap, "foo", (unsigned)strlen("foo"))); - hashmap_destroy(&hashmap); -} - -UTEST(c, remove_and_return_key) { - /* The '&bar' portion of the string just uniques the constant from the 'foo' - * used later. */ - const char *const key = "foo&bar"; - - struct hashmap_s hashmap; - int x = 42; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - ASSERT_EQ(0, hashmap_put(&hashmap, key, 3, &x)); - - /* Use a new string here so that we definitely have a different pointer key - * being provided. */ - ASSERT_EQ(key, hashmap_remove_and_return_key(&hashmap, "foo", - (unsigned)strlen("foo"))); - hashmap_destroy(&hashmap); -} - -static int early_exit(void *const context, void *const element) { - *(int *)context += 1; - *(int *)element += 1; - return 0; -} - -UTEST(c, iterate_early_exit) { - struct hashmap_s hashmap; - int x[27] = {0}; - int total = 0; - char s[27]; - char c; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - - for (c = 'a'; c <= 'z'; c++) { - s[c - 'a'] = c; - } - - for (c = 'a'; c <= 'z'; c++) { - const int index = c - 'a'; - ASSERT_EQ(0, hashmap_put(&hashmap, s + index, 1, x + index)); - } - - ASSERT_EQ(1, hashmap_iterate(&hashmap, early_exit, &total)); - ASSERT_EQ(1, total); - - total = 0; - - for (c = 'a'; c <= 'z'; c++) { - const int index = c - 'a'; - ASSERT_GE(1, x[index]); - if (x[index]) { - total += 1; - } - } - - ASSERT_EQ(1, total); - - hashmap_destroy(&hashmap); -} - -static int all(void *const context, void *const element) { - *(int *)context += 1; - *(int *)element += 1; - return 1; -} - -UTEST(c, iterate_all) { - struct hashmap_s hashmap; - int x[27] = {0}; - int total = 0; - char s[27]; - char c; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - - for (c = 'a'; c <= 'z'; c++) { - s[c - 'a'] = c; - } - - for (c = 'a'; c <= 'z'; c++) { - const int index = c - 'a'; - ASSERT_EQ(0, hashmap_put(&hashmap, s + index, 1, x + index)); - } - - ASSERT_EQ(0, hashmap_iterate(&hashmap, all, &total)); - ASSERT_EQ(26, total); - - for (c = 'a'; c <= 'z'; c++) { - const int index = c - 'a'; - ASSERT_EQ(1, x[index]); - } - - hashmap_destroy(&hashmap); -} - -UTEST(c, num_entries) { - struct hashmap_s hashmap; - int x = 42; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - ASSERT_EQ(0u, hashmap_num_entries(&hashmap)); - ASSERT_EQ(0, hashmap_put(&hashmap, "foo", (unsigned)strlen("foo"), &x)); - ASSERT_EQ(1u, hashmap_num_entries(&hashmap)); - ASSERT_EQ(0, hashmap_remove(&hashmap, "foo", (unsigned)strlen("foo"))); - ASSERT_EQ(0u, hashmap_num_entries(&hashmap)); - hashmap_destroy(&hashmap); -} - -static int rem_all(void *context, struct hashmap_element_s *e) { - (*(int *)context) += e->key_len; - return -1; -} - -UTEST(c, remove_all) { - struct hashmap_s hashmap; - int x[27] = {0}; - int total = 0; - char s[27]; - char c; - - ASSERT_EQ(0, hashmap_create(16, &hashmap)); - - for (c = 'a'; c <= 'z'; c++) { - s[c - 'a'] = c; - } - - for (c = 'a'; c <= 'z'; c++) { - const int index = c - 'a'; - ASSERT_EQ(0, hashmap_put(&hashmap, s + index, 1, x + index)); - } - ASSERT_EQ(26u, hashmap_num_entries(&hashmap)); - ASSERT_EQ(0, hashmap_iterate_pairs(&hashmap, rem_all, &total)); - ASSERT_EQ(26, total); - ASSERT_EQ(0u, hashmap_num_entries(&hashmap)); - hashmap_destroy(&hashmap); -} - -UTEST(c, hash_conflict) { - struct hashmap_s hashmap; - - int x = 42; - int y = 13; - int z = -53; - - ASSERT_EQ(0, hashmap_create(4, &hashmap)); - - // These all hash to the same value. - ASSERT_EQ(0, hashmap_put(&hashmap, "000", 3, &x)); - ASSERT_EQ(0, hashmap_put(&hashmap, "002", 3, &y)); - ASSERT_EQ(0, hashmap_put(&hashmap, "007", 3, &z)); - ASSERT_EQ(3u, hashmap_num_entries(&hashmap)); - - // Now we remove the middle value. - ASSERT_EQ(0, hashmap_remove(&hashmap, "002", 3)); - ASSERT_EQ(2u, hashmap_num_entries(&hashmap)); - - // And now attempt to insert the last value again. There was a bug where this - // would insert a new entry incorrectly instead of resolving to the previous - // entry. - ASSERT_EQ(0, hashmap_put(&hashmap, "007", 3, &z)); - ASSERT_EQ(2u, hashmap_num_entries(&hashmap)); - - hashmap_destroy(&hashmap); -} - -UTEST(c, issue_20) { - struct hashmap_s hashmap; - const char *key = "192.168.2.2hv_api.udache.com/abc/def"; - unsigned int len = (unsigned int)strlen(key); - - char *value = "1"; - char *ptr = NULL; - - hashmap_create(1024, &hashmap); - hashmap_put(&hashmap, key, len, value); - - ptr = (char *)hashmap_get(&hashmap, key, len); - - ASSERT_EQ(value, ptr); - - hashmap_destroy(&hashmap); -} +#include "test.inc" diff --git a/test/test.cpp b/test/test.cpp index ef7f21f..b345a57 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -1,227 +1,6 @@ #include "hashmap.h" #include "utest.h" -#if defined(_MSC_VER) -#define NOTHROW __declspec(nothrow) -#else -#define NOTHROW -#endif +#define MY_TEST_WRAPPER(name) UTEST(cpp, name) -UTEST(cpp, create) { - struct hashmap_s hashmap; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - hashmap_destroy(&hashmap); -} - -UTEST(cpp, create_zero) { - struct hashmap_s hashmap; - ASSERT_EQ(0, hashmap_create(0, &hashmap)); - ASSERT_LT(0u, hashmap_capacity(&hashmap)); - hashmap_destroy(&hashmap); -} - -UTEST(cpp, create_not_power_of_two) { - struct hashmap_s hashmap; - ASSERT_EQ(0, hashmap_create(3, &hashmap)); - ASSERT_LE(3u, hashmap_capacity(&hashmap)); - hashmap_destroy(&hashmap); -} - -static int NOTHROW set_context(void *const context, void *const element) { - *static_cast(context) = *static_cast(element); - return 1; -} - -UTEST(cpp, put) { - struct hashmap_s hashmap; - int x = 42; - int y = 13; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - ASSERT_EQ(0, hashmap_put(&hashmap, "foo", - static_cast(strlen("foo")), &x)); - ASSERT_EQ(0, hashmap_iterate(&hashmap, set_context, &y)); - ASSERT_EQ(x, y); - hashmap_destroy(&hashmap); -} - -UTEST(cpp, get_exists) { - struct hashmap_s hashmap; - int x = 42; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - ASSERT_EQ(0, hashmap_put(&hashmap, "foo", - static_cast(strlen("foo")), &x)); - ASSERT_EQ(&x, static_cast(hashmap_get( - &hashmap, "foo", static_cast(strlen("foo"))))); - hashmap_destroy(&hashmap); -} - -UTEST(cpp, get_does_not_exists) { - struct hashmap_s hashmap; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - ASSERT_FALSE( - hashmap_get(&hashmap, "foo", static_cast(strlen("foo")))); - hashmap_destroy(&hashmap); -} - -UTEST(cpp, remove) { - struct hashmap_s hashmap; - int x = 42; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - ASSERT_EQ(0, hashmap_put(&hashmap, "foo", - static_cast(strlen("foo")), &x)); - ASSERT_EQ( - 0, hashmap_remove(&hashmap, "foo", static_cast(strlen("foo")))); - hashmap_destroy(&hashmap); -} - -UTEST(cpp, remove_and_return_key) { - /* The '&bar' portion of the string just uniques the constant from the 'foo' - * used later. */ - const char *const key = "foo&bar"; - - struct hashmap_s hashmap; - int x = 42; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - ASSERT_EQ(0, hashmap_put(&hashmap, key, 3, &x)); - - /* Use a new string here so that we definitely have a different pointer key - * being provided. */ - ASSERT_EQ(key, static_cast(hashmap_remove_and_return_key( - &hashmap, "foo", static_cast(strlen("foo"))))); - hashmap_destroy(&hashmap); -} - -static int NOTHROW early_exit(void *const context, void *const element) { - *static_cast(context) += 1; - *static_cast(element) += 1; - return 0; -} - -UTEST(cpp, iterate_early_exit) { - struct hashmap_s hashmap; - int x[27] = {0}; - int total = 0; - char s[27]; - char c; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - - for (c = 'a'; c <= 'z'; c++) { - s[c - 'a'] = c; - } - - for (c = 'a'; c <= 'z'; c++) { - const int index = c - 'a'; - ASSERT_EQ(0, hashmap_put(&hashmap, s + index, 1, x + index)); - } - - ASSERT_EQ(1, hashmap_iterate(&hashmap, early_exit, &total)); - ASSERT_EQ(1, total); - - total = 0; - - for (c = 'a'; c <= 'z'; c++) { - const int index = c - 'a'; - ASSERT_GE(1, x[index]); - if (x[index]) { - total += 1; - } - } - - ASSERT_EQ(1, total); - - hashmap_destroy(&hashmap); -} - -static int NOTHROW all(void *const context, void *const element) { - *static_cast(context) += 1; - *static_cast(element) += 1; - return 1; -} - -UTEST(cpp, iterate_all) { - struct hashmap_s hashmap; - int x[27] = {0}; - int total = 0; - char s[27]; - char c; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - - for (c = 'a'; c <= 'z'; c++) { - s[c - 'a'] = c; - } - - for (c = 'a'; c <= 'z'; c++) { - const int index = c - 'a'; - ASSERT_EQ(0, hashmap_put(&hashmap, s + index, 1, x + index)); - } - - ASSERT_EQ(0, hashmap_iterate(&hashmap, all, &total)); - ASSERT_EQ(26, total); - - for (c = 'a'; c <= 'z'; c++) { - const int index = c - 'a'; - ASSERT_EQ(1, x[index]); - } - - hashmap_destroy(&hashmap); -} - -UTEST(cpp, num_entries) { - struct hashmap_s hashmap; - int x = 42; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - ASSERT_EQ(0u, hashmap_num_entries(&hashmap)); - ASSERT_EQ(0, hashmap_put(&hashmap, "foo", - static_cast(strlen("foo")), &x)); - ASSERT_EQ(1u, hashmap_num_entries(&hashmap)); - ASSERT_EQ( - 0, hashmap_remove(&hashmap, "foo", static_cast(strlen("foo")))); - ASSERT_EQ(0u, hashmap_num_entries(&hashmap)); - hashmap_destroy(&hashmap); -} - -UTEST(cpp, hash_conflict) { - struct hashmap_s hashmap; - - int x = 42; - int y = 13; - int z = -53; - - ASSERT_EQ(0, hashmap_create(4, &hashmap)); - - // These all hash to the same value. - ASSERT_EQ(0, hashmap_put(&hashmap, "000", 3, &x)); - ASSERT_EQ(0, hashmap_put(&hashmap, "002", 3, &y)); - ASSERT_EQ(0, hashmap_put(&hashmap, "007", 3, &z)); - ASSERT_EQ(3u, hashmap_num_entries(&hashmap)); - - // Now we remove the middle value. - ASSERT_EQ(0, hashmap_remove(&hashmap, "002", 3)); - ASSERT_EQ(2u, hashmap_num_entries(&hashmap)); - - // And now attempt to insert the last value again. There was a bug where this - // would insert a new entry incorrectly instead of resolving to the previous - // entry. - ASSERT_EQ(0, hashmap_put(&hashmap, "007", 3, &z)); - ASSERT_EQ(2u, hashmap_num_entries(&hashmap)); - - hashmap_destroy(&hashmap); -} - -UTEST(cpp, issue_20) { - struct hashmap_s hashmap; - const char *key = "192.168.2.2hv_api.udache.com/abc/def"; - unsigned int len = static_cast(strlen(key)); - - int value = 42; - int *ptr = UTEST_NULL; - - hashmap_create(1024, &hashmap); - hashmap_put(&hashmap, key, len, &value); - - ptr = static_cast(hashmap_get(&hashmap, key, len)); - - ASSERT_EQ(&value, ptr); - - hashmap_destroy(&hashmap); -} +#include "test.inc" diff --git a/test/test.inc b/test/test.inc new file mode 100644 index 0000000..d3076d2 --- /dev/null +++ b/test/test.inc @@ -0,0 +1,312 @@ +#if defined(__cplusplus) && (__cplusplus >= 201103L) && defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++98-compat" +#endif + +#if defined(__cplusplus) && defined(_MSC_VER) && (_MSC_VER < 1920) +#define NOTHROW __declspec(nothrow) +#define NOEXCEPT +#elif defined(__cplusplus) && (__cplusplus >= 201103L) +#define NOTHROW +#define NOEXCEPT noexcept +#else +#define NOTHROW +#define NOEXCEPT +#endif + +MY_TEST_WRAPPER(create) { + struct hashmap_s hashmap; + ASSERT_EQ(0, hashmap_create(1, &hashmap)); + hashmap_destroy(&hashmap); +} + +MY_TEST_WRAPPER(create_zero) { + struct hashmap_s hashmap; + ASSERT_EQ(0, hashmap_create(0, &hashmap)); + ASSERT_LT(0u, hashmap_capacity(&hashmap)); + hashmap_destroy(&hashmap); +} + +MY_TEST_WRAPPER(create_not_power_of_two) { + struct hashmap_s hashmap; + ASSERT_EQ(0, hashmap_create(3, &hashmap)); + ASSERT_LE(3u, hashmap_capacity(&hashmap)); + hashmap_destroy(&hashmap); +} + +static hashmap_uint32_t custom_hasher(const hashmap_uint32_t seed, + const void *const s, + const hashmap_uint32_t len) { + hashmap_uint64_t cs = 0; + memcpy(&cs, &s, sizeof(s)); + + return seed ^ HASHMAP_CAST(hashmap_uint32_t, cs >> 32) ^ + HASHMAP_CAST(hashmap_uint32_t, cs) ^ len; +} + +MY_TEST_WRAPPER(create_ex) { + int thing = 42; + struct hashmap_s hashmap; + struct hashmap_create_options_s options; + memset(&options, 0, sizeof(options)); + options.initial_capacity = 42; + options.hasher = &custom_hasher; + + ASSERT_EQ(0, hashmap_create_ex(options, &hashmap)); + ASSERT_LE(42u, hashmap_capacity(&hashmap)); + + ASSERT_EQ(0, hashmap_put(&hashmap, &thing, sizeof(thing), HASHMAP_NULL)); + + ASSERT_EQ(1u, hashmap_num_entries(&hashmap)); + + hashmap_destroy(&hashmap); +} + +static int NOTHROW set_context(void *const context, + void *const element) NOEXCEPT { + *HASHMAP_PTR_CAST(int *, context) = *HASHMAP_PTR_CAST(int *, element); + return 1; +} + +MY_TEST_WRAPPER(put) { + struct hashmap_s hashmap; + int x = 42; + int y = 13; + ASSERT_EQ(0, hashmap_create(1, &hashmap)); + ASSERT_EQ(0, hashmap_put(&hashmap, "foo", + HASHMAP_CAST(unsigned, strlen("foo")), &x)); + ASSERT_EQ(0, hashmap_iterate(&hashmap, set_context, &y)); + ASSERT_EQ(x, y); + hashmap_destroy(&hashmap); +} + +MY_TEST_WRAPPER(get_exists) { + struct hashmap_s hashmap; + int x = 42; + ASSERT_EQ(0, hashmap_create(1, &hashmap)); + ASSERT_EQ(0, hashmap_put(&hashmap, "foo", + HASHMAP_CAST(unsigned, strlen("foo")), &x)); + ASSERT_EQ(&x, HASHMAP_PTR_CAST( + int *, hashmap_get(&hashmap, "foo", + HASHMAP_CAST(unsigned, strlen("foo"))))); + hashmap_destroy(&hashmap); +} + +MY_TEST_WRAPPER(get_does_not_exists) { + struct hashmap_s hashmap; + ASSERT_EQ(0, hashmap_create(1, &hashmap)); + ASSERT_FALSE( + hashmap_get(&hashmap, "foo", HASHMAP_CAST(unsigned, strlen("foo")))); + hashmap_destroy(&hashmap); +} + +MY_TEST_WRAPPER(remove) { + struct hashmap_s hashmap; + int x = 42; + ASSERT_EQ(0, hashmap_create(1, &hashmap)); + ASSERT_EQ(0, hashmap_put(&hashmap, "foo", + HASHMAP_CAST(unsigned, strlen("foo")), &x)); + ASSERT_EQ(0, hashmap_remove(&hashmap, "foo", + HASHMAP_CAST(unsigned, strlen("foo")))); + hashmap_destroy(&hashmap); +} + +MY_TEST_WRAPPER(remove_and_return_key) { + /* The '&bar' portion of the string just uniques the constant from the 'foo' + * used later. */ + const char *const key = "foo&bar"; + + struct hashmap_s hashmap; + int x = 42; + ASSERT_EQ(0, hashmap_create(1, &hashmap)); + ASSERT_EQ(0, hashmap_put(&hashmap, key, 3, &x)); + + /* Use a new string here so that we definitely have a different pointer key + * being provided. */ + ASSERT_EQ(key, HASHMAP_PTR_CAST(const char *, + hashmap_remove_and_return_key( + &hashmap, "foo", + HASHMAP_CAST(unsigned, strlen("foo"))))); + hashmap_destroy(&hashmap); +} + +static int NOTHROW early_exit(void *const context, void *const element) { + *HASHMAP_PTR_CAST(int *, context) += 1; + *HASHMAP_PTR_CAST(int *, element) += 1; + return 0; +} + +MY_TEST_WRAPPER(iterate_early_exit) { + struct hashmap_s hashmap; + int x[27] = {0}; + int total = 0; + char s[27]; + char c; + ASSERT_EQ(0, hashmap_create(1, &hashmap)); + + for (c = 'a'; c <= 'z'; c++) { + s[c - 'a'] = c; + } + + for (c = 'a'; c <= 'z'; c++) { + const int index = c - 'a'; + ASSERT_EQ(0, hashmap_put(&hashmap, s + index, 1, x + index)); + } + + ASSERT_EQ(1, hashmap_iterate(&hashmap, early_exit, &total)); + ASSERT_EQ(1, total); + + total = 0; + + for (c = 'a'; c <= 'z'; c++) { + const int index = c - 'a'; + ASSERT_GE(1, x[index]); + if (x[index]) { + total += 1; + } + } + + ASSERT_EQ(1, total); + + hashmap_destroy(&hashmap); +} + +static int NOTHROW all(void *const context, void *const element) { + *HASHMAP_PTR_CAST(int *, context) += 1; + *HASHMAP_PTR_CAST(int *, element) += 1; + return 1; +} + +MY_TEST_WRAPPER(iterate_all) { + struct hashmap_s hashmap; + int x[27] = {0}; + int total = 0; + char s[27]; + char c; + ASSERT_EQ(0, hashmap_create(1, &hashmap)); + + for (c = 'a'; c <= 'z'; c++) { + s[c - 'a'] = c; + } + + for (c = 'a'; c <= 'z'; c++) { + const int index = c - 'a'; + ASSERT_EQ(0, hashmap_put(&hashmap, s + index, 1, x + index)); + } + + ASSERT_EQ(0, hashmap_iterate(&hashmap, all, &total)); + ASSERT_EQ(26, total); + + for (c = 'a'; c <= 'z'; c++) { + const int index = c - 'a'; + ASSERT_EQ(1, x[index]); + } + + hashmap_destroy(&hashmap); +} + +MY_TEST_WRAPPER(num_entries) { + struct hashmap_s hashmap; + int x = 42; + ASSERT_EQ(0, hashmap_create(1, &hashmap)); + ASSERT_EQ(0u, hashmap_num_entries(&hashmap)); + ASSERT_EQ(0, hashmap_put(&hashmap, "foo", + HASHMAP_CAST(unsigned, strlen("foo")), &x)); + ASSERT_EQ(1u, hashmap_num_entries(&hashmap)); + ASSERT_EQ(0, hashmap_remove(&hashmap, "foo", + HASHMAP_CAST(unsigned, strlen("foo")))); + ASSERT_EQ(0u, hashmap_num_entries(&hashmap)); + hashmap_destroy(&hashmap); +} + +MY_TEST_WRAPPER(hash_conflict) { + struct hashmap_s hashmap; + + int x = 42; + int y = 13; + int z = -53; + + ASSERT_EQ(0, hashmap_create(4, &hashmap)); + + // These all hash to the same value. + ASSERT_EQ(0, hashmap_put(&hashmap, "000", 3, &x)); + ASSERT_EQ(0, hashmap_put(&hashmap, "002", 3, &y)); + ASSERT_EQ(0, hashmap_put(&hashmap, "007", 3, &z)); + ASSERT_EQ(3u, hashmap_num_entries(&hashmap)); + + // Now we remove the middle value. + ASSERT_EQ(0, hashmap_remove(&hashmap, "002", 3)); + ASSERT_EQ(2u, hashmap_num_entries(&hashmap)); + + // And now attempt to insert the last value again. There was a bug where this + // would insert a new entry incorrectly instead of resolving to the previous + // entry. + ASSERT_EQ(0, hashmap_put(&hashmap, "007", 3, &z)); + ASSERT_EQ(2u, hashmap_num_entries(&hashmap)); + + hashmap_destroy(&hashmap); +} + +MY_TEST_WRAPPER(issue_20) { + struct hashmap_s hashmap; + const char *key = "192.168.2.2hv_api.udache.com/abc/def"; + unsigned int len = HASHMAP_CAST(unsigned, strlen(key)); + + int value = 42; + int *ptr = UTEST_NULL; + + hashmap_create(1024, &hashmap); + hashmap_put(&hashmap, key, len, &value); + + ptr = HASHMAP_PTR_CAST(int *, hashmap_get(&hashmap, key, len)); + + ASSERT_EQ(&value, ptr); + + hashmap_destroy(&hashmap); +} + +MY_TEST_WRAPPER(one_byte) { + unsigned char data[256]; + int i; + struct hashmap_s hashmap; + + for (i = 0; i < 256; i++) { + data[i] = HASHMAP_CAST(unsigned char, i); + } + + ASSERT_EQ(0, hashmap_create(1, &hashmap)); + + for (i = 0; i < 256; i++) { + ASSERT_EQ(0, hashmap_put(&hashmap, &data[i], 1, NULL)); + } + + ASSERT_EQ(hashmap_num_entries(&hashmap), 256u); + ASSERT_LE(hashmap_capacity(&hashmap), 1024u); + + hashmap_destroy(&hashmap); +} + +MY_TEST_WRAPPER(two_bytes) { + unsigned short *data; + int i; + struct hashmap_s hashmap; + + data = HASHMAP_PTR_CAST(unsigned short *, + malloc(sizeof(unsigned short) * 16384)); + + for (i = 0; i < 16384; i++) { + data[i] = HASHMAP_CAST(unsigned short, i); + } + + ASSERT_EQ(0, hashmap_create(1, &hashmap)); + + for (i = 0; i < 16384; i++) { + ASSERT_EQ(0, hashmap_put(&hashmap, &data[i], 2, NULL)); + } + + ASSERT_EQ(hashmap_num_entries(&hashmap), 16384u); + ASSERT_LE(hashmap_capacity(&hashmap), 65536u); + + hashmap_destroy(&hashmap); + free(data); +} diff --git a/test/test11.cpp b/test/test11.cpp index e80d945..3fbead5 100644 --- a/test/test11.cpp +++ b/test/test11.cpp @@ -1,236 +1,6 @@ #include "hashmap.h" #include "utest.h" -#if defined(_MSC_VER) && (_MSC_VER < 1920) -#define NOTHROW __declspec(nothrow) -#define NOEXCEPT -#else -#define NOTHROW -#define NOEXCEPT noexcept -#endif +#define MY_TEST_WRAPPER(name) UTEST(cpp11, name) -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wc++98-compat" -#endif - -UTEST(cpp11, create) { - struct hashmap_s hashmap; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - hashmap_destroy(&hashmap); -} - -UTEST(cpp11, create_zero) { - struct hashmap_s hashmap; - ASSERT_EQ(0, hashmap_create(0, &hashmap)); - ASSERT_LT(0u, hashmap_capacity(&hashmap)); - hashmap_destroy(&hashmap); -} - -UTEST(cpp11, create_not_power_of_two) { - struct hashmap_s hashmap; - ASSERT_EQ(0, hashmap_create(3, &hashmap)); - ASSERT_LE(3u, hashmap_capacity(&hashmap)); - hashmap_destroy(&hashmap); -} - -static int NOTHROW set_context(void *const context, - void *const element) NOEXCEPT { - *static_cast(context) = *static_cast(element); - return 1; -} - -UTEST(cpp11, put) { - struct hashmap_s hashmap; - int x = 42; - int y = 13; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - ASSERT_EQ(0, hashmap_put(&hashmap, "foo", - static_cast(strlen("foo")), &x)); - ASSERT_EQ(0, hashmap_iterate(&hashmap, set_context, &y)); - ASSERT_EQ(x, y); - hashmap_destroy(&hashmap); -} - -UTEST(cpp11, get_exists) { - struct hashmap_s hashmap; - int x = 42; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - ASSERT_EQ(0, hashmap_put(&hashmap, "foo", - static_cast(strlen("foo")), &x)); - ASSERT_EQ(&x, static_cast(hashmap_get( - &hashmap, "foo", static_cast(strlen("foo"))))); - hashmap_destroy(&hashmap); -} - -UTEST(cpp11, get_does_not_exists) { - struct hashmap_s hashmap; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - ASSERT_FALSE( - hashmap_get(&hashmap, "foo", static_cast(strlen("foo")))); - hashmap_destroy(&hashmap); -} - -UTEST(cpp11, remove) { - struct hashmap_s hashmap; - int x = 42; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - ASSERT_EQ(0, hashmap_put(&hashmap, "foo", - static_cast(strlen("foo")), &x)); - ASSERT_EQ( - 0, hashmap_remove(&hashmap, "foo", static_cast(strlen("foo")))); - hashmap_destroy(&hashmap); -} - -UTEST(cpp11, remove_and_return_key) { - /* The '&bar' portion of the string just uniques the constant from the 'foo' - * used later. */ - const char *const key = "foo&bar"; - - struct hashmap_s hashmap; - int x = 42; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - ASSERT_EQ(0, hashmap_put(&hashmap, key, 3, &x)); - - /* Use a new string here so that we definitely have a different pointer key - * being provided. */ - ASSERT_EQ(key, hashmap_remove_and_return_key( - &hashmap, "foo", static_cast(strlen("foo")))); - hashmap_destroy(&hashmap); -} - -static int NOTHROW early_exit(void *const context, - void *const element) NOEXCEPT { - *static_cast(context) += 1; - *static_cast(element) += 1; - return 0; -} - -UTEST(cpp11, iterate_early_exit) { - struct hashmap_s hashmap; - int x[27] = {0}; - int total = 0; - char s[27]; - char c; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - - for (c = 'a'; c <= 'z'; c++) { - s[c - 'a'] = c; - } - - for (c = 'a'; c <= 'z'; c++) { - const int index = c - 'a'; - ASSERT_EQ(0, hashmap_put(&hashmap, s + index, 1, x + index)); - } - - ASSERT_EQ(1, hashmap_iterate(&hashmap, early_exit, &total)); - ASSERT_EQ(1, total); - - total = 0; - - for (c = 'a'; c <= 'z'; c++) { - const int index = c - 'a'; - ASSERT_GE(1, x[index]); - if (x[index]) { - total += 1; - } - } - - ASSERT_EQ(1, total); - - hashmap_destroy(&hashmap); -} - -static int NOTHROW all(void *const context, void *const element) NOEXCEPT { - *static_cast(context) += 1; - *static_cast(element) += 1; - return 1; -} - -UTEST(cpp11, iterate_all) { - struct hashmap_s hashmap; - int x[27] = {0}; - int total = 0; - char s[27]; - char c; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - - for (c = 'a'; c <= 'z'; c++) { - s[c - 'a'] = c; - } - - for (c = 'a'; c <= 'z'; c++) { - const int index = c - 'a'; - ASSERT_EQ(0, hashmap_put(&hashmap, s + index, 1, x + index)); - } - - ASSERT_EQ(0, hashmap_iterate(&hashmap, all, &total)); - ASSERT_EQ(26, total); - - for (c = 'a'; c <= 'z'; c++) { - const int index = c - 'a'; - ASSERT_EQ(1, x[index]); - } - - hashmap_destroy(&hashmap); -} - -UTEST(cpp11, num_entries) { - struct hashmap_s hashmap; - int x = 42; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - ASSERT_EQ(0u, hashmap_num_entries(&hashmap)); - ASSERT_EQ(0, hashmap_put(&hashmap, "foo", - static_cast(strlen("foo")), &x)); - ASSERT_EQ(1u, hashmap_num_entries(&hashmap)); - ASSERT_EQ( - 0, hashmap_remove(&hashmap, "foo", static_cast(strlen("foo")))); - ASSERT_EQ(0u, hashmap_num_entries(&hashmap)); - hashmap_destroy(&hashmap); -} - -UTEST(cpp11, hash_conflict) { - struct hashmap_s hashmap; - - int x = 42; - int y = 13; - int z = -53; - - ASSERT_EQ(0, hashmap_create(4, &hashmap)); - - // These all hash to the same value. - ASSERT_EQ(0, hashmap_put(&hashmap, "000", 3, &x)); - ASSERT_EQ(0, hashmap_put(&hashmap, "002", 3, &y)); - ASSERT_EQ(0, hashmap_put(&hashmap, "007", 3, &z)); - ASSERT_EQ(3u, hashmap_num_entries(&hashmap)); - - // Now we remove the middle value. - ASSERT_EQ(0, hashmap_remove(&hashmap, "002", 3)); - ASSERT_EQ(2u, hashmap_num_entries(&hashmap)); - - // And now attempt to insert the last value again. There was a bug where this - // would insert a new entry incorrectly instead of resolving to the previous - // entry. - ASSERT_EQ(0, hashmap_put(&hashmap, "007", 3, &z)); - ASSERT_EQ(2u, hashmap_num_entries(&hashmap)); - - hashmap_destroy(&hashmap); -} - -UTEST(cpp11, issue_20) { - struct hashmap_s hashmap; - const char *key = "192.168.2.2hv_api.udache.com/abc/def"; - unsigned int len = static_cast(strlen(key)); - - int value = 42; - void *ptr = UTEST_NULL; - - hashmap_create(1024, &hashmap); - hashmap_put(&hashmap, key, len, &value); - - ptr = hashmap_get(&hashmap, key, len); - - ASSERT_EQ(&value, ptr); - - hashmap_destroy(&hashmap); -} +#include "test.inc" diff --git a/test/test_sse42.c b/test/test_sse42.c index 3a8d868..99c1c34 100644 --- a/test/test_sse42.c +++ b/test/test_sse42.c @@ -3,216 +3,9 @@ #if (defined(_MSC_VER) && defined(__AVX__)) || \ (!defined(_MSC_VER) && defined(__SSE4_2__)) -UTEST(c_sse42, create) { - struct hashmap_s hashmap; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - hashmap_destroy(&hashmap); -} -UTEST(c_sse42, create_zero) { - struct hashmap_s hashmap; - ASSERT_EQ(0, hashmap_create(0, &hashmap)); - ASSERT_LT(0u, hashmap_capacity(&hashmap)); - hashmap_destroy(&hashmap); -} +#define MY_TEST_WRAPPER(name) UTEST(c_sse2, name) -UTEST(c_sse42, create_not_power_of_two) { - struct hashmap_s hashmap; - ASSERT_EQ(0, hashmap_create(3, &hashmap)); - ASSERT_LE(3u, hashmap_capacity(&hashmap)); - hashmap_destroy(&hashmap); -} - -static int set_context(void *const context, void *const element) { - *(int *)context = *(int *)element; - return 1; -} - -UTEST(c_sse42, put) { - struct hashmap_s hashmap; - int x = 42; - int y = 13; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - ASSERT_EQ(0, hashmap_put(&hashmap, "foo", (unsigned)strlen("foo"), &x)); - ASSERT_EQ(0, hashmap_put(&hashmap, "bar", (unsigned)strlen("bar"), &x)); - ASSERT_EQ(0, hashmap_iterate(&hashmap, set_context, &y)); - ASSERT_EQ(x, y); - hashmap_destroy(&hashmap); -} - -UTEST(c_sse42, get_exists) { - struct hashmap_s hashmap; - int x = 42; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - ASSERT_EQ(0, hashmap_put(&hashmap, "foo", (unsigned)strlen("foo"), &x)); - ASSERT_EQ(&x, hashmap_get(&hashmap, "foo", (unsigned)strlen("foo"))); - hashmap_destroy(&hashmap); -} - -UTEST(c_sse42, get_does_not_exists) { - struct hashmap_s hashmap; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - ASSERT_EQ(NULL, hashmap_get(&hashmap, "foo", (unsigned)strlen("foo"))); - hashmap_destroy(&hashmap); -} - -UTEST(c_sse42, remove) { - struct hashmap_s hashmap; - int x = 42; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - ASSERT_EQ(0, hashmap_put(&hashmap, "foo", (unsigned)strlen("foo"), &x)); - ASSERT_EQ(0, hashmap_remove(&hashmap, "foo", (unsigned)strlen("foo"))); - hashmap_destroy(&hashmap); -} - -static int early_exit(void *const context, void *const element) { - *(int *)context += 1; - *(int *)element += 1; - return 0; -} - -UTEST(c_sse42, remove_and_return_key) { - /* The '&bar' portion of the string just uniques the constant from the 'foo' - * used later. */ - const char *const key = "foo&bar"; - - struct hashmap_s hashmap; - int x = 42; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - ASSERT_EQ(0, hashmap_put(&hashmap, key, 3, &x)); - - /* Use a new string here so that we definitely have a different pointer key - * being provided. */ - ASSERT_EQ(key, hashmap_remove_and_return_key(&hashmap, "foo", - (unsigned)strlen("foo"))); - hashmap_destroy(&hashmap); -} - -UTEST(c_sse42, iterate_early_exit) { - struct hashmap_s hashmap; - int x[27] = {0}; - int total = 0; - char s[27]; - char c; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - - for (c = 'a'; c <= 'z'; c++) { - s[c - 'a'] = c; - } - - for (c = 'a'; c <= 'z'; c++) { - const int index = c - 'a'; - ASSERT_EQ(0, hashmap_put(&hashmap, s + index, 1, x + index)); - } - - ASSERT_EQ(1, hashmap_iterate(&hashmap, early_exit, &total)); - ASSERT_EQ(1, total); - - total = 0; - - for (c = 'a'; c <= 'z'; c++) { - const int index = c - 'a'; - ASSERT_GE(1, x[index]); - if (x[index]) { - total += 1; - } - } - - ASSERT_EQ(1, total); - - hashmap_destroy(&hashmap); -} - -static int all(void *const context, void *const element) { - *(int *)context += 1; - *(int *)element += 1; - return 1; -} - -UTEST(c_sse42, iterate_all) { - struct hashmap_s hashmap; - int x[27] = {0}; - int total = 0; - char s[27]; - char c; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - - for (c = 'a'; c <= 'z'; c++) { - s[c - 'a'] = c; - } - - for (c = 'a'; c <= 'z'; c++) { - const int index = c - 'a'; - ASSERT_EQ(0, hashmap_put(&hashmap, s + index, 1, x + index)); - } - - ASSERT_EQ(0, hashmap_iterate(&hashmap, all, &total)); - ASSERT_EQ(26, total); - - for (c = 'a'; c <= 'z'; c++) { - const int index = c - 'a'; - ASSERT_EQ(1, x[index]); - } - - hashmap_destroy(&hashmap); -} - -UTEST(c_sse42, num_entries) { - struct hashmap_s hashmap; - int x = 42; - ASSERT_EQ(0, hashmap_create(1, &hashmap)); - ASSERT_EQ(0u, hashmap_num_entries(&hashmap)); - ASSERT_EQ(0, hashmap_put(&hashmap, "foo", (unsigned)strlen("foo"), &x)); - ASSERT_EQ(1u, hashmap_num_entries(&hashmap)); - ASSERT_EQ(0, hashmap_remove(&hashmap, "foo", (unsigned)strlen("foo"))); - ASSERT_EQ(0u, hashmap_num_entries(&hashmap)); - hashmap_destroy(&hashmap); -} - -UTEST(c_sse42, hash_conflict) { - struct hashmap_s hashmap; - - int x = 42; - int y = 13; - int z = -53; - - ASSERT_EQ(0, hashmap_create(4, &hashmap)); - - // These all hash to the same value. - ASSERT_EQ(0, hashmap_put(&hashmap, "000", 3, &x)); - ASSERT_EQ(0, hashmap_put(&hashmap, "002", 3, &y)); - ASSERT_EQ(0, hashmap_put(&hashmap, "007", 3, &z)); - ASSERT_EQ(3u, hashmap_num_entries(&hashmap)); - - // Now we remove the middle value. - ASSERT_EQ(0, hashmap_remove(&hashmap, "002", 3)); - ASSERT_EQ(2u, hashmap_num_entries(&hashmap)); - - // And now attempt to insert the last value again. There was a bug where this - // would insert a new entry incorrectly instead of resolving to the previous - // entry. - ASSERT_EQ(0, hashmap_put(&hashmap, "007", 3, &z)); - ASSERT_EQ(2u, hashmap_num_entries(&hashmap)); - - hashmap_destroy(&hashmap); -} - -UTEST(c_sse42, issue_20) { - struct hashmap_s hashmap; - const char *key = "192.168.2.2hv_api.udache.com/abc/def"; - unsigned int len = (unsigned int)strlen(key); - - char *value = "1"; - char *ptr = NULL; - - hashmap_create(1024, &hashmap); - hashmap_put(&hashmap, key, len, value); - - ptr = (char *)hashmap_get(&hashmap, key, len); - - ASSERT_EQ(value, ptr); - - hashmap_destroy(&hashmap); -} +#include "test.inc" #endif