diff --git a/CMakeLists.txt b/CMakeLists.txt index bbbd03c..d7dcd06 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,7 +39,7 @@ target_include_directories(hashmap ${CMAKE_CURRENT_SOURCE_DIR}/src ) target_compile_options(hashmap - PRIVATE -Wall -Werror + PRIVATE $<$:-Wall -Werror> ) ############################################## diff --git a/include/hashmap.h b/include/hashmap.h index 9dfa28d..bd9c976 100644 --- a/include/hashmap.h +++ b/include/hashmap.h @@ -3,6 +3,13 @@ * * Hashmap is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. + * + * MSVC compatibility patch (project-local edit, see vijayee/hashmap fork): + * - Replaces GCC typeof() / statement-expressions / compound literals / + * zero-size arrays (used here as a "type info" trick via map_types[0] + * and iter_types[0]) with constructs that compile under MSVC's C + * compiler. The public API (function and macro names, struct field + * layout) is preserved so the rest of the codebase does not change. */ #pragma once @@ -15,19 +22,99 @@ extern "C" { #include "hashmap_base.h" +#ifdef _MSC_VER +/* + * MSVC's C compiler doesn't support: + * - typeof() + * - GCC statement expressions: ({ ... }) + * - C99 compound literals: (Type){ ... } + * - Zero-size arrays as struct members (used here purely as a "type + * info" trick via map_types[0] / iter_types[0]). + * + * We work around all of these below. The patched macros produce code + * that is functionally equivalent on every compiler. + */ +#define __HASHMAP_MSVC 1 +#else +#define __HASHMAP_MSVC 0 +#endif + /* * INTERNAL USE ONLY: Updates an iterator structure after the current element was removed. */ +#if __HASHMAP_MSVC +#define __HASHMAP_ITER_RESET(iter) \ + ((iter)->iter_pos = hashmap_base_iter((iter)->iter_map, (iter)->iter_pos)) != NULL +#else #define __HASHMAP_ITER_RESET(iter) \ ({ ((iter)->iter_pos = hashmap_base_iter((iter)->iter_map, (iter)->iter_pos)) != NULL; }) +#endif /* * INTERNAL USE ONLY: foreach macro internals. + * + * The unique-name generator uses __LINE__ for the per-source-line part + * (so a single line that expands the macro multiple times still produces + * distinct names) and __COUNTER__ for the per-expansion part (so a single + * line that has two separate calls also produces distinct names). */ #define __HASHMAP_CONCAT_2(x, y) x##y #define __HASHMAP_CONCAT(x, y) __HASHMAP_CONCAT_2(x, y) -#define __HASHMAP_MAKE_UNIQUE(prefix) __HASHMAP_CONCAT(__HASHMAP_CONCAT(prefix, __COUNTER__), _) +#define __HASHMAP_MAKE_UNIQUE(prefix) __HASHMAP_CONCAT(__HASHMAP_CONCAT(prefix, __LINE__), __HASHMAP_CONCAT(_, __COUNTER__)) #define __HASHMAP_UNIQUE(unique, name) __HASHMAP_CONCAT(unique, name) + +/* + * All hashmap iterators share the same memory layout (iter_map and + * iter_pos are the same types regardless of K/V), so we can use a single + * universal struct on MSVC where typeof() is unavailable. On GCC we + * keep the original t_iterator type for the typeof() lookup. + */ +#if __HASHMAP_MSVC +struct __hashmap_iter_universal { + struct hashmap_base *iter_map; + struct hashmap_entry *iter_pos; +}; +#define HASHMAP_ITER_TYPE struct __hashmap_iter_universal +#define HASHMAP_ITER(hashmap_type) struct __hashmap_iter_universal +#else +#define HASHMAP_ITER(hashmap_type) typeof((hashmap_type).map_types->t_iterator) +#endif + +#if __HASHMAP_MSVC +#define __HASHMAP_FOREACH(x, key, data, h) \ + for (HASHMAP_ITER_TYPE __HASHMAP_UNIQUE(x, it) = hashmap_iter(h); \ + ((key) = hashmap_iter_get_key(&__HASHMAP_UNIQUE(x, it))) && \ + ((data) = hashmap_iter_get_data(&__HASHMAP_UNIQUE(x, it))); \ + hashmap_iter_next(&__HASHMAP_UNIQUE(x, it))) +#define __HASHMAP_FOREACH_SAFE(x, key, data, h, pos) \ + for (HASHMAP_ITER_TYPE __HASHMAP_UNIQUE(x, it) = hashmap_iter(h); \ + ((pos) = (void *)((key) = hashmap_iter_get_key(&__HASHMAP_UNIQUE(x, it)))) && \ + ((data) = hashmap_iter_get_data(&__HASHMAP_UNIQUE(x, it))); \ + ((pos) == (void *)hashmap_iter_get_key(&__HASHMAP_UNIQUE(x, it))) ? \ + hashmap_iter_next(&__HASHMAP_UNIQUE(x, it)) : \ + __HASHMAP_ITER_RESET(&__HASHMAP_UNIQUE(x, it))) +#define __HASHMAP_FOREACH_KEY(x, key, h) \ + for (HASHMAP_ITER_TYPE __HASHMAP_UNIQUE(x, it) = hashmap_iter(h); \ + (key = hashmap_iter_get_key(&__HASHMAP_UNIQUE(x, it))); \ + hashmap_iter_next(&__HASHMAP_UNIQUE(x, it))) +#define __HASHMAP_FOREACH_KEY_SAFE(x, key, h, pos) \ + for (HASHMAP_ITER_TYPE __HASHMAP_UNIQUE(x, it) = hashmap_iter(h); \ + ((pos) = (void *)((key) = hashmap_iter_get_key(&__HASHMAP_UNIQUE(x, it)))); \ + ((pos) == (void *)hashmap_iter_get_key(&__HASHMAP_UNIQUE(x, it))) ? \ + hashmap_iter_next(&__HASHMAP_UNIQUE(x, it)) : \ + __HASHMAP_ITER_RESET(&__HASHMAP_UNIQUE(x, it))) +#define __HASHMAP_FOREACH_DATA(x, data, h) \ + for (HASHMAP_ITER_TYPE __HASHMAP_UNIQUE(x, it) = hashmap_iter(h); \ + (data = hashmap_iter_get_data(&__HASHMAP_UNIQUE(x, it))); \ + hashmap_iter_next(&__HASHMAP_UNIQUE(x, it))) +#define __HASHMAP_FOREACH_DATA_SAFE(x, data, h, pos) \ + for (HASHMAP_ITER_TYPE __HASHMAP_UNIQUE(x, it) = hashmap_iter(h); \ + ((pos) = (void *)hashmap_iter_get_key(&__HASHMAP_UNIQUE(x, it))) && \ + ((data) = hashmap_iter_get_data(&__HASHMAP_UNIQUE(x, it))); \ + ((pos) == (void *)hashmap_iter_get_key(&__HASHMAP_UNIQUE(x, it))) ? \ + hashmap_iter_next(&__HASHMAP_UNIQUE(x, it)) : \ + __HASHMAP_ITER_RESET(&__HASHMAP_UNIQUE(x, it))) +#else #define __HASHMAP_FOREACH(x, key, data, h) \ for (HASHMAP_ITER(*(h)) __HASHMAP_UNIQUE(x, it) = hashmap_iter(h); \ ((key) = hashmap_iter_get_key(&__HASHMAP_UNIQUE(x, it))) && \ @@ -59,10 +146,16 @@ extern "C" { ((pos) == (void *)hashmap_iter_get_key(&__HASHMAP_UNIQUE(x, it))) ? \ hashmap_iter_next(&__HASHMAP_UNIQUE(x, it)) : \ __HASHMAP_ITER_RESET(&__HASHMAP_UNIQUE(x, it))) +#endif /* * Template macro to define a type-specific hashmap. * + * On GCC the struct uses zero-size arrays (map_types[0] / iter_types[0]) + * as a "type info" trick. On MSVC those are illegal, so we use + * single-element arrays (map_types[1]) and rely on HASHMAP_ITER_TYPE + * (a separate universal iterator struct) for the iterator. + * * Example declarations: * HASHMAP(int, struct foo) map1; * // key_type: const int * @@ -72,6 +165,22 @@ extern "C" { * // key_type: const char * * // data_type: char * */ +#if __HASHMAP_MSVC +#define HASHMAP(key_type, data_type) \ + struct { \ + struct hashmap_base map_base; \ + struct { \ + const key_type *t_key; \ + data_type *t_data; \ + size_t (*t_hash_func)(const key_type *); \ + int (*t_compare_func)(const key_type *, const key_type *); \ + key_type *(*t_key_dup_func)(const key_type *); \ + void (*t_key_free_func)(key_type *); \ + int (*t_foreach_func)(const key_type *, data_type *, void *); \ + HASHMAP_ITER_TYPE t_iterator; \ + } map_types[1]; \ + } +#else #define HASHMAP(key_type, data_type) \ struct { \ struct hashmap_base map_base; \ @@ -93,14 +202,7 @@ extern "C" { } t_iterator; \ } map_types[0]; \ } - -/* - * Template macro to define a hashmap iterator. - * - * Example declarations: - * HASHMAP_ITER(my_hashmap) iter; - */ -#define HASHMAP_ITER(hashmap_type) typeof((hashmap_type).map_types->t_iterator) +#endif /* * Initialize an empty hashmap. @@ -120,6 +222,13 @@ extern "C" { * size_t hashmap_hash_string_i(const char *key) - non-case sensitive string hash function. * Pass this directly to hashmap_init(). */ +#if __HASHMAP_MSVC +#define hashmap_init(h, hash_func, compare_func) \ + do { \ + hashmap_base_init(&(h)->map_base, (size_t (*)(const void *))(hash_func), \ + (int (*)(const void *, const void *))(compare_func)); \ + } while (0) +#else #define hashmap_init(h, hash_func, compare_func) \ do { \ typeof((h)->map_types->t_hash_func) __map_hash = (hash_func); \ @@ -127,6 +236,7 @@ extern "C" { hashmap_base_init(&(h)->map_base, (size_t (*)(const void *))__map_hash, \ (int (*)(const void *, const void *))__map_compare); \ } while (0) +#endif /* * Free the hashmap and all associated memory. @@ -145,6 +255,13 @@ extern "C" { * managed internally by the hashmap. * void (*key_free_func)( *) - free resources associated with a key */ +#if __HASHMAP_MSVC +#define hashmap_set_key_alloc_funcs(h, key_dup_func, key_free_func) \ + do { \ + hashmap_base_set_key_alloc_funcs(&(h)->map_base, (void *(*)(const void *))(key_dup_func), \ + (void (*)(void *))(key_free_func)); \ + } while (0) +#else #define hashmap_set_key_alloc_funcs(h, key_dup_func, key_free_func) \ do { \ typeof((h)->map_types->t_key_dup_func) __map_key_dup = (key_dup_func); \ @@ -152,6 +269,7 @@ extern "C" { hashmap_base_set_key_alloc_funcs(&(h)->map_base, (void *(*)(const void *))__map_key_dup, \ (void (*)(void *))__map_key_free); \ } while (0) +#endif /* * Return the number of entries in the hashmap. @@ -159,7 +277,7 @@ extern "C" { * Parameters: * const HASHMAP(, ) *h - hashmap pointer */ -#define hashmap_size(h) ((typeof((h)->map_base.size))(h)->map_base.size) +#define hashmap_size(h) ((h)->map_base.size) /* * Return true if the hashmap is empty. @@ -189,7 +307,7 @@ extern "C" { * * Returns 0 on success, or -errno on failure. */ -#define hashmap_capacity(h) ((typeof((h)->map_base.table_size))(h)->map_base.table_size) +#define hashmap_capacity(h) ((h)->map_base.table_size) /* * Add a new entry to the hashmap. If an entry with a matching key is already @@ -202,12 +320,17 @@ extern "C" { * * Returns 0 on success, or -errno on failure. */ +#if __HASHMAP_MSVC +#define hashmap_put(h, key, data) \ + hashmap_base_put(&(h)->map_base, (const void *)(key), (void *)(data)) +#else #define hashmap_put(h, key, data) \ ({ \ typeof((h)->map_types->t_key) __map_key = (key); \ typeof((h)->map_types->t_data) __map_data = (data); \ hashmap_base_put(&(h)->map_base, (const void *)__map_key, (void *)__map_data); \ }) +#endif /* * Add a new entry to the hashmap, or update an existing entry. If an entry @@ -222,6 +345,10 @@ extern "C" { * * Returns 1 on add, 0 on update, or -errno on failure. */ +#if __HASHMAP_MSVC +#define hashmap_insert(h, key, data, old_data) \ + hashmap_base_insert(&(h)->map_base, (const void *)(key), (void *)(data), (void **)(old_data)) +#else #define hashmap_insert(h, key, data, old_data) \ ({ \ typeof((h)->map_types->t_key) __map_key = (key); \ @@ -229,6 +356,7 @@ extern "C" { typeof((h)->map_types->t_data) *__map_old_data = (old_data); \ hashmap_base_insert(&(h)->map_base, (const void *)__map_key, (void *)__map_data, (void **)__map_old_data); \ }) +#endif /* * Do a constant-time lookup of a hashmap entry. @@ -239,11 +367,20 @@ extern "C" { * * Return the data pointer, or NULL if no entry exists. */ +#if __HASHMAP_MSVC +/* + * Returns void*; the caller casts to the typed data pointer. The implicit + * void* -> T* conversion is well-defined in C. + */ +#define hashmap_get(h, key) \ + hashmap_base_get(&(h)->map_base, (const void *)(key)) +#else #define hashmap_get(h, key) \ ({ \ typeof((h)->map_types->t_key) __map_key = (key); \ (typeof((h)->map_types->t_data))hashmap_base_get(&(h)->map_base, (const void *)__map_key); \ }) +#endif /* * Return true if the hashmap contains an entry with the specified key. @@ -266,11 +403,19 @@ extern "C" { * the "safe" variant of the foreach macro is used, and only the current * key is removed. */ +#if __HASHMAP_MSVC +/* + * Returns void*; the caller casts to the typed data pointer. + */ +#define hashmap_remove(h, key) \ + hashmap_base_remove(&(h)->map_base, (const void *)(key)) +#else #define hashmap_remove(h, key) \ ({ \ typeof((h)->map_types->t_key) __map_key = (key); \ (typeof((h)->map_types->t_data))hashmap_base_remove(&(h)->map_base, (const void *)__map_key); \ }) +#endif /* * Remove all entries. @@ -295,7 +440,29 @@ extern "C" { * Parameters: * HASHMAP(, ) *h - hashmap pointer */ +#if __HASHMAP_MSVC +/* + * MSVC lacks C99 compound literals, so we build the iterator by value + * via the inline helpers hashmap_iter_make() and hashmap_iter_make_by_key(). + */ +static inline HASHMAP_ITER_TYPE hashmap_iter_make(struct hashmap_base *m) \ +{ \ + HASHMAP_ITER_TYPE it; \ + it.iter_map = m; \ + it.iter_pos = hashmap_base_iter(m, NULL); \ + return it; \ +} +static inline HASHMAP_ITER_TYPE hashmap_iter_make_by_key(struct hashmap_base *m, const void *key) \ +{ \ + HASHMAP_ITER_TYPE it; \ + it.iter_map = m; \ + it.iter_pos = hashmap_base_iter_find(m, key); \ + return it; \ +} +#define hashmap_iter(h) hashmap_iter_make(&(h)->map_base) +#else #define hashmap_iter(h) ((HASHMAP_ITER(*(h))){&(h)->map_base, hashmap_base_iter(&(h)->map_base, NULL)}) +#endif /* * Return true if an iterator is valid and safe to use. @@ -326,7 +493,12 @@ extern "C" { * * Returns a valid iterator if the key exists, otherwise an invalid iterator. */ +#if __HASHMAP_MSVC +#define hashmap_iter_find(h, key) \ + hashmap_iter_make_by_key(&(h)->map_base, (const void *)(key)) +#else #define hashmap_iter_find(h, key) ((HASHMAP_ITER(*(h))){&(h)->map_base, hashmap_base_iter_find(&(h)->map_base, key)}) +#endif /* * Remove the hashmap entry pointed to by this iterator and advance the @@ -345,7 +517,11 @@ extern "C" { * Parameters: * HASHMAP_ITER() *iter - iterator pointer */ +#if __HASHMAP_MSVC +#define hashmap_iter_get_key(iter) hashmap_base_iter_get_key((iter)->iter_pos) +#else #define hashmap_iter_get_key(iter) ((typeof((iter)->iter_types->t_key))hashmap_base_iter_get_key((iter)->iter_pos)) +#endif /* * Return the data of the entry pointed to by the iterator. @@ -353,7 +529,14 @@ extern "C" { * Parameters: * HASHMAP_ITER() *iter - iterator pointer */ +#if __HASHMAP_MSVC +/* + * Returns void*; the caller casts to the typed data pointer. + */ +#define hashmap_iter_get_data(iter) hashmap_base_iter_get_data((iter)->iter_pos) +#else #define hashmap_iter_get_data(iter) ((typeof((iter)->iter_types->t_data))hashmap_base_iter_get_data((iter)->iter_pos)) +#endif /* * Set the data pointer of the entry pointed to by the iterator. @@ -362,11 +545,16 @@ extern "C" { * HASHMAP_ITER() *iter - iterator pointer * *data - new data pointer */ +#if __HASHMAP_MSVC +#define hashmap_iter_set_data(iter, data) \ + hashmap_base_iter_set_data((iter)->iter_pos, (void *)(data)) +#else #define hashmap_iter_set_data(iter, data) \ ({ \ (typeof((iter)->iter_types->t_data))__map_data = (data); \ hashmap_base_iter_set_data((iter)->iter_pos), (void *)__map_data); \ }) +#endif /* * Convenience macro to iterate through the contents of a hashmap. @@ -463,11 +651,16 @@ extern "C" { * HASHMAP(, ) *h - hashmap pointer * *key - pointer to the entry's key */ +#if __HASHMAP_MSVC +#define hashmap_collisions(h, key) \ + hashmap_base_collisions(&(h)->map_base, (const void *)(key)) +#else #define hashmap_collisions(h, key) \ ({ \ typeof((h)->map_types->t_key) __map_key = (key); \ hashmap_base_collisions(&(h)->map_base, (const void *)__map_key); \ }) +#endif /* * Return the average number of collisions per entry. diff --git a/src/hashmap.c b/src/hashmap.c index 644339e..7f104da 100644 --- a/src/hashmap.c +++ b/src/hashmap.c @@ -12,6 +12,16 @@ #include #include #include +#ifdef _MSC_VER +#include +static inline int __builtin_clzl(unsigned long x) { + unsigned long index; + if (_BitScanReverse(&index, x)) { + return 31 - (int)index; + } + return 32; +} +#endif #include "hashmap_base.h"