X_HASHTABLE_VERSION_MAJOR
#define X_HASHTABLE_VERSION_MAJOR 1
STDX - Generic Hashtable
Part of the STDX General Purpose C Library by marciovmf
License: MIT
https://github.com/marciovmf/stdx
Provides a generic, type-agnostic hashtable implementation with
customizable hash and equality functions. Supports arbitrary key
and value types, optional custom allocators, and built-in iteration.
Includes helpers for common cases like string keys.
To compile the implementation define XIMPLHASHTABLE
in one source file before including this header.
To customize how this module allocates memory, define
XHASHTABLEALLOC / XHASHTABLEREALLOC / XHASHTABLEFREE before including.
The hashtable core operates on void* and explicit key/value sizes.
For convenience, this header also provides macros that generate
type-safe wrappers on top of the generic API.
These macros create:
XHashtablecreate, set, get, has, remove, count)This removes the need for manual casts and makes the API easier to use.
X_HASHTABLE_TYPE(int32_t, float)
XHashtable_int32_t_float* ht = x_hashtable_int32_t_float_create();
x_hashtable_int32_t_float_set(ht, 10, 2.5f);
float value;
if (x_hashtable_int32_t_float_get(ht, 10, &value))
{
printf("value = %f\n", value);
}
If the key or value type is not a valid identifier (for example
char*, unsigned int, or complex types), use the named version:
X_HASHTABLE_TYPE_NAMED(char*, int32_t, cstr_i32)
XHashtable_cstr_i32* ht = x_hashtable_cstr_i32_create();
Some key/value combinations require explicit semantics. The following helpers are provided:
X_HASHTABLE_TYPE_PTR_KEY_NAMEDX_HASHTABLE_TYPE_CSTR_KEY_NAMEDX_HASHTABLE_TYPE_PTR_VALUE_NAMEDX_HASHTABLE_TYPE_PTR_KEY_PTR_VALUE_NAMEDX_HASHTABLE_TYPE_CSTR_KEY_PTR_VALUE_NAMEDThese macros declare the correct ownership and comparison behavior for pointer keys, string keys, or pointer values.
X_HASHTABLE_TYPE_CSTR_KEY_NAMED(int32_t, cstr_i32)
XHashtable_cstr_i32* ht = x_hashtable_cstr_i32_create();
x_hashtable_cstr_i32_set(ht, "ONE", 1);
x_hashtable_cstr_i32_set(ht, "TWO", 2);
Internally these wrappers call the generic API, so there is no additional runtime overhead.
#define X_HASHTABLE_VERSION_MAJOR 1
#define X_HASHTABLE_VERSION_MINOR 0
#define X_HASHTABLE_VERSION_PATCH 0
#define X_HASHTABLE_VERSION(X_HASHTABLE_VERSION_MAJOR *10000+X_HASHTABLE_VERSION_MINOR *100+X_HASHTABLE_VERSION_PATCH)
#define X_HASHTABLE_INITIAL_CAPACITY 16
#define X_HASHTABLE_LOAD_FACTOR 0.75
typedef size_t(*XHashFnHash)(const void *key, size_t);
typedef bool(*XHashFnCompare)(const void *a, const void *b);
typedef void(*XHashFnClone)(void *dst, const void *src);
typedef void(*XHashFnDestroy)(void *ptr);
typedef struct{
enum XEntryState{X_HASH_ENTRY_FREE, X_HASH_ENTRY_OCCUPIED, X_HASH_ENTRY_DELETED,}state;
}XHashEntry;
typedef struct{
size_t key_size;
size_t value_size;
size_t count;
size_t capacity;
XHashEntry *entries;
void *keys;
void *values;
bool key_is_pointer;
bool value_is_pointer;
bool key_is_null_terminated;
bool value_is_null_terminated;
XHashFnHash fn_key_hash;
XHashFnCompare fn_key_compare;
XHashFnClone fn_key_copy;
XHashFnDestroy fn_key_free;
XHashFnClone fn_value_copy;
XHashFnDestroy fn_value_free;
}XHashtable;
typedef struct{
XHashtable *table;
size_t index;
}XHashtableIter;
Create a hashtable using raw size and explicit string/pointer traits for keys and values.
X_HASHTABLE_API XHashtable * x_hashtable_create_ex(
size_t key_size,
bool key_null_terminated,
bool key_is_pointer,
size_t value_size,
bool value_null_terminated,
bool value_is_pointer
);
size_t key_sizebool key_null_terminatedbool key_is_pointersize_t value_sizebool value_null_terminatedbool value_is_pointerPointer to a newly created hashtable, or NULL on failure.
Create a fully-configurable hashtable with custom hashing, comparison, cloning, and destruction.
XHashtable * x_hashtable_create_full(
size_t key_size,
size_t value_size,
XHashFnHash fn_key_hash,
XHashFnCompare fn_key_compare,
XHashFnClone fn_key_copy,
XHashFnDestroy fn_key_free,
XHashFnClone fn_value_copy,
XHashFnDestroy fn_value_free
);
size_t key_sizesize_t value_sizeXHashFnHash fn_key_hashXHashFnCompare fn_key_compareXHashFnClone fn_key_copyXHashFnDestroy fn_key_freeXHashFnClone fn_value_copyXHashFnDestroy fn_value_freePointer to a newly created hashtable, or NULL on failure.
Insert or update a key/value pair in the hashtable.
X_HASHTABLE_API bool x_hashtable_set(
XHashtable *table,
const void *key,
const void *value
);
XHashtable *tableconst void *keyconst void *valueTrue on success, false on failure (e.g., allocation failure).
Retrieve a value from the hashtable by key.
X_HASHTABLE_API bool x_hashtable_get(
XHashtable *table,
const void *key,
void *out_value
);
XHashtable *tableconst void *keyvoid *out_valueTrue if the key was found and out_value was written, false otherwise.
Destroy a hashtable and free all associated resources.
X_HASHTABLE_API void x_hashtable_destroy(XHashtable *table);
XHashtable *tableNothing.
Get the number of stored entries in the hashtable.
X_HASHTABLE_API size_t x_hashtable_count(const XHashtable *table);
const XHashtable *tableNumber of key/value pairs currently stored.
Compare two NUL-terminated strings for equality.
X_HASHTABLE_API bool stdx_str_eq(
const void *a,
const void *b
);
const void *aconst void *bTrue if equal, false otherwise.
Check whether the hashtable contains a key.
X_HASHTABLE_API bool x_hashtable_has(
XHashtable *table,
const void *key
);
XHashtable *tableconst void *keyTrue if the key exists, false otherwise.
Remove an entry from the hashtable by key.
X_HASHTABLE_API bool x_hashtable_remove(
XHashtable *table,
const void *key
);
XHashtable *tableconst void *keyTrue if an entry was removed, false if the key was not found.
Hash an arbitrary byte buffer.
X_HASHTABLE_API size_t x_hashtable_hash_bytes(
const void *ptr,
size_t size
);
const void *ptrsize_t sizeHash value for the given byte buffer.
Hash a NUL-terminated C string (or string-like buffer) for hashtable use.
X_HASHTABLE_API size_t x_hashtable_hash_cstr(
const void *ptr,
size_t size
);
const void *ptrsize_t sizeHash value for the string.
Clone a NUL-terminated C string into destination storage.
X_HASHTABLE_API void x_hashtable_clone_cstr(
void *dest,
const void *src
);
void *destconst void *srcNothing.
Compare two NUL-terminated C strings for hashtable key equality.
X_HASHTABLE_API bool x_hashtable_compare_cstr(
const void *a,
const void *b
);
const void *aconst void *bTrue if equal, false otherwise.
Free a cloned NUL-terminated C string previously allocated by x_hashtable_clone_cstr().
X_HASHTABLE_API void x_hashtable_free_cstr(void *a);
void *aNothing.
Initialize an iterator for a hashtable.
X_HASHTABLE_API bool x_hashtable_iter_begin(
XHashtable *table,
XHashtableIter *it
);
XHashtable *tableXHashtableIter *itTrue if the iterator was initialized (even if empty table), false on invalid args.
Advance iterator to the next occupied entry.
X_HASHTABLE_API bool x_hashtable_iter_next(
XHashtableIter *it,
void **out_key,
void **out_value
);
XHashtableIter *itvoid **out_keyvoid **out_valueTrue if an entry was produced, false if iteration finished or invalid args.
Const-friendly variant of x_hashtable_iter_next().
X_HASHTABLE_API bool x_hashtable_iter_next_const(
XHashtableIter *it,
const void **out_key,
const void **out_value
);
Get the raw slot index of the current entry (mainly for debugging).
X_HASHTABLE_API size_t x_hashtable_iter_slot(const XHashtableIter *it);
const XHashtableIter *itCurrent slot index, or table->capacity if finished/invalid.
#define X_HASHTABLE_TYPE(tk, tv) X_HASHTABLE_TYPE_NAMED(tk, tv, tk##_##tv)
#define X_HASHTABLE_TYPE_NAMED(tk, tv, suffix) \
typedef XHashtable XHashtable_##suffix;\
static inline XHashtable_##suffix *x_hashtable_##suffix##_create(void)\
{\
return(XHashtable_##suffix *)x_hashtable_create_ex(sizeof(tk), false, false, sizeof(tv), false, false);\
}\
static inline bool x_hashtable_##suffix##_set(XHashtable_##suffix *table, tk key, tv value)\
{\
return x_hashtable_set((XHashtable *)table,&key,&value);\
}\
static inline bool x_hashtable_##suffix##_get(XHashtable_##suffix *table, tk key, tv *out_value)\
{\
return x_hashtable_get((XHashtable *)table,&key, out_value);\
}\
static inline bool x_hashtable_##suffix##_has(XHashtable_##suffix *table, tk key)\
{\
return x_hashtable_has((XHashtable *)table,&key);\
}\
static inline bool x_hashtable_##suffix##_remove(XHashtable_##suffix *table, tk key)\
{\
return x_hashtable_remove((XHashtable *)table,&key);\
}\
static inline void x_hashtable_##suffix##_destroy(XHashtable_##suffix *table)\
{\
x_hashtable_destroy((XHashtable *)table);\
}\
static inline size_t x_hashtable_##suffix##_count(const XHashtable_##suffix *table)\
{\
return x_hashtable_count((const XHashtable *)table);\
}
#define X_HASHTABLE_TYPE_PTR_KEY_NAMED(tk, tv, suffix) \
typedef XHashtable XHashtable_##suffix;\
static inline XHashtable_##suffix *x_hashtable_##suffix##_create(void)\
{\
return(XHashtable_##suffix *)x_hashtable_create_ex(sizeof(tk), false, true, sizeof(tv), false, false);\
}\
static inline bool x_hashtable_##suffix##_set(XHashtable_##suffix *table, tk key, tv value)\
{\
return x_hashtable_set((XHashtable *)table, key,&value);\
}\
static inline bool x_hashtable_##suffix##_get(XHashtable_##suffix *table, tk key, tv *out_value)\
{\
return x_hashtable_get((XHashtable *)table, key, out_value);\
}\
static inline bool x_hashtable_##suffix##_has(XHashtable_##suffix *table, tk key)\
{\
return x_hashtable_has((XHashtable *)table, key);\
}\
static inline bool x_hashtable_##suffix##_remove(XHashtable_##suffix *table, tk key)\
{\
return x_hashtable_remove((XHashtable *)table, key);\
}\
static inline void x_hashtable_##suffix##_destroy(XHashtable_##suffix *table)\
{\
x_hashtable_destroy((XHashtable *)table);\
}\
static inline size_t x_hashtable_##suffix##_count(const XHashtable_##suffix *table)\
{\
return x_hashtable_count((const XHashtable *)table);\
}
#define X_HASHTABLE_TYPE_CSTR_KEY_NAMED(tv, suffix) \
typedef XHashtable XHashtable_##suffix;\
static inline XHashtable_##suffix *x_hashtable_##suffix##_create(void)\
{\
return(XHashtable_##suffix *)x_hashtable_create_ex(sizeof(char *), true, true, sizeof(tv), false, false);\
}\
static inline bool x_hashtable_##suffix##_set(XHashtable_##suffix *table, const char *key, tv value)\
{\
return x_hashtable_set((XHashtable *)table, key,&value);\
}\
static inline bool x_hashtable_##suffix##_get(XHashtable_##suffix *table, const char *key, tv *out_value)\
{\
return x_hashtable_get((XHashtable *)table, key, out_value);\
}\
static inline bool x_hashtable_##suffix##_has(XHashtable_##suffix *table, const char *key)\
{\
return x_hashtable_has((XHashtable *)table, key);\
}\
static inline bool x_hashtable_##suffix##_remove(XHashtable_##suffix *table, const char *key)\
{\
return x_hashtable_remove((XHashtable *)table, key);\
}\
static inline void x_hashtable_##suffix##_destroy(XHashtable_##suffix *table)\
{\
x_hashtable_destroy((XHashtable *)table);\
}\
static inline size_t x_hashtable_##suffix##_count(const XHashtable_##suffix *table)\
{\
return x_hashtable_count((const XHashtable *)table);\
}
#define X_HASHTABLE_TYPE_PTR_VALUE_NAMED(tk, tv, suffix) \
typedef XHashtable XHashtable_##suffix;\
static inline XHashtable_##suffix *x_hashtable_##suffix##_create(void)\
{\
return(XHashtable_##suffix *)x_hashtable_create_ex(sizeof(tk), false, false, sizeof(tv), false, true);\
}\
static inline bool x_hashtable_##suffix##_set(XHashtable_##suffix *table, tk key, tv value)\
{\
return x_hashtable_set((XHashtable *)table,&key, value);\
}\
static inline bool x_hashtable_##suffix##_get(XHashtable_##suffix *table, tk key, tv *out_value)\
{\
return x_hashtable_get((XHashtable *)table,&key, out_value);\
}\
static inline bool x_hashtable_##suffix##_has(XHashtable_##suffix *table, tk key)\
{\
return x_hashtable_has((XHashtable *)table,&key);\
}\
static inline bool x_hashtable_##suffix##_remove(XHashtable_##suffix *table, tk key)\
{\
return x_hashtable_remove((XHashtable *)table,&key);\
}\
static inline void x_hashtable_##suffix##_destroy(XHashtable_##suffix *table)\
{\
x_hashtable_destroy((XHashtable *)table);\
}\
static inline size_t x_hashtable_##suffix##_count(const XHashtable_##suffix *table)\
{\
return x_hashtable_count((const XHashtable *)table);\
}
#define X_HASHTABLE_TYPE_PTR_KEY_PTR_VALUE_NAMED(tk, tv, suffix) \
typedef XHashtable XHashtable_##suffix;\
static inline XHashtable_##suffix *x_hashtable_##suffix##_create(void)\
{\
return(XHashtable_##suffix *)x_hashtable_create_ex(sizeof(tk), false, true, sizeof(tv), false, true);\
}\
static inline bool x_hashtable_##suffix##_set(XHashtable_##suffix *table, tk key, tv value)\
{\
return x_hashtable_set((XHashtable *)table, key, value);\
}\
static inline bool x_hashtable_##suffix##_get(XHashtable_##suffix *table, tk key, tv *out_value)\
{\
return x_hashtable_get((XHashtable *)table, key, out_value);\
}\
static inline bool x_hashtable_##suffix##_has(XHashtable_##suffix *table, tk key)\
{\
return x_hashtable_has((XHashtable *)table, key);\
}\
static inline bool x_hashtable_##suffix##_remove(XHashtable_##suffix *table, tk key)\
{\
return x_hashtable_remove((XHashtable *)table, key);\
}\
static inline void x_hashtable_##suffix##_destroy(XHashtable_##suffix *table)\
{\
x_hashtable_destroy((XHashtable *)table);\
}\
static inline size_t x_hashtable_##suffix##_count(const XHashtable_##suffix *table)\
{\
return x_hashtable_count((const XHashtable *)table);\
}
#define X_HASHTABLE_TYPE_CSTR_KEY_PTR_VALUE_NAMED(tv, suffix) \
typedef XHashtable XHashtable_##suffix;\
static inline XHashtable_##suffix *x_hashtable_##suffix##_create(void)\
{\
return(XHashtable_##suffix *)x_hashtable_create_ex(sizeof(char *), true, true, sizeof(tv), false, true);\
}\
static inline bool x_hashtable_##suffix##_set(XHashtable_##suffix *table, const char *key, tv value)\
{\
return x_hashtable_set((XHashtable *)table, key, value);\
}\
static inline bool x_hashtable_##suffix##_get(XHashtable_##suffix *table, const char *key, tv *out_value)\
{\
return x_hashtable_get((XHashtable *)table, key, out_value);\
}\
static inline bool x_hashtable_##suffix##_has(XHashtable_##suffix *table, const char *key)\
{\
return x_hashtable_has((XHashtable *)table, key);\
}\
static inline bool x_hashtable_##suffix##_remove(XHashtable_##suffix *table, const char *key)\
{\
return x_hashtable_remove((XHashtable *)table, key);\
}\
static inline void x_hashtable_##suffix##_destroy(XHashtable_##suffix *table)\
{\
x_hashtable_destroy((XHashtable *)table);\
}\
static inline size_t x_hashtable_##suffix##_count(const XHashtable_##suffix *table)\
{\
return x_hashtable_count((const XHashtable *)table);\
}
Internal macro for allocating memory. To override how this header allocates memory, define this macro with a different implementation before including this header.
#define X_HASHTABLE_ALLOC(sz) malloc(sz)
szInternal macro for allocating zero-initialized memory. To override how this header allocates zero-initialized memory, define this macro with a different implementation before including this header.
#define X_HASHTABLE_CALLOC(n, sz) calloc((n), (sz))
nszInternal macro for freeing memory. To override how this header frees memory, define this macro with a different implementation before including this header.
#define X_HASHTABLE_FREE(p) free(p)
p