Map support has been implemented.
This commit is contained in:
parent
f431a0731a
commit
aa8d45936c
@ -16,8 +16,7 @@ int main(void)
|
||||
{
|
||||
hash_set64_t *hash_set;
|
||||
uint64_t value;
|
||||
uintptr_t cursor = 0U;
|
||||
size_t offset = 0U;
|
||||
size_t cursor = 0U, offset = 0U;
|
||||
|
||||
/* print logo */
|
||||
printf("LibHashSet Example v%" PRIu16 ".%" PRIu16 ".%" PRIu16 " [%s]\n\n",
|
||||
|
79
libhashset/include/hash_map.h
Normal file
79
libhashset/include/hash_map.h
Normal file
@ -0,0 +1,79 @@
|
||||
/******************************************************************************/
|
||||
/* HashSet for C99, by LoRd_MuldeR <MuldeR2@GMX.de> */
|
||||
/* This work has been released under the CC0 1.0 Universal license! */
|
||||
/******************************************************************************/
|
||||
|
||||
#ifndef _LIBHASHSET_MAP_INCLUDED
|
||||
#define _LIBHASHSET_MAP_INCLUDED
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(_WIN32) && defined(HASHSET_DLL)
|
||||
# ifdef _HASHSET_EXPORTS
|
||||
# define HASHSET_API extern __declspec(dllexport)
|
||||
# else
|
||||
# define HASHSET_API extern __declspec(dllimport)
|
||||
# endif
|
||||
#else
|
||||
# define HASHSET_API extern
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if !defined(_MSC_VER) && !defined(__MINGW32__) && !defined(_ERRNO_T_DEFINED)
|
||||
typedef int errno_t;
|
||||
#endif
|
||||
|
||||
HASHSET_API const uint16_t HASHSET_VERSION_MAJOR;
|
||||
HASHSET_API const uint16_t HASHSET_VERSION_MINOR;
|
||||
HASHSET_API const uint16_t HASHSET_VERSION_PATCH;
|
||||
|
||||
HASHSET_API const char *const HASHSET_BUILD_DATE;
|
||||
HASHSET_API const char *const HASHSET_BUILD_TIME;
|
||||
|
||||
struct _hash_map32;
|
||||
struct _hash_map64;
|
||||
|
||||
typedef struct _hash_map32 hash_map32_t;
|
||||
typedef struct _hash_map64 hash_map64_t;
|
||||
|
||||
HASHSET_API hash_map32_t *hash_map_create32(const size_t initial_capacity, const double load_factor);
|
||||
HASHSET_API hash_map64_t *hash_map_create64(const size_t initial_capacity, const double load_factor);
|
||||
|
||||
HASHSET_API void hash_map_destroy32(hash_map32_t *const instance);
|
||||
HASHSET_API void hash_map_destroy64(hash_map64_t *const instance);
|
||||
|
||||
HASHSET_API errno_t hash_map_insert32(hash_map32_t *const instance, const uint32_t key, const uintptr_t value);
|
||||
HASHSET_API errno_t hash_map_insert64(hash_map64_t *const instance, const uint64_t key, const uintptr_t value);
|
||||
|
||||
HASHSET_API errno_t hash_map_remove32(hash_map32_t *const instance, const uint32_t key);
|
||||
HASHSET_API errno_t hash_map_remove64(hash_map64_t *const instance, const uint64_t key);
|
||||
|
||||
HASHSET_API errno_t hash_map_clear32(hash_map32_t *const instance);
|
||||
HASHSET_API errno_t hash_map_clear64(hash_map64_t *const instance);
|
||||
|
||||
HASHSET_API errno_t hash_map_contains32(const hash_map32_t *const instance, const uint32_t key);
|
||||
HASHSET_API errno_t hash_map_contains64(const hash_map64_t *const instance, const uint64_t key);
|
||||
|
||||
HASHSET_API errno_t hash_map_get32(const hash_map32_t *const instance, const uint32_t key, uintptr_t *const value);
|
||||
HASHSET_API errno_t hash_map_get64(const hash_map64_t *const instance, const uint64_t key, uintptr_t *const value);
|
||||
|
||||
HASHSET_API errno_t hash_map_iterate32(const hash_map32_t *const instance, size_t *const cursor, uint32_t *const key, uintptr_t *const value);
|
||||
HASHSET_API errno_t hash_map_iterate64(const hash_map64_t *const instance, size_t *const cursor, uint64_t *const key, uintptr_t *const value);
|
||||
|
||||
HASHSET_API size_t hash_map_size32(const hash_map32_t *const instance);
|
||||
HASHSET_API size_t hash_map_size64(const hash_map64_t *const instance);
|
||||
|
||||
HASHSET_API errno_t hash_map_info32(const hash_map32_t *const instance, size_t *const capacity, size_t *const valid, size_t *const deleted, size_t *const limit);
|
||||
HASHSET_API errno_t hash_map_info64(const hash_map64_t *const instance, size_t *const capacity, size_t *const valid, size_t *const deleted, size_t *const limit);
|
||||
|
||||
HASHSET_API errno_t hash_map_dump32(const hash_map32_t *const instance, int (*const callback)(const size_t index, const char status, const uint32_t key, const uintptr_t value));
|
||||
HASHSET_API errno_t hash_map_dump64(const hash_map64_t *const instance, int (*const callback)(const size_t index, const char status, const uint64_t key, const uintptr_t value));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /*_LIBHASHSET_MAP_INCLUDED*/
|
@ -3,8 +3,8 @@
|
||||
/* This work has been released under the CC0 1.0 Universal license! */
|
||||
/******************************************************************************/
|
||||
|
||||
#ifndef _LIBHASHSET_INCLUDED
|
||||
#define _LIBHASHSET_INCLUDED
|
||||
#ifndef _LIBHASHSET_SET_INCLUDED
|
||||
#define _LIBHASHSET_SET_INCLUDED
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
@ -46,20 +46,20 @@ HASHSET_API hash_set64_t *hash_set_create64(const size_t initial_capacity, const
|
||||
HASHSET_API void hash_set_destroy32(hash_set32_t *const instance);
|
||||
HASHSET_API void hash_set_destroy64(hash_set64_t *const instance);
|
||||
|
||||
HASHSET_API errno_t hash_set_insert32(hash_set32_t *const instance, const uint32_t value);
|
||||
HASHSET_API errno_t hash_set_insert64(hash_set64_t *const instance, const uint64_t value);
|
||||
HASHSET_API errno_t hash_set_insert32(hash_set32_t *const instance, const uint32_t item);
|
||||
HASHSET_API errno_t hash_set_insert64(hash_set64_t *const instance, const uint64_t item);
|
||||
|
||||
HASHSET_API errno_t hash_set_remove32(hash_set32_t *const instance, const uint32_t value);
|
||||
HASHSET_API errno_t hash_set_remove64(hash_set64_t *const instance, const uint64_t value);
|
||||
HASHSET_API errno_t hash_set_remove32(hash_set32_t *const instance, const uint32_t item);
|
||||
HASHSET_API errno_t hash_set_remove64(hash_set64_t *const instance, const uint64_t item);
|
||||
|
||||
HASHSET_API errno_t hash_set_clear32(hash_set32_t *const instance);
|
||||
HASHSET_API errno_t hash_set_clear64(hash_set64_t *const instance);
|
||||
|
||||
HASHSET_API errno_t hash_set_contains32(const hash_set32_t *const instance, const uint32_t value);
|
||||
HASHSET_API errno_t hash_set_contains64(const hash_set64_t *const instance, const uint64_t value);
|
||||
HASHSET_API errno_t hash_set_contains32(const hash_set32_t *const instance, const uint32_t item);
|
||||
HASHSET_API errno_t hash_set_contains64(const hash_set64_t *const instance, const uint64_t item);
|
||||
|
||||
HASHSET_API errno_t hash_set_iterate32(const hash_set32_t *const instance, uintptr_t *const cursor, uint32_t *const value);
|
||||
HASHSET_API errno_t hash_set_iterate64(const hash_set64_t *const instance, uintptr_t *const cursor, uint64_t *const value);
|
||||
HASHSET_API errno_t hash_set_iterate32(const hash_set32_t *const instance, size_t *const cursor, uint32_t *const item);
|
||||
HASHSET_API errno_t hash_set_iterate64(const hash_set64_t *const instance, size_t *const cursor, uint64_t *const item);
|
||||
|
||||
HASHSET_API size_t hash_set_size32(const hash_set32_t *const instance);
|
||||
HASHSET_API size_t hash_set_size64(const hash_set64_t *const instance);
|
||||
@ -67,10 +67,10 @@ HASHSET_API size_t hash_set_size64(const hash_set64_t *const instance);
|
||||
HASHSET_API errno_t hash_set_info32(const hash_set32_t *const instance, size_t *const capacity, size_t *const valid, size_t *const deleted, size_t *const limit);
|
||||
HASHSET_API errno_t hash_set_info64(const hash_set64_t *const instance, size_t *const capacity, size_t *const valid, size_t *const deleted, size_t *const limit);
|
||||
|
||||
HASHSET_API errno_t hash_set_dump32(const hash_set32_t *const instance, int (*const callback)(const size_t index, const char status, const uint32_t value));
|
||||
HASHSET_API errno_t hash_set_dump64(const hash_set64_t *const instance, int (*const callback)(const size_t index, const char status, const uint64_t value));
|
||||
HASHSET_API errno_t hash_set_dump32(const hash_set32_t *const instance, int (*const callback)(const size_t index, const char status, const uint32_t item));
|
||||
HASHSET_API errno_t hash_set_dump64(const hash_set64_t *const instance, int (*const callback)(const size_t index, const char status, const uint64_t item));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* _LIBHASHSET_INCLUDED */
|
||||
#endif /*_LIBHASHSET_SET_INCLUDED*/
|
||||
|
@ -39,11 +39,15 @@
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="include\hash_map.h" />
|
||||
<ClInclude Include="include\hash_set.h" />
|
||||
<ClInclude Include="src\common.h" />
|
||||
<ClInclude Include="src\generic.h" />
|
||||
<ClInclude Include="src\generic_hash_map.h" />
|
||||
<ClInclude Include="src\generic_hash_set.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\hash_map_32.c" />
|
||||
<ClCompile Include="src\hash_map_64.c" />
|
||||
<ClCompile Include="src\hash_set_32.c" />
|
||||
<ClCompile Include="src\hash_set_64.c" />
|
||||
<ClCompile Include="src\version.c" />
|
||||
|
@ -21,7 +21,13 @@
|
||||
<ClInclude Include="src\common.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\generic.h">
|
||||
<ClInclude Include="src\generic_hash_set.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\hash_map.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\generic_hash_map.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
@ -35,5 +41,11 @@
|
||||
<ClCompile Include="src\hash_set_32.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\hash_map_64.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\hash_map_32.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
452
libhashset/src/generic_hash_map.h
Normal file
452
libhashset/src/generic_hash_map.h
Normal file
@ -0,0 +1,452 @@
|
||||
/******************************************************************************/
|
||||
/* HashSet for C99, by LoRd_MuldeR <MuldeR2@GMX.de> */
|
||||
/* This work has been released under the CC0 1.0 Universal license! */
|
||||
/******************************************************************************/
|
||||
|
||||
#ifndef _LIBHASHSET_GENERIC_MAP_INCLUDED
|
||||
#define _LIBHASHSET_GENERIC_MAP_INCLUDED
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#ifndef NAME_SUFFIX
|
||||
#error NAME_SUFFIX must be defined!
|
||||
#endif
|
||||
|
||||
#define _CONCAT(X,Y) X##Y
|
||||
#define CONCAT(X,Y) _CONCAT(X,Y)
|
||||
|
||||
#define DECLARE(X) CONCAT(X,NAME_SUFFIX)
|
||||
|
||||
/* ------------------------------------------------- */
|
||||
/* Data types */
|
||||
/* ------------------------------------------------- */
|
||||
|
||||
typedef struct DECLARE(_hash_map_data)
|
||||
{
|
||||
key_t *keys;
|
||||
uintptr_t *values;
|
||||
uint8_t *used, *deleted;
|
||||
size_t capacity;
|
||||
}
|
||||
hash_data_t;
|
||||
|
||||
struct DECLARE(_hash_map)
|
||||
{
|
||||
double load_factor;
|
||||
size_t valid, deleted, limit;
|
||||
hash_data_t data;
|
||||
};
|
||||
|
||||
/* ------------------------------------------------- */
|
||||
/* Allocation */
|
||||
/* ------------------------------------------------- */
|
||||
|
||||
static INLINE bool_t alloc_data(hash_data_t *const data, const size_t capacity)
|
||||
{
|
||||
zero_memory(data, 1U, sizeof(hash_data_t));
|
||||
|
||||
data->keys = (key_t*) calloc(capacity, sizeof(key_t));
|
||||
if (!data->values)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
data->values = (uintptr_t*) calloc(capacity, sizeof(uintptr_t));
|
||||
if (!data->values)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
data->used = (uint8_t*) calloc(div_ceil(capacity, 8U), sizeof(uint8_t));
|
||||
if (!data->used)
|
||||
{
|
||||
SAFE_FREE(data->values);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
data->deleted = (uint8_t*) calloc(div_ceil(capacity, 8U), sizeof(uint8_t));
|
||||
if (!data->deleted)
|
||||
{
|
||||
SAFE_FREE(data->used);
|
||||
SAFE_FREE(data->values);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
data->capacity = capacity;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INLINE void free_data(hash_data_t *const data)
|
||||
{
|
||||
if (data)
|
||||
{
|
||||
SAFE_FREE(data->keys);
|
||||
SAFE_FREE(data->values);
|
||||
SAFE_FREE(data->used);
|
||||
SAFE_FREE(data->deleted);
|
||||
data->capacity = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------- */
|
||||
/* Set functions */
|
||||
/* ------------------------------------------------- */
|
||||
|
||||
#define INDEX(X) ((size_t)((X) % data->capacity))
|
||||
|
||||
static INLINE bool_t find_slot(const hash_data_t *const data, const key_t key, size_t *const index_out, bool_t *const reused_out)
|
||||
{
|
||||
uint64_t loop = 0U;
|
||||
bool_t is_saved = FALSE;
|
||||
size_t index;
|
||||
|
||||
for (index = INDEX(hash_compute(loop, key)); get_flag(data->used, index); index = INDEX(hash_compute(++loop, key)))
|
||||
{
|
||||
if (get_flag(data->deleted, index))
|
||||
{
|
||||
if (!is_saved)
|
||||
{
|
||||
SAFE_SET(index_out, index);
|
||||
SAFE_SET(reused_out, TRUE);
|
||||
is_saved = TRUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (data->keys[index] == key)
|
||||
{
|
||||
SAFE_SET(index_out, index);
|
||||
SAFE_SET(reused_out, FALSE);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_saved)
|
||||
{
|
||||
SAFE_SET(index_out, index);
|
||||
SAFE_SET(reused_out, FALSE);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static INLINE void put_value(hash_data_t *const data, const size_t index, const key_t key, const uintptr_t value, const bool_t reusing)
|
||||
{
|
||||
data->keys[index] = key;
|
||||
data->values[index] = value;
|
||||
|
||||
if (reusing)
|
||||
{
|
||||
assert(get_flag(data->used, index));
|
||||
clear_flag(data->deleted, index);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(!get_flag(data->deleted, index));
|
||||
set_flag(data->used, index);
|
||||
}
|
||||
}
|
||||
|
||||
static INLINE size_t compute_limit(const size_t capacity, const double load_factor)
|
||||
{
|
||||
size_t limit = round_sz(capacity * load_factor);
|
||||
|
||||
while (capacity && (limit >= capacity))
|
||||
{
|
||||
limit = safe_decr(limit);
|
||||
}
|
||||
|
||||
return limit;
|
||||
}
|
||||
|
||||
static INLINE errno_t rebuild_map(hash_map_t *const instance, const size_t new_capacity)
|
||||
{
|
||||
size_t k, index;
|
||||
hash_data_t temp;
|
||||
|
||||
if (new_capacity < instance->valid)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if (!alloc_data(&temp, new_capacity))
|
||||
{
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
for (k = 0U; k < instance->data.capacity; ++k)
|
||||
{
|
||||
if (IS_VALID(instance->data, k))
|
||||
{
|
||||
const key_t key = instance->data.keys[k];
|
||||
const uintptr_t value = instance->data.values[k];
|
||||
if (find_slot(&temp, key, &index, NULL))
|
||||
{
|
||||
free_data(&temp);
|
||||
return EFAULT; /*this should never happen!*/
|
||||
}
|
||||
put_value(&temp, index, key, value, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
free_data(&instance->data);
|
||||
instance->data = temp;
|
||||
instance->limit = compute_limit(instance->data.capacity, instance->load_factor);
|
||||
instance->deleted = 0U;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
/* PUBLIC FUNCTIONS */
|
||||
/* ========================================================================= */
|
||||
|
||||
hash_map_t *DECLARE(hash_map_create)(const size_t initial_capacity, const double load_factor)
|
||||
{
|
||||
hash_map_t* instance = (hash_map_t*) calloc(1U, sizeof(hash_map_t));
|
||||
if (!instance)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!alloc_data(&instance->data, (initial_capacity > 0U) ? next_pow2(initial_capacity) : DEFAULT_CAPACITY))
|
||||
{
|
||||
SAFE_FREE(instance);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
instance->load_factor = (load_factor > DBL_EPSILON) ? BOUND(0.125, load_factor, 1.0) : DEFAULT_LOADFCTR;
|
||||
instance->limit = compute_limit(instance->data.capacity, instance->load_factor);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void DECLARE(hash_map_destroy)(hash_map_t *instance)
|
||||
{
|
||||
if (instance)
|
||||
{
|
||||
free_data(&instance->data);
|
||||
zero_memory(instance, 1U, sizeof(hash_map_t));
|
||||
SAFE_FREE(instance);
|
||||
}
|
||||
}
|
||||
|
||||
errno_t DECLARE(hash_map_insert)(hash_map_t *const instance, const key_t key, const uintptr_t value)
|
||||
{
|
||||
size_t index;
|
||||
bool_t slot_reused;
|
||||
|
||||
if ((!instance) || (!instance->data.keys))
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if (find_slot(&instance->data, key, &index, &slot_reused))
|
||||
{
|
||||
instance->data.values[index] = index;
|
||||
return EEXIST;
|
||||
}
|
||||
|
||||
if ((!slot_reused) && (safe_add(instance->valid, instance->deleted) >= instance->limit))
|
||||
{
|
||||
const errno_t error = rebuild_map(instance, safe_times2(instance->data.capacity));
|
||||
if (error)
|
||||
{
|
||||
return error;
|
||||
}
|
||||
if (find_slot(&instance->data, key, &index, &slot_reused))
|
||||
{
|
||||
return EFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
put_value(&instance->data, index, key, value, slot_reused);
|
||||
|
||||
instance->valid = safe_incr(instance->valid);
|
||||
if (slot_reused)
|
||||
{
|
||||
instance->deleted = safe_decr(instance->deleted);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
errno_t DECLARE(hash_map_contains)(const hash_map_t *const instance, const key_t key)
|
||||
{
|
||||
if ((!instance) || (!instance->data.keys))
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
return (instance->valid && find_slot(&instance->data, key, NULL, NULL)) ? 0 : ENOENT;
|
||||
}
|
||||
|
||||
errno_t DECLARE(hash_map_get)(const hash_map_t *const instance, const key_t key, uintptr_t *const value)
|
||||
{
|
||||
size_t index;
|
||||
|
||||
if ((!instance) || (!instance->data.keys))
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if (!find_slot(&instance->data, key, &index, NULL))
|
||||
{
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
*value = instance->data.values[index];
|
||||
return 0;
|
||||
}
|
||||
|
||||
errno_t DECLARE(hash_map_remove)(hash_map_t *const instance, const key_t key)
|
||||
{
|
||||
size_t index;
|
||||
|
||||
if ((!instance) || (!instance->data.keys))
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if ((!instance->valid) || (!find_slot(&instance->data, key, &index, NULL)))
|
||||
{
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
set_flag(instance->data.deleted, index);
|
||||
instance->deleted = safe_incr(instance->deleted);
|
||||
instance->valid = safe_decr(instance->valid);
|
||||
|
||||
if (!instance->valid)
|
||||
{
|
||||
return DECLARE(hash_map_clear)(instance);
|
||||
}
|
||||
|
||||
if (instance->deleted > (instance->limit / 2U))
|
||||
{
|
||||
const size_t min_capacity = next_pow2(round_sz(safe_incr(instance->valid) / instance->load_factor));
|
||||
const errno_t error = rebuild_map(instance, (instance->data.capacity > min_capacity) ? min_capacity : instance->data.capacity);
|
||||
if (error && (error != ENOMEM))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
errno_t DECLARE(hash_map_clear)(hash_map_t *const instance)
|
||||
{
|
||||
if ((!instance) || (!instance->data.values))
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if (instance->valid || instance->deleted)
|
||||
{
|
||||
const size_t count = div_ceil(instance->data.capacity, 8U);
|
||||
instance->valid = instance->deleted = 0U;
|
||||
zero_memory(instance->data.used, count, sizeof(uint8_t));
|
||||
zero_memory(instance->data.deleted, count, sizeof(uint8_t));
|
||||
}
|
||||
else
|
||||
{
|
||||
return EAGAIN;
|
||||
}
|
||||
|
||||
if (instance->data.capacity > MINIMUM_CAPACITY)
|
||||
{
|
||||
const errno_t error = rebuild_map(instance, MINIMUM_CAPACITY);
|
||||
if (error && (error != ENOMEM))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
errno_t DECLARE(hash_map_iterate)(const hash_map_t *const instance, size_t *const cursor, key_t *const key, uintptr_t *const value)
|
||||
{
|
||||
size_t index;
|
||||
|
||||
if ((!instance) || (!cursor) || (*cursor >= SIZE_MAX) || (!instance->data.values))
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
for (index = *cursor; index < instance->data.capacity; ++index)
|
||||
{
|
||||
if (IS_VALID(instance->data, index))
|
||||
{
|
||||
if (key)
|
||||
{
|
||||
*key = instance->data.keys[index];
|
||||
}
|
||||
if (value)
|
||||
{
|
||||
*value = instance->data.values[index];
|
||||
}
|
||||
*cursor = index + 1U;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
*cursor = SIZE_MAX;
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
size_t DECLARE(hash_map_size)(const hash_map_t *const instance)
|
||||
{
|
||||
return instance ? instance->valid : 0U;
|
||||
}
|
||||
|
||||
errno_t DECLARE(hash_map_info)(const hash_map_t *const instance, size_t *const capacity, size_t *const valid, size_t *const deleted, size_t *const limit)
|
||||
{
|
||||
if ((!instance) || (!instance->data.values))
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if (capacity)
|
||||
{
|
||||
*capacity = instance->data.capacity;
|
||||
}
|
||||
if (valid)
|
||||
{
|
||||
*valid = instance->valid;
|
||||
}
|
||||
if (deleted)
|
||||
{
|
||||
*deleted = instance->deleted;
|
||||
}
|
||||
if (limit)
|
||||
{
|
||||
*limit = instance->limit;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
HASHSET_API errno_t DECLARE(hash_map_dump)(const hash_map_t *const instance, int (*const callback)(const size_t index, const char status, const key_t key, const uintptr_t value))
|
||||
{
|
||||
size_t index;
|
||||
|
||||
if ((!instance) || (!instance->data.values))
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
for (index = 0U; index < instance->data.capacity; ++index)
|
||||
{
|
||||
if (!callback(index, get_flag(instance->data.used, index) ? (get_flag(instance->data.deleted, index) ? 'd' : 'v') : 'u', instance->data.keys[index], instance->data.values[index]))
|
||||
{
|
||||
return ECANCELED;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /*_LIBHASHSET_GENERIC_MAP_INCLUDED*/
|
@ -3,8 +3,8 @@
|
||||
/* This work has been released under the CC0 1.0 Universal license! */
|
||||
/******************************************************************************/
|
||||
|
||||
#ifndef _LIBHASHSET_GENERIC_INCLUDED
|
||||
#define _LIBHASHSET_GENERIC_INCLUDED
|
||||
#ifndef _LIBHASHSET_GENERIC_SET_INCLUDED
|
||||
#define _LIBHASHSET_GENERIC_SET_INCLUDED
|
||||
|
||||
#include "common.h"
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
|
||||
typedef struct DECLARE(_hash_set_data)
|
||||
{
|
||||
value_t *values;
|
||||
item_t *items;
|
||||
uint8_t *used, *deleted;
|
||||
size_t capacity;
|
||||
}
|
||||
@ -44,8 +44,8 @@ static INLINE bool_t alloc_data(hash_data_t *const data, const size_t capacity)
|
||||
{
|
||||
zero_memory(data, 1U, sizeof(hash_data_t));
|
||||
|
||||
data->values = (value_t*) calloc(capacity, sizeof(value_t));
|
||||
if (!data->values)
|
||||
data->items = (item_t*) calloc(capacity, sizeof(item_t));
|
||||
if (!data->items)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
@ -53,7 +53,7 @@ static INLINE bool_t alloc_data(hash_data_t *const data, const size_t capacity)
|
||||
data->used = (uint8_t*) calloc(div_ceil(capacity, 8U), sizeof(uint8_t));
|
||||
if (!data->used)
|
||||
{
|
||||
SAFE_FREE(data->values);
|
||||
SAFE_FREE(data->items);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -61,7 +61,7 @@ static INLINE bool_t alloc_data(hash_data_t *const data, const size_t capacity)
|
||||
if (!data->deleted)
|
||||
{
|
||||
SAFE_FREE(data->used);
|
||||
SAFE_FREE(data->values);
|
||||
SAFE_FREE(data->items);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -73,7 +73,7 @@ static INLINE void free_data(hash_data_t *const data)
|
||||
{
|
||||
if (data)
|
||||
{
|
||||
SAFE_FREE(data->values);
|
||||
SAFE_FREE(data->items);
|
||||
SAFE_FREE(data->used);
|
||||
SAFE_FREE(data->deleted);
|
||||
data->capacity = 0U;
|
||||
@ -86,13 +86,13 @@ static INLINE void free_data(hash_data_t *const data)
|
||||
|
||||
#define INDEX(X) ((size_t)((X) % data->capacity))
|
||||
|
||||
static INLINE bool_t find_slot(const hash_data_t *const data, const value_t value, size_t *const index_out, bool_t *const reused_out)
|
||||
static INLINE bool_t find_slot(const hash_data_t *const data, const item_t item, size_t *const index_out, bool_t *const reused_out)
|
||||
{
|
||||
uint64_t loop = 0U;
|
||||
bool_t is_saved = FALSE;
|
||||
size_t index;
|
||||
|
||||
for (index = INDEX(hash_compute(loop, value)); get_flag(data->used, index); index = INDEX(hash_compute(++loop, value)))
|
||||
for (index = INDEX(hash_compute(loop, item)); get_flag(data->used, index); index = INDEX(hash_compute(++loop, item)))
|
||||
{
|
||||
if (get_flag(data->deleted, index))
|
||||
{
|
||||
@ -105,7 +105,7 @@ static INLINE bool_t find_slot(const hash_data_t *const data, const value_t valu
|
||||
}
|
||||
else
|
||||
{
|
||||
if (data->values[index] == value)
|
||||
if (data->items[index] == item)
|
||||
{
|
||||
SAFE_SET(index_out, index);
|
||||
SAFE_SET(reused_out, FALSE);
|
||||
@ -123,9 +123,10 @@ static INLINE bool_t find_slot(const hash_data_t *const data, const value_t valu
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static INLINE void put_value(hash_data_t *const data, const size_t index, const value_t value, const bool_t reusing)
|
||||
static INLINE void put_item(hash_data_t *const data, const size_t index, const item_t item, const bool_t reusing)
|
||||
{
|
||||
data->values[index] = value;
|
||||
data->items[index] = item;
|
||||
|
||||
if (reusing)
|
||||
{
|
||||
assert(get_flag(data->used, index));
|
||||
@ -169,13 +170,13 @@ static INLINE errno_t rebuild_set(hash_set_t *const instance, const size_t new_c
|
||||
{
|
||||
if (IS_VALID(instance->data, k))
|
||||
{
|
||||
const value_t value = instance->data.values[k];
|
||||
if (find_slot(&temp, value, &index, NULL))
|
||||
const item_t item = instance->data.items[k];
|
||||
if (find_slot(&temp, item, &index, NULL))
|
||||
{
|
||||
free_data(&temp);
|
||||
return EFAULT; /*this should never happen!*/
|
||||
}
|
||||
put_value(&temp, index, value, FALSE);
|
||||
put_item(&temp, index, item, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -221,17 +222,17 @@ void DECLARE(hash_set_destroy)(hash_set_t *instance)
|
||||
}
|
||||
}
|
||||
|
||||
errno_t DECLARE(hash_set_insert)(hash_set_t *const instance, const value_t value)
|
||||
errno_t DECLARE(hash_set_insert)(hash_set_t *const instance, const item_t item)
|
||||
{
|
||||
size_t index;
|
||||
bool_t slot_reused;
|
||||
|
||||
if ((!instance) || (!instance->data.values))
|
||||
if ((!instance) || (!instance->data.items))
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if (find_slot(&instance->data, value, &index, &slot_reused))
|
||||
if (find_slot(&instance->data, item, &index, &slot_reused))
|
||||
{
|
||||
return EEXIST;
|
||||
}
|
||||
@ -243,13 +244,13 @@ errno_t DECLARE(hash_set_insert)(hash_set_t *const instance, const value_t value
|
||||
{
|
||||
return error;
|
||||
}
|
||||
if (find_slot(&instance->data, value, &index, &slot_reused))
|
||||
if (find_slot(&instance->data, item, &index, &slot_reused))
|
||||
{
|
||||
return EFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
put_value(&instance->data, index, value, slot_reused);
|
||||
put_item(&instance->data, index, item, slot_reused);
|
||||
|
||||
instance->valid = safe_incr(instance->valid);
|
||||
if (slot_reused)
|
||||
@ -260,26 +261,26 @@ errno_t DECLARE(hash_set_insert)(hash_set_t *const instance, const value_t value
|
||||
return 0;
|
||||
}
|
||||
|
||||
errno_t DECLARE(hash_set_contains)(const hash_set_t *const instance, const value_t value)
|
||||
errno_t DECLARE(hash_set_contains)(const hash_set_t *const instance, const item_t item)
|
||||
{
|
||||
if ((!instance) || (!instance->data.values))
|
||||
if ((!instance) || (!instance->data.items))
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
return (instance->valid && find_slot(&instance->data, value, NULL, NULL)) ? 0 : ENOENT;
|
||||
return (instance->valid && find_slot(&instance->data, item, NULL, NULL)) ? 0 : ENOENT;
|
||||
}
|
||||
|
||||
errno_t DECLARE(hash_set_remove)(hash_set_t *const instance, const value_t value)
|
||||
errno_t DECLARE(hash_set_remove)(hash_set_t *const instance, const item_t item)
|
||||
{
|
||||
size_t index;
|
||||
|
||||
if ((!instance) || (!instance->data.values))
|
||||
if ((!instance) || (!instance->data.items))
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if ((!instance->valid) || (!find_slot(&instance->data, value, &index, NULL)))
|
||||
if ((!instance->valid) || (!find_slot(&instance->data, item, &index, NULL)))
|
||||
{
|
||||
return ENOENT;
|
||||
}
|
||||
@ -308,7 +309,7 @@ errno_t DECLARE(hash_set_remove)(hash_set_t *const instance, const value_t value
|
||||
|
||||
errno_t DECLARE(hash_set_clear)(hash_set_t *const instance)
|
||||
{
|
||||
if ((!instance) || (!instance->data.values))
|
||||
if ((!instance) || (!instance->data.items))
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
@ -337,29 +338,29 @@ errno_t DECLARE(hash_set_clear)(hash_set_t *const instance)
|
||||
return 0;
|
||||
}
|
||||
|
||||
errno_t DECLARE(hash_set_iterate)(const hash_set_t *const instance, uintptr_t *const cursor, value_t *const value)
|
||||
errno_t DECLARE(hash_set_iterate)(const hash_set_t *const instance, size_t *const cursor, item_t *const item)
|
||||
{
|
||||
size_t index;
|
||||
|
||||
if ((!instance) || (!cursor) || (*cursor >= SIZE_MAX) || (!instance->data.values))
|
||||
if ((!instance) || (!cursor) || (*cursor >= SIZE_MAX) || (!instance->data.items))
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
for (index = (size_t)(*cursor); index < instance->data.capacity; ++index)
|
||||
for (index = *cursor; index < instance->data.capacity; ++index)
|
||||
{
|
||||
if (IS_VALID(instance->data, index))
|
||||
{
|
||||
if (value)
|
||||
if (item)
|
||||
{
|
||||
*value = instance->data.values[index];
|
||||
*item = instance->data.items[index];
|
||||
}
|
||||
*cursor = (uintptr_t)(index + 1U);
|
||||
*cursor = index + 1U;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
*cursor = (uintptr_t)SIZE_MAX;
|
||||
*cursor = SIZE_MAX;
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
@ -370,7 +371,7 @@ size_t DECLARE(hash_set_size)(const hash_set_t *const instance)
|
||||
|
||||
errno_t DECLARE(hash_set_info)(const hash_set_t *const instance, size_t *const capacity, size_t *const valid, size_t *const deleted, size_t *const limit)
|
||||
{
|
||||
if ((!instance) || (!instance->data.values))
|
||||
if ((!instance) || (!instance->data.items))
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
@ -395,18 +396,18 @@ errno_t DECLARE(hash_set_info)(const hash_set_t *const instance, size_t *const c
|
||||
return 0;
|
||||
}
|
||||
|
||||
HASHSET_API errno_t DECLARE(hash_set_dump)(const hash_set_t *const instance, int (*const callback)(const size_t index, const char status, const value_t value))
|
||||
HASHSET_API errno_t DECLARE(hash_set_dump)(const hash_set_t *const instance, int (*const callback)(const size_t index, const char status, const item_t item))
|
||||
{
|
||||
size_t index;
|
||||
|
||||
if ((!instance) || (!instance->data.values))
|
||||
if ((!instance) || (!instance->data.items))
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
for (index = 0U; index < instance->data.capacity; ++index)
|
||||
{
|
||||
if (!callback(index, get_flag(instance->data.used, index) ? (get_flag(instance->data.deleted, index) ? 'd' : 'v') : 'u', instance->data.values[index]))
|
||||
if (!callback(index, get_flag(instance->data.used, index) ? (get_flag(instance->data.deleted, index) ? 'd' : 'v') : 'u', instance->data.items[index]))
|
||||
{
|
||||
return ECANCELED;
|
||||
}
|
||||
@ -415,4 +416,4 @@ HASHSET_API errno_t DECLARE(hash_set_dump)(const hash_set_t *const instance, int
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* _LIBHASHSET_GENERIC_INCLUDED */
|
||||
#endif /*_LIBHASHSET_GENERIC_SET_INCLUDED*/
|
12
libhashset/src/hash_map_32.c
Normal file
12
libhashset/src/hash_map_32.c
Normal file
@ -0,0 +1,12 @@
|
||||
/******************************************************************************/
|
||||
/* HashSet for C99, by LoRd_MuldeR <MuldeR2@GMX.de> */
|
||||
/* This work has been released under the CC0 1.0 Universal license! */
|
||||
/******************************************************************************/
|
||||
|
||||
#include <hash_map.h>
|
||||
|
||||
#define NAME_SUFFIX 32
|
||||
typedef hash_map32_t hash_map_t;
|
||||
typedef uint32_t key_t;
|
||||
|
||||
#include "generic_hash_map.h"
|
12
libhashset/src/hash_map_64.c
Normal file
12
libhashset/src/hash_map_64.c
Normal file
@ -0,0 +1,12 @@
|
||||
/******************************************************************************/
|
||||
/* HashSet for C99, by LoRd_MuldeR <MuldeR2@GMX.de> */
|
||||
/* This work has been released under the CC0 1.0 Universal license! */
|
||||
/******************************************************************************/
|
||||
|
||||
#include <hash_map.h>
|
||||
|
||||
#define NAME_SUFFIX 64
|
||||
typedef hash_map64_t hash_map_t;
|
||||
typedef uint64_t key_t;
|
||||
|
||||
#include "generic_hash_map.h"
|
@ -4,10 +4,9 @@
|
||||
/******************************************************************************/
|
||||
|
||||
#include <hash_set.h>
|
||||
#include "common.h"
|
||||
|
||||
#define NAME_SUFFIX 32
|
||||
typedef hash_set32_t hash_set_t;
|
||||
typedef uint32_t value_t;
|
||||
typedef uint32_t item_t;
|
||||
|
||||
#include "generic.h"
|
||||
#include "generic_hash_set.h"
|
||||
|
@ -7,6 +7,6 @@
|
||||
|
||||
#define NAME_SUFFIX 64
|
||||
typedef hash_set64_t hash_set_t;
|
||||
typedef uint64_t value_t;
|
||||
typedef uint64_t item_t;
|
||||
|
||||
#include "generic.h"
|
||||
#include "generic_hash_set.h"
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include <hash_set.h>
|
||||
|
||||
const uint16_t HASHSET_VERSION_MAJOR = UINT16_C(1);
|
||||
const uint16_t HASHSET_VERSION_MINOR = UINT16_C(1);
|
||||
const uint16_t HASHSET_VERSION_MINOR = UINT16_C(2);
|
||||
const uint16_t HASHSET_VERSION_PATCH = UINT16_C(0);
|
||||
|
||||
const char *const HASHSET_BUILD_DATE = __DATE__;
|
||||
|
@ -180,10 +180,9 @@ int test_function_1(hash_set64_t *const hash_set)
|
||||
|
||||
int test_function_2(hash_set64_t *const hash_set)
|
||||
{
|
||||
size_t r, j, capacity, valid, deleted, limit;
|
||||
size_t r, j, cursor, capacity, valid, deleted, limit;
|
||||
uint64_t value;
|
||||
uint8_t spinner = 0U, *test1, *test2;
|
||||
uintptr_t cursor;
|
||||
|
||||
random_t random;
|
||||
random_init(&random);
|
||||
|
Loading…
Reference in New Issue
Block a user