improve hash key handling

Wed, 08 Jun 2022 21:33:31 +0200

author
Mike Becker <universe@uap-core.de>
date
Wed, 08 Jun 2022 21:33:31 +0200
changeset 563
69a83fad8a35
parent 562
fd3368c20413
child 564
5d8ad7a0ff71

improve hash key handling

src/CMakeLists.txt file | annotate | diff | comparison | revisions
src/cx/common.h file | annotate | diff | comparison | revisions
src/cx/hash_key.h file | annotate | diff | comparison | revisions
src/cx/hash_map.h file | annotate | diff | comparison | revisions
src/cx/map.h file | annotate | diff | comparison | revisions
src/hash_key.c file | annotate | diff | comparison | revisions
src/hash_map.c file | annotate | diff | comparison | revisions
src/map.c file | annotate | diff | comparison | revisions
test/test_map.cpp file | annotate | diff | comparison | revisions
--- a/src/CMakeLists.txt	Fri May 27 17:40:27 2022 +0200
+++ b/src/CMakeLists.txt	Wed Jun 08 21:33:31 2022 +0200
@@ -5,7 +5,7 @@
         linked_list.c
         tree.c
         buffer.c
-        map.c
+        hash_key.c
         hash_map.c
 )
 set(headers
@@ -18,6 +18,7 @@
         cx/tree.h
         cx/buffer.h
         cx/map.h
+        cx/hash_key.h
         cx/hash_map.h
         )
 
--- a/src/cx/common.h	Fri May 27 17:40:27 2022 +0200
+++ b/src/cx/common.h	Wed Jun 08 21:33:31 2022 +0200
@@ -94,25 +94,6 @@
 #include <stdbool.h>
 
 /**
- * Structure that contains a pointer to arbitrary data.
- */
-struct cx_data_ptr_s {
-    /**
-     * A pointer to the data.
-     */
-    unsigned char const *data;
-    /**
-     * The length of the data.
-     */
-    size_t len;
-};
-
-/**
- * Type for a data pointer with length information.
- */
-typedef struct cx_data_ptr_s CxDataPtr;
-
-/**
  * Function pointer compatible with fwrite-like functions.
  */
 typedef size_t (*cx_write_func)(
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cx/hash_key.h	Wed Jun 08 21:33:31 2022 +0200
@@ -0,0 +1,125 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * \file hash_key.h
+ * \brief Interface for map implementations.
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+
+#ifndef UCX_HASH_KEY_H
+#define UCX_HASH_KEY_H
+
+#include "stddef.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Internal structure for a key within a hash map. */
+struct cx_hash_key_s {
+    /** The key data. */
+    union {
+        unsigned char *bytes;
+        unsigned char const *cbytes;
+        char const *cstr;
+        char *str;
+        void *obj;
+    } data;
+    /**
+     * The key data length.
+     */
+    size_t len;
+    /** The hash value of the key data. */
+    unsigned hash;
+};
+
+/**
+ * Type for a hash key.
+ */
+typedef struct cx_hash_key_s CxHashKey;
+
+/**
+ * Computes a murmur hash-2.
+ *
+ * You need to initialize data and len in the key struct.
+ * The hash is then directly written to that struct.
+ *
+ * @param key the key, the hash shall be computed for
+ */
+void cx_hash_murmur(CxHashKey *key);
+
+/**
+ * Computes a hash key from a string.
+ *
+ * The string needs to be zero-terminated.
+ *
+ * @param str the string
+ * @return the hash key
+ */
+__attribute__((__warn_unused_result__))
+CxHashKey cx_hash_key_str(char const *str);
+
+/**
+ * Computes a hash key from a byte array.
+ *
+ * @param bytes the array
+ * @param len the length
+ * @return the hash key
+ */
+__attribute__((__warn_unused_result__))
+CxHashKey cx_hash_key_bytes(
+        unsigned char const *bytes,
+        size_t len
+);
+
+/**
+ * Computes a hash key for an arbitrary object.
+ *
+ * The computation uses the in-memory representation that might not be
+ * the same on different platforms. Therefore, this hash should not be
+ * used for data exchange with different machines.
+ *
+ * @param obj a pointer to an arbitrary object
+ * @param len the length of object in memory
+ * @return the hash key
+ */
+__attribute__((__warn_unused_result__))
+CxHashKey cx_hash_key(
+        void *obj,
+        size_t len
+);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* UCX_HASH_KEY_H */
--- a/src/cx/hash_map.h	Fri May 27 17:40:27 2022 +0200
+++ b/src/cx/hash_map.h	Wed Jun 08 21:33:31 2022 +0200
@@ -26,8 +26,8 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 /**
- * \file map.h
- * \brief Interface for map implementations.
+ * \file hash_map.h
+ * \brief Hash map implementation.
  * \author Mike Becker
  * \author Olaf Wintermann
  * \version 3.0
@@ -43,18 +43,6 @@
 extern "C" {
 #endif
 
-/** Internal structure for a key within a hash map. */
-struct cx_hash_map_key_s {
-    /** The key data. */
-    unsigned char *data;
-    /**
-     * The key data length.
-     */
-    size_t len;
-    /** The hash value of the key data. */
-    unsigned hash;
-};
-
 /** Internal structure for an element of a hash map. */
 struct cx_hash_map_element_s {
     /** The value data. */
@@ -64,7 +52,7 @@
     struct cx_hash_map_element_s *next;
 
     /** The corresponding key. */
-    struct cx_hash_map_key_s key;
+    CxHashKey key;
 };
 
 /**
--- a/src/cx/map.h	Fri May 27 17:40:27 2022 +0200
+++ b/src/cx/map.h	Wed Jun 08 21:33:31 2022 +0200
@@ -40,6 +40,7 @@
 #include "common.h"
 #include "allocator.h"
 #include "iterator.h"
+#include "hash_key.h"
 
 #ifdef    __cplusplus
 extern "C" {
@@ -87,7 +88,7 @@
     __attribute__((__nonnull__))
     int (*put)(
             CxMap *map,
-            CxDataPtr key,
+            CxHashKey key,
             void *value
     );
 
@@ -97,7 +98,7 @@
     __attribute__((__nonnull__, __warn_unused_result__))
     void *(*get)(
             CxMap const *map,
-            CxDataPtr key
+            CxHashKey key
     );
 
     /**
@@ -106,7 +107,7 @@
     __attribute__((__nonnull__, __warn_unused_result__))
     void *(*remove)(
             CxMap *map,
-            CxDataPtr key
+            CxHashKey key
     );
 
     /**
@@ -135,7 +136,7 @@
     /**
      * A pointer to the key.
      */
-    CxDataPtr const *key;
+    CxHashKey const *key;
     /**
      * A pointer to the value.
      */
@@ -176,7 +177,7 @@
 __attribute__((__nonnull__))
 static inline int cxMapPut(
         CxMap *map,
-        CxDataPtr key,
+        CxHashKey key,
         void *value
 ) {
     return map->cl->put(map, key, value);
@@ -192,7 +193,7 @@
 __attribute__((__nonnull__, __warn_unused_result__))
 static inline void *cxMapGet(
         CxMap const *map,
-        CxDataPtr key
+        CxHashKey key
 ) {
     return map->cl->get(map, key);
 }
@@ -207,7 +208,7 @@
 __attribute__((__nonnull__, __warn_unused_result__))
 static inline void *cxMapRemove(
         CxMap *map,
-        CxDataPtr key
+        CxHashKey key
 ) {
     return map->cl->remove(map, key);
 }
@@ -262,15 +263,6 @@
     return map->cl->iterator(map);
 }
 
-/**
- * Convenience function to make a key from a NULL-terminated string.
- *
- * @param str the NULL-terminated string
- * @return the string wrapped to be used as a map key
- */
-__attribute__((__nonnull__, __warn_unused_result__))
-CxDataPtr cxMapKeyStr(char const *str);
-
 #ifdef    __cplusplus
 }
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hash_key.c	Wed Jun 08 21:33:31 2022 +0200
@@ -0,0 +1,107 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "cx/hash_key.h"
+#include <string.h>
+
+void cx_hash_murmur(CxHashKey *key) {
+    size_t len = key->len;
+    unsigned char const *data = key->data.cbytes;
+
+    unsigned m = 0x5bd1e995;
+    unsigned r = 24;
+    unsigned h = 25 ^ len;
+    unsigned i = 0;
+    while (len >= 4) {
+        unsigned k = data[i + 0] & 0xFF;
+        k |= (data[i + 1] & 0xFF) << 8;
+        k |= (data[i + 2] & 0xFF) << 16;
+        k |= (data[i + 3] & 0xFF) << 24;
+
+        k *= m;
+        k ^= k >> r;
+        k *= m;
+
+        h *= m;
+        h ^= k;
+
+        i += 4;
+        len -= 4;
+    }
+
+    switch (len) {
+        case 3:
+            h ^= (data[i + 2] & 0xFF) << 16;
+                    __attribute__((__fallthrough__));
+        case 2:
+            h ^= (data[i + 1] & 0xFF) << 8;
+                    __attribute__((__fallthrough__));
+        case 1:
+            h ^= (data[i + 0] & 0xFF);
+            h *= m;
+                    __attribute__((__fallthrough__));
+        default:
+            /* do nothing */;
+    }
+
+    h ^= h >> 13;
+    h *= m;
+    h ^= h >> 15;
+
+    key->hash = h;
+}
+
+CxHashKey cx_hash_key_str(char const *str) {
+    CxHashKey key;
+    key.data.cstr = str;
+    key.len = str == NULL ? 0 : (1 + strlen(str));
+    cx_hash_murmur(&key);
+    return key;
+}
+
+CxHashKey cx_hash_key_bytes(
+        unsigned char const *bytes,
+        size_t len
+) {
+    CxHashKey key;
+    key.data.cbytes = bytes;
+    key.len = len;
+    cx_hash_murmur(&key);
+    return key;
+}
+
+CxHashKey cx_hash_key(
+        void *obj,
+        size_t len
+) {
+    CxHashKey key;
+    key.data.obj = obj;
+    key.len = len;
+    cx_hash_murmur(&key);
+    return key;
+}
--- a/src/hash_map.c	Fri May 27 17:40:27 2022 +0200
+++ b/src/hash_map.c	Wed Jun 08 21:33:31 2022 +0200
@@ -30,60 +30,6 @@
 #include "cx/hash_map.h"
 #include "cx/utils.h"
 
-/**
- * Computes a murmur hash-2.
- *
- * @param data the data to hash
- * @param len the length of the data
- * @return the murmur hash-2 of the data
- */
-static unsigned cx_hash_map_murmur(
-        unsigned char const *data,
-        size_t len
-) {
-    unsigned m = 0x5bd1e995;
-    unsigned r = 24;
-    unsigned h = 25 ^ len;
-    unsigned i = 0;
-    while (len >= 4) {
-        unsigned k = data[i + 0] & 0xFF;
-        k |= (data[i + 1] & 0xFF) << 8;
-        k |= (data[i + 2] & 0xFF) << 16;
-        k |= (data[i + 3] & 0xFF) << 24;
-
-        k *= m;
-        k ^= k >> r;
-        k *= m;
-
-        h *= m;
-        h ^= k;
-
-        i += 4;
-        len -= 4;
-    }
-
-    switch (len) {
-        case 3:
-            h ^= (data[i + 2] & 0xFF) << 16;
-                    __attribute__((__fallthrough__));
-        case 2:
-            h ^= (data[i + 1] & 0xFF) << 8;
-                    __attribute__((__fallthrough__));
-        case 1:
-            h ^= (data[i + 0] & 0xFF);
-            h *= m;
-                    __attribute__((__fallthrough__));
-        default:
-            /* do nothing */;
-    }
-
-    h ^= h >> 13;
-    h *= m;
-    h ^= h >> 15;
-
-    return h;
-}
-
 static void cx_hash_map_clear(struct cx_map_s *map) {
     struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
     cx_for_n(i, hash_map->bucket_count) {
@@ -92,7 +38,7 @@
             do {
                 struct cx_hash_map_element_s *next = elem->next;
                 // free the key data
-                cxFree(map->allocator, elem->key.data);
+                cxFree(map->allocator, elem->key.data.obj);
                 // free the node
                 cxFree(map->allocator, elem);
                 // proceed
@@ -119,13 +65,17 @@
 
 static int cx_hash_map_put(
         CxMap *map,
-        CxDataPtr key,
+        CxHashKey key,
         void *value
 ) {
     struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
     CxAllocator *allocator = map->allocator;
 
-    unsigned hash = cx_hash_map_murmur(key.data, key.len);
+    unsigned hash = key.hash;
+    if (hash == 0) {
+        cx_hash_murmur(&key);
+        hash = key.hash;
+    }
 
     size_t slot = hash % hash_map->bucket_count;
     struct cx_hash_map_element_s *elm = hash_map->buckets[slot];
@@ -151,8 +101,8 @@
         if (kd == NULL) {
             return -1;
         }
-        memcpy(kd, key.data, key.len);
-        e->key.data = kd;
+        memcpy(kd, key.data.obj, key.len);
+        e->key.data.obj = kd;
         e->key.len = key.len;
         e->key.hash = hash;
 
@@ -187,7 +137,7 @@
         prev->next = elm->next;
     }
     // free element
-    cxFree(hash_map->base.allocator, elm->key.data);
+    cxFree(hash_map->base.allocator, elm->key.data.obj);
     cxFree(hash_map->base.allocator, elm);
     // decrease size
     hash_map->base.size--;
@@ -203,19 +153,23 @@
  */
 static void *cx_hash_map_get_remove(
         CxMap *map,
-        CxDataPtr key,
+        CxHashKey key,
         bool remove
 ) {
     struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
 
-    unsigned hash = cx_hash_map_murmur(key.data, key.len);
+    unsigned hash = key.hash;
+    if (hash == 0) {
+        cx_hash_murmur(&key);
+        hash = key.hash;
+    }
 
     size_t slot = hash % hash_map->bucket_count;
     struct cx_hash_map_element_s *elm = hash_map->buckets[slot];
     struct cx_hash_map_element_s *prev = NULL;
     while (elm && elm->key.hash <= hash) {
         if (elm->key.hash == hash && elm->key.len == key.len) {
-            if (memcmp(elm->key.data, key.data, key.len) == 0) {
+            if (memcmp(elm->key.data.obj, key.data.obj, key.len) == 0) {
                 void *data = elm->data;
                 if (remove) {
                     cx_hash_map_unlink(hash_map, slot, prev, elm);
@@ -232,7 +186,7 @@
 
 static void *cx_hash_map_get(
         CxMap const *map,
-        CxDataPtr key
+        CxHashKey key
 ) {
     // we can safely cast, because we know when remove=false, the map stays untouched
     return cx_hash_map_get_remove((CxMap *) map, key, false);
@@ -240,7 +194,7 @@
 
 static void *cx_hash_map_remove(
         CxMap *map,
-        CxDataPtr key
+        CxHashKey key
 ) {
     return cx_hash_map_get_remove(map, key, true);
 }
--- a/src/map.c	Fri May 27 17:40:27 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- *   1. Redistributions of source code must retain the above copyright
- *      notice, this list of conditions and the following disclaimer.
- *
- *   2. Redistributions in binary form must reproduce the above copyright
- *      notice, this list of conditions and the following disclaimer in the
- *      documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <string.h>
-#include "cx/map.h"
-
-CxDataPtr cxMapKeyStr(char const *str) {
-    CxDataPtr key = {(unsigned char const *) str, 1 + strlen(str)};
-    return key;
-}
\ No newline at end of file
--- a/test/test_map.cpp	Fri May 27 17:40:27 2022 +0200
+++ b/test/test_map.cpp	Wed Jun 08 21:33:31 2022 +0200
@@ -72,9 +72,9 @@
     {
         auto keyiter = cxMapIteratorKeys(map);
         std::unordered_set<std::string> keys;
-        cx_foreach(CxDataPtr*, elem, keyiter) {
+        cx_foreach(CxHashKey*, elem, keyiter) {
             // we use that our test keys contain NULL-terminated strings
-            keys.insert(std::string(reinterpret_cast<const char *>(elem->data)));
+            keys.insert(std::string(elem->data.cstr));
         }
         EXPECT_EQ(keyiter.index, map->size);
         ASSERT_EQ(keys.size(), map->size);
@@ -103,7 +103,7 @@
         auto pairiter = cxMapIterator(map);
         std::unordered_map<std::string, std::string> pairs;
         cx_foreach(CxMapEntry*, entry, pairiter) {
-            pairs[std::string((char const *) entry->key->data)] = std::string((char *) entry->value);
+            pairs[std::string(entry->key->data.cstr)] = std::string((char *) entry->value);
         }
         EXPECT_EQ(pairiter.index, map->size);
         ASSERT_EQ(pairs.size(), refmap.size());
@@ -144,7 +144,8 @@
 
     // execute operations and verify results
     for (auto &&op: ops) {
-        CxDataPtr key = cxMapKeyStr(op.key);
+        CxHashKey key = cx_hash_key_str(op.key);
+        key.hash = 0; // force the hash map to compute the hash
         if (op.op == map_operation::put) {
             // execute a put operation and verify that the exact value can be read back
             refmap[std::string(op.key)] = std::string(op.value);
@@ -176,26 +177,26 @@
     CxTestingAllocator allocator;
     auto map = cxHashMapCreate(&allocator, 4);
 
-    cxMapPut(map, cxMapKeyStr("key 1"), (void *) "val 1");
-    cxMapPut(map, cxMapKeyStr("key 2"), (void *) "val 2");
-    cxMapPut(map, cxMapKeyStr("key 3"), (void *) "val 3");
-    cxMapPut(map, cxMapKeyStr("key 4"), (void *) "val 4");
-    cxMapPut(map, cxMapKeyStr("key 5"), (void *) "val 5");
-    cxMapPut(map, cxMapKeyStr("key 6"), (void *) "val 6");
+    cxMapPut(map, cx_hash_key_str("key 1"), (void *) "val 1");
+    cxMapPut(map, cx_hash_key_str("key 2"), (void *) "val 2");
+    cxMapPut(map, cx_hash_key_str("key 3"), (void *) "val 3");
+    cxMapPut(map, cx_hash_key_str("key 4"), (void *) "val 4");
+    cxMapPut(map, cx_hash_key_str("key 5"), (void *) "val 5");
+    cxMapPut(map, cx_hash_key_str("key 6"), (void *) "val 6");
 
     auto iter = cxMapIterator(map);
     cx_foreach(CxMapEntry*, entry, iter) {
-        if (entry->key->data[4] % 2 == 1) iter.remove = true;
+        if (entry->key->data.cstr[4] % 2 == 1) iter.remove = true;
     }
     EXPECT_EQ(map->size, 3);
     EXPECT_EQ(iter.index, map->size);
 
-    EXPECT_EQ(cxMapGet(map, cxMapKeyStr("key 1")), nullptr);
-    EXPECT_NE(cxMapGet(map, cxMapKeyStr("key 2")), nullptr);
-    EXPECT_EQ(cxMapGet(map, cxMapKeyStr("key 3")), nullptr);
-    EXPECT_NE(cxMapGet(map, cxMapKeyStr("key 4")), nullptr);
-    EXPECT_EQ(cxMapGet(map, cxMapKeyStr("key 5")), nullptr);
-    EXPECT_NE(cxMapGet(map, cxMapKeyStr("key 6")), nullptr);
+    EXPECT_EQ(cxMapGet(map, cx_hash_key_str("key 1")), nullptr);
+    EXPECT_NE(cxMapGet(map, cx_hash_key_str("key 2")), nullptr);
+    EXPECT_EQ(cxMapGet(map, cx_hash_key_str("key 3")), nullptr);
+    EXPECT_NE(cxMapGet(map, cx_hash_key_str("key 4")), nullptr);
+    EXPECT_EQ(cxMapGet(map, cx_hash_key_str("key 5")), nullptr);
+    EXPECT_NE(cxMapGet(map, cx_hash_key_str("key 6")), nullptr);
 
     cxMapDestroy(map);
     EXPECT_TRUE(allocator.verify());
@@ -205,12 +206,12 @@
     CxTestingAllocator allocator;
     auto map = cxHashMapCreate(&allocator, 8);
 
-    cxMapPut(map, cxMapKeyStr("key 1"), (void *) "val 1");
-    cxMapPut(map, cxMapKeyStr("key 2"), (void *) "val 2");
-    cxMapPut(map, cxMapKeyStr("key 3"), (void *) "val 3");
-    cxMapPut(map, cxMapKeyStr("key 4"), (void *) "val 4");
-    cxMapPut(map, cxMapKeyStr("key 5"), (void *) "val 5");
-    cxMapPut(map, cxMapKeyStr("key 6"), (void *) "val 6");
+    cxMapPut(map, cx_hash_key_str("key 1"), (void *) "val 1");
+    cxMapPut(map, cx_hash_key_str("key 2"), (void *) "val 2");
+    cxMapPut(map, cx_hash_key_str("key 3"), (void *) "val 3");
+    cxMapPut(map, cx_hash_key_str("key 4"), (void *) "val 4");
+    cxMapPut(map, cx_hash_key_str("key 5"), (void *) "val 5");
+    cxMapPut(map, cx_hash_key_str("key 6"), (void *) "val 6");
 
     // 6/8 does not exceed 0.75, therefore the function should not rehash
     int result = cxMapRehash(map);
@@ -225,26 +226,26 @@
     CxTestingAllocator allocator;
     auto map = cxHashMapCreate(&allocator, 8);
 
-    cxMapPut(map, cxMapKeyStr("key 1"), (void *) "val 1");
-    cxMapPut(map, cxMapKeyStr("key 2"), (void *) "val 2");
-    cxMapPut(map, cxMapKeyStr("key 3"), (void *) "val 3");
-    cxMapPut(map, cxMapKeyStr("key 4"), (void *) "val 4");
-    cxMapPut(map, cxMapKeyStr("key 5"), (void *) "val 5");
-    cxMapPut(map, cxMapKeyStr("key 6"), (void *) "val 6");
-    cxMapPut(map, cxMapKeyStr("key 7"), (void *) "val 7");
+    cxMapPut(map, cx_hash_key_str("key 1"), (void *) "val 1");
+    cxMapPut(map, cx_hash_key_str("key 2"), (void *) "val 2");
+    cxMapPut(map, cx_hash_key_str("key 3"), (void *) "val 3");
+    cxMapPut(map, cx_hash_key_str("key 4"), (void *) "val 4");
+    cxMapPut(map, cx_hash_key_str("key 5"), (void *) "val 5");
+    cxMapPut(map, cx_hash_key_str("key 6"), (void *) "val 6");
+    cxMapPut(map, cx_hash_key_str("key 7"), (void *) "val 7");
 
     int result = cxMapRehash(map);
     EXPECT_EQ(result, 0);
     EXPECT_EQ(reinterpret_cast<struct cx_hash_map_s *>(map)->bucket_count, 17);
     EXPECT_EQ(map->size, 7);
 
-    EXPECT_EQ(strcmp((char *) cxMapGet(map, cxMapKeyStr("key 1")), "val 1"), 0);
-    EXPECT_EQ(strcmp((char *) cxMapGet(map, cxMapKeyStr("key 2")), "val 2"), 0);
-    EXPECT_EQ(strcmp((char *) cxMapGet(map, cxMapKeyStr("key 3")), "val 3"), 0);
-    EXPECT_EQ(strcmp((char *) cxMapGet(map, cxMapKeyStr("key 4")), "val 4"), 0);
-    EXPECT_EQ(strcmp((char *) cxMapGet(map, cxMapKeyStr("key 5")), "val 5"), 0);
-    EXPECT_EQ(strcmp((char *) cxMapGet(map, cxMapKeyStr("key 6")), "val 6"), 0);
-    EXPECT_EQ(strcmp((char *) cxMapGet(map, cxMapKeyStr("key 7")), "val 7"), 0);
+    EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 1")), "val 1"), 0);
+    EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 2")), "val 2"), 0);
+    EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 3")), "val 3"), 0);
+    EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 4")), "val 4"), 0);
+    EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 5")), "val 5"), 0);
+    EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 6")), "val 6"), 0);
+    EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 7")), "val 7"), 0);
 
     cxMapDestroy(map);
     EXPECT_TRUE(allocator.verify());

mercurial