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
     1.1 --- a/src/CMakeLists.txt	Fri May 27 17:40:27 2022 +0200
     1.2 +++ b/src/CMakeLists.txt	Wed Jun 08 21:33:31 2022 +0200
     1.3 @@ -5,7 +5,7 @@
     1.4          linked_list.c
     1.5          tree.c
     1.6          buffer.c
     1.7 -        map.c
     1.8 +        hash_key.c
     1.9          hash_map.c
    1.10  )
    1.11  set(headers
    1.12 @@ -18,6 +18,7 @@
    1.13          cx/tree.h
    1.14          cx/buffer.h
    1.15          cx/map.h
    1.16 +        cx/hash_key.h
    1.17          cx/hash_map.h
    1.18          )
    1.19  
     2.1 --- a/src/cx/common.h	Fri May 27 17:40:27 2022 +0200
     2.2 +++ b/src/cx/common.h	Wed Jun 08 21:33:31 2022 +0200
     2.3 @@ -94,25 +94,6 @@
     2.4  #include <stdbool.h>
     2.5  
     2.6  /**
     2.7 - * Structure that contains a pointer to arbitrary data.
     2.8 - */
     2.9 -struct cx_data_ptr_s {
    2.10 -    /**
    2.11 -     * A pointer to the data.
    2.12 -     */
    2.13 -    unsigned char const *data;
    2.14 -    /**
    2.15 -     * The length of the data.
    2.16 -     */
    2.17 -    size_t len;
    2.18 -};
    2.19 -
    2.20 -/**
    2.21 - * Type for a data pointer with length information.
    2.22 - */
    2.23 -typedef struct cx_data_ptr_s CxDataPtr;
    2.24 -
    2.25 -/**
    2.26   * Function pointer compatible with fwrite-like functions.
    2.27   */
    2.28  typedef size_t (*cx_write_func)(
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/src/cx/hash_key.h	Wed Jun 08 21:33:31 2022 +0200
     3.3 @@ -0,0 +1,125 @@
     3.4 +/*
     3.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3.6 + *
     3.7 + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
     3.8 + *
     3.9 + * Redistribution and use in source and binary forms, with or without
    3.10 + * modification, are permitted provided that the following conditions are met:
    3.11 + *
    3.12 + *   1. Redistributions of source code must retain the above copyright
    3.13 + *      notice, this list of conditions and the following disclaimer.
    3.14 + *
    3.15 + *   2. Redistributions in binary form must reproduce the above copyright
    3.16 + *      notice, this list of conditions and the following disclaimer in the
    3.17 + *      documentation and/or other materials provided with the distribution.
    3.18 + *
    3.19 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    3.20 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    3.21 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    3.22 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    3.23 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    3.24 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    3.25 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    3.26 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    3.27 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    3.28 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    3.29 + * POSSIBILITY OF SUCH DAMAGE.
    3.30 + */
    3.31 +/**
    3.32 + * \file hash_key.h
    3.33 + * \brief Interface for map implementations.
    3.34 + * \author Mike Becker
    3.35 + * \author Olaf Wintermann
    3.36 + * \version 3.0
    3.37 + * \copyright 2-Clause BSD License
    3.38 + */
    3.39 +
    3.40 +
    3.41 +#ifndef UCX_HASH_KEY_H
    3.42 +#define UCX_HASH_KEY_H
    3.43 +
    3.44 +#include "stddef.h"
    3.45 +
    3.46 +#ifdef __cplusplus
    3.47 +extern "C" {
    3.48 +#endif
    3.49 +
    3.50 +/** Internal structure for a key within a hash map. */
    3.51 +struct cx_hash_key_s {
    3.52 +    /** The key data. */
    3.53 +    union {
    3.54 +        unsigned char *bytes;
    3.55 +        unsigned char const *cbytes;
    3.56 +        char const *cstr;
    3.57 +        char *str;
    3.58 +        void *obj;
    3.59 +    } data;
    3.60 +    /**
    3.61 +     * The key data length.
    3.62 +     */
    3.63 +    size_t len;
    3.64 +    /** The hash value of the key data. */
    3.65 +    unsigned hash;
    3.66 +};
    3.67 +
    3.68 +/**
    3.69 + * Type for a hash key.
    3.70 + */
    3.71 +typedef struct cx_hash_key_s CxHashKey;
    3.72 +
    3.73 +/**
    3.74 + * Computes a murmur hash-2.
    3.75 + *
    3.76 + * You need to initialize data and len in the key struct.
    3.77 + * The hash is then directly written to that struct.
    3.78 + *
    3.79 + * @param key the key, the hash shall be computed for
    3.80 + */
    3.81 +void cx_hash_murmur(CxHashKey *key);
    3.82 +
    3.83 +/**
    3.84 + * Computes a hash key from a string.
    3.85 + *
    3.86 + * The string needs to be zero-terminated.
    3.87 + *
    3.88 + * @param str the string
    3.89 + * @return the hash key
    3.90 + */
    3.91 +__attribute__((__warn_unused_result__))
    3.92 +CxHashKey cx_hash_key_str(char const *str);
    3.93 +
    3.94 +/**
    3.95 + * Computes a hash key from a byte array.
    3.96 + *
    3.97 + * @param bytes the array
    3.98 + * @param len the length
    3.99 + * @return the hash key
   3.100 + */
   3.101 +__attribute__((__warn_unused_result__))
   3.102 +CxHashKey cx_hash_key_bytes(
   3.103 +        unsigned char const *bytes,
   3.104 +        size_t len
   3.105 +);
   3.106 +
   3.107 +/**
   3.108 + * Computes a hash key for an arbitrary object.
   3.109 + *
   3.110 + * The computation uses the in-memory representation that might not be
   3.111 + * the same on different platforms. Therefore, this hash should not be
   3.112 + * used for data exchange with different machines.
   3.113 + *
   3.114 + * @param obj a pointer to an arbitrary object
   3.115 + * @param len the length of object in memory
   3.116 + * @return the hash key
   3.117 + */
   3.118 +__attribute__((__warn_unused_result__))
   3.119 +CxHashKey cx_hash_key(
   3.120 +        void *obj,
   3.121 +        size_t len
   3.122 +);
   3.123 +
   3.124 +#ifdef __cplusplus
   3.125 +} // extern "C"
   3.126 +#endif
   3.127 +
   3.128 +#endif /* UCX_HASH_KEY_H */
     4.1 --- a/src/cx/hash_map.h	Fri May 27 17:40:27 2022 +0200
     4.2 +++ b/src/cx/hash_map.h	Wed Jun 08 21:33:31 2022 +0200
     4.3 @@ -26,8 +26,8 @@
     4.4   * POSSIBILITY OF SUCH DAMAGE.
     4.5   */
     4.6  /**
     4.7 - * \file map.h
     4.8 - * \brief Interface for map implementations.
     4.9 + * \file hash_map.h
    4.10 + * \brief Hash map implementation.
    4.11   * \author Mike Becker
    4.12   * \author Olaf Wintermann
    4.13   * \version 3.0
    4.14 @@ -43,18 +43,6 @@
    4.15  extern "C" {
    4.16  #endif
    4.17  
    4.18 -/** Internal structure for a key within a hash map. */
    4.19 -struct cx_hash_map_key_s {
    4.20 -    /** The key data. */
    4.21 -    unsigned char *data;
    4.22 -    /**
    4.23 -     * The key data length.
    4.24 -     */
    4.25 -    size_t len;
    4.26 -    /** The hash value of the key data. */
    4.27 -    unsigned hash;
    4.28 -};
    4.29 -
    4.30  /** Internal structure for an element of a hash map. */
    4.31  struct cx_hash_map_element_s {
    4.32      /** The value data. */
    4.33 @@ -64,7 +52,7 @@
    4.34      struct cx_hash_map_element_s *next;
    4.35  
    4.36      /** The corresponding key. */
    4.37 -    struct cx_hash_map_key_s key;
    4.38 +    CxHashKey key;
    4.39  };
    4.40  
    4.41  /**
     5.1 --- a/src/cx/map.h	Fri May 27 17:40:27 2022 +0200
     5.2 +++ b/src/cx/map.h	Wed Jun 08 21:33:31 2022 +0200
     5.3 @@ -40,6 +40,7 @@
     5.4  #include "common.h"
     5.5  #include "allocator.h"
     5.6  #include "iterator.h"
     5.7 +#include "hash_key.h"
     5.8  
     5.9  #ifdef    __cplusplus
    5.10  extern "C" {
    5.11 @@ -87,7 +88,7 @@
    5.12      __attribute__((__nonnull__))
    5.13      int (*put)(
    5.14              CxMap *map,
    5.15 -            CxDataPtr key,
    5.16 +            CxHashKey key,
    5.17              void *value
    5.18      );
    5.19  
    5.20 @@ -97,7 +98,7 @@
    5.21      __attribute__((__nonnull__, __warn_unused_result__))
    5.22      void *(*get)(
    5.23              CxMap const *map,
    5.24 -            CxDataPtr key
    5.25 +            CxHashKey key
    5.26      );
    5.27  
    5.28      /**
    5.29 @@ -106,7 +107,7 @@
    5.30      __attribute__((__nonnull__, __warn_unused_result__))
    5.31      void *(*remove)(
    5.32              CxMap *map,
    5.33 -            CxDataPtr key
    5.34 +            CxHashKey key
    5.35      );
    5.36  
    5.37      /**
    5.38 @@ -135,7 +136,7 @@
    5.39      /**
    5.40       * A pointer to the key.
    5.41       */
    5.42 -    CxDataPtr const *key;
    5.43 +    CxHashKey const *key;
    5.44      /**
    5.45       * A pointer to the value.
    5.46       */
    5.47 @@ -176,7 +177,7 @@
    5.48  __attribute__((__nonnull__))
    5.49  static inline int cxMapPut(
    5.50          CxMap *map,
    5.51 -        CxDataPtr key,
    5.52 +        CxHashKey key,
    5.53          void *value
    5.54  ) {
    5.55      return map->cl->put(map, key, value);
    5.56 @@ -192,7 +193,7 @@
    5.57  __attribute__((__nonnull__, __warn_unused_result__))
    5.58  static inline void *cxMapGet(
    5.59          CxMap const *map,
    5.60 -        CxDataPtr key
    5.61 +        CxHashKey key
    5.62  ) {
    5.63      return map->cl->get(map, key);
    5.64  }
    5.65 @@ -207,7 +208,7 @@
    5.66  __attribute__((__nonnull__, __warn_unused_result__))
    5.67  static inline void *cxMapRemove(
    5.68          CxMap *map,
    5.69 -        CxDataPtr key
    5.70 +        CxHashKey key
    5.71  ) {
    5.72      return map->cl->remove(map, key);
    5.73  }
    5.74 @@ -262,15 +263,6 @@
    5.75      return map->cl->iterator(map);
    5.76  }
    5.77  
    5.78 -/**
    5.79 - * Convenience function to make a key from a NULL-terminated string.
    5.80 - *
    5.81 - * @param str the NULL-terminated string
    5.82 - * @return the string wrapped to be used as a map key
    5.83 - */
    5.84 -__attribute__((__nonnull__, __warn_unused_result__))
    5.85 -CxDataPtr cxMapKeyStr(char const *str);
    5.86 -
    5.87  #ifdef    __cplusplus
    5.88  }
    5.89  #endif
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/src/hash_key.c	Wed Jun 08 21:33:31 2022 +0200
     6.3 @@ -0,0 +1,107 @@
     6.4 +/*
     6.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     6.6 + *
     6.7 + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
     6.8 + *
     6.9 + * Redistribution and use in source and binary forms, with or without
    6.10 + * modification, are permitted provided that the following conditions are met:
    6.11 + *
    6.12 + *   1. Redistributions of source code must retain the above copyright
    6.13 + *      notice, this list of conditions and the following disclaimer.
    6.14 + *
    6.15 + *   2. Redistributions in binary form must reproduce the above copyright
    6.16 + *      notice, this list of conditions and the following disclaimer in the
    6.17 + *      documentation and/or other materials provided with the distribution.
    6.18 + *
    6.19 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    6.20 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    6.21 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    6.22 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    6.23 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    6.24 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    6.25 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    6.26 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    6.27 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    6.28 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    6.29 + * POSSIBILITY OF SUCH DAMAGE.
    6.30 + */
    6.31 +
    6.32 +#include "cx/hash_key.h"
    6.33 +#include <string.h>
    6.34 +
    6.35 +void cx_hash_murmur(CxHashKey *key) {
    6.36 +    size_t len = key->len;
    6.37 +    unsigned char const *data = key->data.cbytes;
    6.38 +
    6.39 +    unsigned m = 0x5bd1e995;
    6.40 +    unsigned r = 24;
    6.41 +    unsigned h = 25 ^ len;
    6.42 +    unsigned i = 0;
    6.43 +    while (len >= 4) {
    6.44 +        unsigned k = data[i + 0] & 0xFF;
    6.45 +        k |= (data[i + 1] & 0xFF) << 8;
    6.46 +        k |= (data[i + 2] & 0xFF) << 16;
    6.47 +        k |= (data[i + 3] & 0xFF) << 24;
    6.48 +
    6.49 +        k *= m;
    6.50 +        k ^= k >> r;
    6.51 +        k *= m;
    6.52 +
    6.53 +        h *= m;
    6.54 +        h ^= k;
    6.55 +
    6.56 +        i += 4;
    6.57 +        len -= 4;
    6.58 +    }
    6.59 +
    6.60 +    switch (len) {
    6.61 +        case 3:
    6.62 +            h ^= (data[i + 2] & 0xFF) << 16;
    6.63 +                    __attribute__((__fallthrough__));
    6.64 +        case 2:
    6.65 +            h ^= (data[i + 1] & 0xFF) << 8;
    6.66 +                    __attribute__((__fallthrough__));
    6.67 +        case 1:
    6.68 +            h ^= (data[i + 0] & 0xFF);
    6.69 +            h *= m;
    6.70 +                    __attribute__((__fallthrough__));
    6.71 +        default:
    6.72 +            /* do nothing */;
    6.73 +    }
    6.74 +
    6.75 +    h ^= h >> 13;
    6.76 +    h *= m;
    6.77 +    h ^= h >> 15;
    6.78 +
    6.79 +    key->hash = h;
    6.80 +}
    6.81 +
    6.82 +CxHashKey cx_hash_key_str(char const *str) {
    6.83 +    CxHashKey key;
    6.84 +    key.data.cstr = str;
    6.85 +    key.len = str == NULL ? 0 : (1 + strlen(str));
    6.86 +    cx_hash_murmur(&key);
    6.87 +    return key;
    6.88 +}
    6.89 +
    6.90 +CxHashKey cx_hash_key_bytes(
    6.91 +        unsigned char const *bytes,
    6.92 +        size_t len
    6.93 +) {
    6.94 +    CxHashKey key;
    6.95 +    key.data.cbytes = bytes;
    6.96 +    key.len = len;
    6.97 +    cx_hash_murmur(&key);
    6.98 +    return key;
    6.99 +}
   6.100 +
   6.101 +CxHashKey cx_hash_key(
   6.102 +        void *obj,
   6.103 +        size_t len
   6.104 +) {
   6.105 +    CxHashKey key;
   6.106 +    key.data.obj = obj;
   6.107 +    key.len = len;
   6.108 +    cx_hash_murmur(&key);
   6.109 +    return key;
   6.110 +}
     7.1 --- a/src/hash_map.c	Fri May 27 17:40:27 2022 +0200
     7.2 +++ b/src/hash_map.c	Wed Jun 08 21:33:31 2022 +0200
     7.3 @@ -30,60 +30,6 @@
     7.4  #include "cx/hash_map.h"
     7.5  #include "cx/utils.h"
     7.6  
     7.7 -/**
     7.8 - * Computes a murmur hash-2.
     7.9 - *
    7.10 - * @param data the data to hash
    7.11 - * @param len the length of the data
    7.12 - * @return the murmur hash-2 of the data
    7.13 - */
    7.14 -static unsigned cx_hash_map_murmur(
    7.15 -        unsigned char const *data,
    7.16 -        size_t len
    7.17 -) {
    7.18 -    unsigned m = 0x5bd1e995;
    7.19 -    unsigned r = 24;
    7.20 -    unsigned h = 25 ^ len;
    7.21 -    unsigned i = 0;
    7.22 -    while (len >= 4) {
    7.23 -        unsigned k = data[i + 0] & 0xFF;
    7.24 -        k |= (data[i + 1] & 0xFF) << 8;
    7.25 -        k |= (data[i + 2] & 0xFF) << 16;
    7.26 -        k |= (data[i + 3] & 0xFF) << 24;
    7.27 -
    7.28 -        k *= m;
    7.29 -        k ^= k >> r;
    7.30 -        k *= m;
    7.31 -
    7.32 -        h *= m;
    7.33 -        h ^= k;
    7.34 -
    7.35 -        i += 4;
    7.36 -        len -= 4;
    7.37 -    }
    7.38 -
    7.39 -    switch (len) {
    7.40 -        case 3:
    7.41 -            h ^= (data[i + 2] & 0xFF) << 16;
    7.42 -                    __attribute__((__fallthrough__));
    7.43 -        case 2:
    7.44 -            h ^= (data[i + 1] & 0xFF) << 8;
    7.45 -                    __attribute__((__fallthrough__));
    7.46 -        case 1:
    7.47 -            h ^= (data[i + 0] & 0xFF);
    7.48 -            h *= m;
    7.49 -                    __attribute__((__fallthrough__));
    7.50 -        default:
    7.51 -            /* do nothing */;
    7.52 -    }
    7.53 -
    7.54 -    h ^= h >> 13;
    7.55 -    h *= m;
    7.56 -    h ^= h >> 15;
    7.57 -
    7.58 -    return h;
    7.59 -}
    7.60 -
    7.61  static void cx_hash_map_clear(struct cx_map_s *map) {
    7.62      struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
    7.63      cx_for_n(i, hash_map->bucket_count) {
    7.64 @@ -92,7 +38,7 @@
    7.65              do {
    7.66                  struct cx_hash_map_element_s *next = elem->next;
    7.67                  // free the key data
    7.68 -                cxFree(map->allocator, elem->key.data);
    7.69 +                cxFree(map->allocator, elem->key.data.obj);
    7.70                  // free the node
    7.71                  cxFree(map->allocator, elem);
    7.72                  // proceed
    7.73 @@ -119,13 +65,17 @@
    7.74  
    7.75  static int cx_hash_map_put(
    7.76          CxMap *map,
    7.77 -        CxDataPtr key,
    7.78 +        CxHashKey key,
    7.79          void *value
    7.80  ) {
    7.81      struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
    7.82      CxAllocator *allocator = map->allocator;
    7.83  
    7.84 -    unsigned hash = cx_hash_map_murmur(key.data, key.len);
    7.85 +    unsigned hash = key.hash;
    7.86 +    if (hash == 0) {
    7.87 +        cx_hash_murmur(&key);
    7.88 +        hash = key.hash;
    7.89 +    }
    7.90  
    7.91      size_t slot = hash % hash_map->bucket_count;
    7.92      struct cx_hash_map_element_s *elm = hash_map->buckets[slot];
    7.93 @@ -151,8 +101,8 @@
    7.94          if (kd == NULL) {
    7.95              return -1;
    7.96          }
    7.97 -        memcpy(kd, key.data, key.len);
    7.98 -        e->key.data = kd;
    7.99 +        memcpy(kd, key.data.obj, key.len);
   7.100 +        e->key.data.obj = kd;
   7.101          e->key.len = key.len;
   7.102          e->key.hash = hash;
   7.103  
   7.104 @@ -187,7 +137,7 @@
   7.105          prev->next = elm->next;
   7.106      }
   7.107      // free element
   7.108 -    cxFree(hash_map->base.allocator, elm->key.data);
   7.109 +    cxFree(hash_map->base.allocator, elm->key.data.obj);
   7.110      cxFree(hash_map->base.allocator, elm);
   7.111      // decrease size
   7.112      hash_map->base.size--;
   7.113 @@ -203,19 +153,23 @@
   7.114   */
   7.115  static void *cx_hash_map_get_remove(
   7.116          CxMap *map,
   7.117 -        CxDataPtr key,
   7.118 +        CxHashKey key,
   7.119          bool remove
   7.120  ) {
   7.121      struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
   7.122  
   7.123 -    unsigned hash = cx_hash_map_murmur(key.data, key.len);
   7.124 +    unsigned hash = key.hash;
   7.125 +    if (hash == 0) {
   7.126 +        cx_hash_murmur(&key);
   7.127 +        hash = key.hash;
   7.128 +    }
   7.129  
   7.130      size_t slot = hash % hash_map->bucket_count;
   7.131      struct cx_hash_map_element_s *elm = hash_map->buckets[slot];
   7.132      struct cx_hash_map_element_s *prev = NULL;
   7.133      while (elm && elm->key.hash <= hash) {
   7.134          if (elm->key.hash == hash && elm->key.len == key.len) {
   7.135 -            if (memcmp(elm->key.data, key.data, key.len) == 0) {
   7.136 +            if (memcmp(elm->key.data.obj, key.data.obj, key.len) == 0) {
   7.137                  void *data = elm->data;
   7.138                  if (remove) {
   7.139                      cx_hash_map_unlink(hash_map, slot, prev, elm);
   7.140 @@ -232,7 +186,7 @@
   7.141  
   7.142  static void *cx_hash_map_get(
   7.143          CxMap const *map,
   7.144 -        CxDataPtr key
   7.145 +        CxHashKey key
   7.146  ) {
   7.147      // we can safely cast, because we know when remove=false, the map stays untouched
   7.148      return cx_hash_map_get_remove((CxMap *) map, key, false);
   7.149 @@ -240,7 +194,7 @@
   7.150  
   7.151  static void *cx_hash_map_remove(
   7.152          CxMap *map,
   7.153 -        CxDataPtr key
   7.154 +        CxHashKey key
   7.155  ) {
   7.156      return cx_hash_map_get_remove(map, key, true);
   7.157  }
     8.1 --- a/src/map.c	Fri May 27 17:40:27 2022 +0200
     8.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.3 @@ -1,35 +0,0 @@
     8.4 -/*
     8.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     8.6 - *
     8.7 - * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
     8.8 - *
     8.9 - * Redistribution and use in source and binary forms, with or without
    8.10 - * modification, are permitted provided that the following conditions are met:
    8.11 - *
    8.12 - *   1. Redistributions of source code must retain the above copyright
    8.13 - *      notice, this list of conditions and the following disclaimer.
    8.14 - *
    8.15 - *   2. Redistributions in binary form must reproduce the above copyright
    8.16 - *      notice, this list of conditions and the following disclaimer in the
    8.17 - *      documentation and/or other materials provided with the distribution.
    8.18 - *
    8.19 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    8.20 - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    8.21 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    8.22 - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    8.23 - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    8.24 - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    8.25 - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    8.26 - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    8.27 - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    8.28 - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    8.29 - * POSSIBILITY OF SUCH DAMAGE.
    8.30 - */
    8.31 -
    8.32 -#include <string.h>
    8.33 -#include "cx/map.h"
    8.34 -
    8.35 -CxDataPtr cxMapKeyStr(char const *str) {
    8.36 -    CxDataPtr key = {(unsigned char const *) str, 1 + strlen(str)};
    8.37 -    return key;
    8.38 -}
    8.39 \ No newline at end of file
     9.1 --- a/test/test_map.cpp	Fri May 27 17:40:27 2022 +0200
     9.2 +++ b/test/test_map.cpp	Wed Jun 08 21:33:31 2022 +0200
     9.3 @@ -72,9 +72,9 @@
     9.4      {
     9.5          auto keyiter = cxMapIteratorKeys(map);
     9.6          std::unordered_set<std::string> keys;
     9.7 -        cx_foreach(CxDataPtr*, elem, keyiter) {
     9.8 +        cx_foreach(CxHashKey*, elem, keyiter) {
     9.9              // we use that our test keys contain NULL-terminated strings
    9.10 -            keys.insert(std::string(reinterpret_cast<const char *>(elem->data)));
    9.11 +            keys.insert(std::string(elem->data.cstr));
    9.12          }
    9.13          EXPECT_EQ(keyiter.index, map->size);
    9.14          ASSERT_EQ(keys.size(), map->size);
    9.15 @@ -103,7 +103,7 @@
    9.16          auto pairiter = cxMapIterator(map);
    9.17          std::unordered_map<std::string, std::string> pairs;
    9.18          cx_foreach(CxMapEntry*, entry, pairiter) {
    9.19 -            pairs[std::string((char const *) entry->key->data)] = std::string((char *) entry->value);
    9.20 +            pairs[std::string(entry->key->data.cstr)] = std::string((char *) entry->value);
    9.21          }
    9.22          EXPECT_EQ(pairiter.index, map->size);
    9.23          ASSERT_EQ(pairs.size(), refmap.size());
    9.24 @@ -144,7 +144,8 @@
    9.25  
    9.26      // execute operations and verify results
    9.27      for (auto &&op: ops) {
    9.28 -        CxDataPtr key = cxMapKeyStr(op.key);
    9.29 +        CxHashKey key = cx_hash_key_str(op.key);
    9.30 +        key.hash = 0; // force the hash map to compute the hash
    9.31          if (op.op == map_operation::put) {
    9.32              // execute a put operation and verify that the exact value can be read back
    9.33              refmap[std::string(op.key)] = std::string(op.value);
    9.34 @@ -176,26 +177,26 @@
    9.35      CxTestingAllocator allocator;
    9.36      auto map = cxHashMapCreate(&allocator, 4);
    9.37  
    9.38 -    cxMapPut(map, cxMapKeyStr("key 1"), (void *) "val 1");
    9.39 -    cxMapPut(map, cxMapKeyStr("key 2"), (void *) "val 2");
    9.40 -    cxMapPut(map, cxMapKeyStr("key 3"), (void *) "val 3");
    9.41 -    cxMapPut(map, cxMapKeyStr("key 4"), (void *) "val 4");
    9.42 -    cxMapPut(map, cxMapKeyStr("key 5"), (void *) "val 5");
    9.43 -    cxMapPut(map, cxMapKeyStr("key 6"), (void *) "val 6");
    9.44 +    cxMapPut(map, cx_hash_key_str("key 1"), (void *) "val 1");
    9.45 +    cxMapPut(map, cx_hash_key_str("key 2"), (void *) "val 2");
    9.46 +    cxMapPut(map, cx_hash_key_str("key 3"), (void *) "val 3");
    9.47 +    cxMapPut(map, cx_hash_key_str("key 4"), (void *) "val 4");
    9.48 +    cxMapPut(map, cx_hash_key_str("key 5"), (void *) "val 5");
    9.49 +    cxMapPut(map, cx_hash_key_str("key 6"), (void *) "val 6");
    9.50  
    9.51      auto iter = cxMapIterator(map);
    9.52      cx_foreach(CxMapEntry*, entry, iter) {
    9.53 -        if (entry->key->data[4] % 2 == 1) iter.remove = true;
    9.54 +        if (entry->key->data.cstr[4] % 2 == 1) iter.remove = true;
    9.55      }
    9.56      EXPECT_EQ(map->size, 3);
    9.57      EXPECT_EQ(iter.index, map->size);
    9.58  
    9.59 -    EXPECT_EQ(cxMapGet(map, cxMapKeyStr("key 1")), nullptr);
    9.60 -    EXPECT_NE(cxMapGet(map, cxMapKeyStr("key 2")), nullptr);
    9.61 -    EXPECT_EQ(cxMapGet(map, cxMapKeyStr("key 3")), nullptr);
    9.62 -    EXPECT_NE(cxMapGet(map, cxMapKeyStr("key 4")), nullptr);
    9.63 -    EXPECT_EQ(cxMapGet(map, cxMapKeyStr("key 5")), nullptr);
    9.64 -    EXPECT_NE(cxMapGet(map, cxMapKeyStr("key 6")), nullptr);
    9.65 +    EXPECT_EQ(cxMapGet(map, cx_hash_key_str("key 1")), nullptr);
    9.66 +    EXPECT_NE(cxMapGet(map, cx_hash_key_str("key 2")), nullptr);
    9.67 +    EXPECT_EQ(cxMapGet(map, cx_hash_key_str("key 3")), nullptr);
    9.68 +    EXPECT_NE(cxMapGet(map, cx_hash_key_str("key 4")), nullptr);
    9.69 +    EXPECT_EQ(cxMapGet(map, cx_hash_key_str("key 5")), nullptr);
    9.70 +    EXPECT_NE(cxMapGet(map, cx_hash_key_str("key 6")), nullptr);
    9.71  
    9.72      cxMapDestroy(map);
    9.73      EXPECT_TRUE(allocator.verify());
    9.74 @@ -205,12 +206,12 @@
    9.75      CxTestingAllocator allocator;
    9.76      auto map = cxHashMapCreate(&allocator, 8);
    9.77  
    9.78 -    cxMapPut(map, cxMapKeyStr("key 1"), (void *) "val 1");
    9.79 -    cxMapPut(map, cxMapKeyStr("key 2"), (void *) "val 2");
    9.80 -    cxMapPut(map, cxMapKeyStr("key 3"), (void *) "val 3");
    9.81 -    cxMapPut(map, cxMapKeyStr("key 4"), (void *) "val 4");
    9.82 -    cxMapPut(map, cxMapKeyStr("key 5"), (void *) "val 5");
    9.83 -    cxMapPut(map, cxMapKeyStr("key 6"), (void *) "val 6");
    9.84 +    cxMapPut(map, cx_hash_key_str("key 1"), (void *) "val 1");
    9.85 +    cxMapPut(map, cx_hash_key_str("key 2"), (void *) "val 2");
    9.86 +    cxMapPut(map, cx_hash_key_str("key 3"), (void *) "val 3");
    9.87 +    cxMapPut(map, cx_hash_key_str("key 4"), (void *) "val 4");
    9.88 +    cxMapPut(map, cx_hash_key_str("key 5"), (void *) "val 5");
    9.89 +    cxMapPut(map, cx_hash_key_str("key 6"), (void *) "val 6");
    9.90  
    9.91      // 6/8 does not exceed 0.75, therefore the function should not rehash
    9.92      int result = cxMapRehash(map);
    9.93 @@ -225,26 +226,26 @@
    9.94      CxTestingAllocator allocator;
    9.95      auto map = cxHashMapCreate(&allocator, 8);
    9.96  
    9.97 -    cxMapPut(map, cxMapKeyStr("key 1"), (void *) "val 1");
    9.98 -    cxMapPut(map, cxMapKeyStr("key 2"), (void *) "val 2");
    9.99 -    cxMapPut(map, cxMapKeyStr("key 3"), (void *) "val 3");
   9.100 -    cxMapPut(map, cxMapKeyStr("key 4"), (void *) "val 4");
   9.101 -    cxMapPut(map, cxMapKeyStr("key 5"), (void *) "val 5");
   9.102 -    cxMapPut(map, cxMapKeyStr("key 6"), (void *) "val 6");
   9.103 -    cxMapPut(map, cxMapKeyStr("key 7"), (void *) "val 7");
   9.104 +    cxMapPut(map, cx_hash_key_str("key 1"), (void *) "val 1");
   9.105 +    cxMapPut(map, cx_hash_key_str("key 2"), (void *) "val 2");
   9.106 +    cxMapPut(map, cx_hash_key_str("key 3"), (void *) "val 3");
   9.107 +    cxMapPut(map, cx_hash_key_str("key 4"), (void *) "val 4");
   9.108 +    cxMapPut(map, cx_hash_key_str("key 5"), (void *) "val 5");
   9.109 +    cxMapPut(map, cx_hash_key_str("key 6"), (void *) "val 6");
   9.110 +    cxMapPut(map, cx_hash_key_str("key 7"), (void *) "val 7");
   9.111  
   9.112      int result = cxMapRehash(map);
   9.113      EXPECT_EQ(result, 0);
   9.114      EXPECT_EQ(reinterpret_cast<struct cx_hash_map_s *>(map)->bucket_count, 17);
   9.115      EXPECT_EQ(map->size, 7);
   9.116  
   9.117 -    EXPECT_EQ(strcmp((char *) cxMapGet(map, cxMapKeyStr("key 1")), "val 1"), 0);
   9.118 -    EXPECT_EQ(strcmp((char *) cxMapGet(map, cxMapKeyStr("key 2")), "val 2"), 0);
   9.119 -    EXPECT_EQ(strcmp((char *) cxMapGet(map, cxMapKeyStr("key 3")), "val 3"), 0);
   9.120 -    EXPECT_EQ(strcmp((char *) cxMapGet(map, cxMapKeyStr("key 4")), "val 4"), 0);
   9.121 -    EXPECT_EQ(strcmp((char *) cxMapGet(map, cxMapKeyStr("key 5")), "val 5"), 0);
   9.122 -    EXPECT_EQ(strcmp((char *) cxMapGet(map, cxMapKeyStr("key 6")), "val 6"), 0);
   9.123 -    EXPECT_EQ(strcmp((char *) cxMapGet(map, cxMapKeyStr("key 7")), "val 7"), 0);
   9.124 +    EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 1")), "val 1"), 0);
   9.125 +    EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 2")), "val 2"), 0);
   9.126 +    EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 3")), "val 3"), 0);
   9.127 +    EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 4")), "val 4"), 0);
   9.128 +    EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 5")), "val 5"), 0);
   9.129 +    EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 6")), "val 6"), 0);
   9.130 +    EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 7")), "val 7"), 0);
   9.131  
   9.132      cxMapDestroy(map);
   9.133      EXPECT_TRUE(allocator.verify());

mercurial