diff --git a/CHANGELOG.md b/CHANGELOG.md index 2253993..0ad2504 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `wpSipGetHash128` implementation for the 128 variant of siphash - `wpHashSet`, `wpHashSetCustomHasher`, `wpHashSetAlloc` and `wpHashSetCustomHasherAlloc` constructors - `wpHashSetFind`, `wpHashSetInsertCapped`, `wpHashSetInsertAlloc`, `wpHashSetRemove`, `wpHashSetItems`, `wpHashSetCapacity`, `wpHashSetCount`, `wpHashSetItemSize`, `wpHashSetClear` and `wpHashSetDealloc` utilities +- `wpHashMap`, `wpHashMapCustomHasher`, `wpHashMapAlloc` and `wpHashMapCustomHasherAlloc` constructors +- `wpHashMapKeys`, `wpHashMapValues`, `wpHashMapCapacity`, `wpHashMapCount`, `wpHashMapKeySize`, `wpHashMapValueSize`, `wpHashMapFind`, `wpHashMapInsertCapped`, `wpHashMapInsertOrUpdateCapped`, `wpHashMapUpdate`, `wpHashMapInsertAlloc`, `wpHashMapInsertOrUpdateAlloc`, `wpHashMapRemove`, `wpHashMapClear` and `wpHashMapDealloc` utilities - `wpU8Const`, `wpU16Const`, `wpU32Const`, `wpU64Const`, `wpI8Const`, `wpI16Const`, `wpI32Const`, and `wpI64Const` aliases - `wpPrngXorshiftInitWithSeed` to allow for user control with PRNG - `wpPrngXorshift256InRange`, `wpPrngXorshift256ssInRange`, `wpPrngXorshift256pInRange`, `wpPrngXorshift256Choice`, `wpPrngXorshift256ssChoice` and `wpPrngXorshift256pChoice` utilities diff --git a/src/base/hash/map/map.c b/src/base/hash/map/map.c new file mode 100644 index 0000000..ba76513 --- /dev/null +++ b/src/base/hash/map/map.c @@ -0,0 +1,83 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "map.h" +#include "../table/table.h" + +WpHashMap hashMapAlloc(const WpAllocator *allocator, WpU8StreamEncoder encoder, WpKeyEqualTest key_eq, + u64 capacity, u64 key_size, u64 value_size) { + return hashMapAllocWithCustomHasher(allocator, WP_HASH_TABLE_DEFAULT_HASHER, encoder, key_eq, + capacity, key_size, value_size); +} + +WpHashMap hashMapAllocWithCustomHasher(const WpAllocator *allocator, WpHasher hasher, + WpU8StreamEncoder encoder, WpKeyEqualTest key_eq, + u64 capacity, u64 key_size, u64 value_size) { + WpHashMap map = {0}; + hashTableAlloc(allocator, &map, hasher, encoder, key_eq, capacity, key_size, value_size, true); + return map; +} + +WpHashLookupResult hashMapFind(WpHashMap *map, void *key, u64 key_size) { + u64 hash = hashTableCalcHash(map, key, key_size); + WpHashBucketRef ref = hashTableBucketRef(map, hash); + return hashTableLookup(map, key, ref, key_size); +} + +void hashMapUpdate(WpHashMap *map, void *key, void *value, u64 key_size, u64 value_size) { + WpHashLookupResult result = hashMapFind(map, key, key_size); + if (result.found) { + hashTableUpdate(map, value, result.ref, value_size); + } +} + +void hashMapInsertCapped(WpHashMap *map, void *key, void *value, u64 key_size, u64 value_size) { + WpHashLookupResult result = hashMapFind(map, key, key_size); + if (!result.found && map->load_factor < WP_HASH_MAX_LOAD_FACTOR) { + hashTableInsert(map, key, value, result.ref, key_size, value_size); + } +} + +void hashMapInsertOrUpdateCapped(WpHashMap *map, void *key, void *value, u64 key_size, u64 value_size) { + WpHashLookupResult result = hashMapFind(map, key, key_size); + if (result.found) { + hashTableUpdate(map, value, result.ref, value_size); + } else if (map->load_factor < WP_HASH_MAX_LOAD_FACTOR) { + hashTableInsert(map, key, value, result.ref, key_size, value_size); + } +} + +void hashMapInsertAlloc(const WpAllocator *allocator, WpHashMap *map, void *key, void *value, + u64 key_size, u64 value_size) { + WpHashLookupResult result = hashMapFind(map, key, key_size); + if (!result.found) { + if (map->load_factor >= WP_HASH_MAX_LOAD_FACTOR) { + hashTableGrow(allocator, map, key_size, value_size); + // Rerun key lookup after growing + result = hashMapFind(map, key, key_size); + } + + hashTableInsert(map, key, value, result.ref, key_size, value_size); + } +} + +void hashMapInsertOrUpdateAlloc(const WpAllocator *allocator, WpHashMap *map, void *key, void *value, + u64 key_size, u64 value_size) { + WpHashLookupResult result = hashMapFind(map, key, key_size); + if (result.found) { + hashTableUpdate(map, value, result.ref, value_size); + } else { + if (map->load_factor >= WP_HASH_MAX_LOAD_FACTOR) { + hashTableGrow(allocator, map, key_size, value_size); + // Rerun key lookup after growing + result = hashMapFind(map, key, key_size); + } + hashTableInsert(map, key, value, result.ref, key_size, value_size); + } +} + +void hashMapRemove(WpHashMap *map, void *key, u64 key_size, u64 value_size) { + WpHashLookupResult result = hashMapFind(map, key, key_size); + if (result.found) { + hashTableRemove(map, result.ref, key_size, value_size); + } +} diff --git a/src/base/hash/map/map.h b/src/base/hash/map/map.h new file mode 100644 index 0000000..6dce003 --- /dev/null +++ b/src/base/hash/map/map.h @@ -0,0 +1,134 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef MAP_H +#define MAP_H + +#include "../table/table.h" +#include "../../../common/misc/misc_utils.h" +#include "../../../common/platform/platform.h" + +#ifdef WP_PLATFORM_CPP +BEGIN_C_LINKAGE +#endif // !WP_PLATFORM_CPP + +typedef WpHashTable WpHashMap; + +#ifdef WP_PLATFORM_CPP +#define wpHashMap(KEY_TYPE, VALUE_TYPE, U8_STREAM_ENCODER, KEY_EQ_TEST, CAPACITY) \ + (WpHashMap{ \ + WP_HASH_TABLE_DEFAULT_HASHER, \ + U8_STREAM_ENCODER, \ + KEY_EQ_TEST, \ + wpArrayWithCapacity(KEY_TYPE, wpMiscUtilsU64RoundUpPow2(CAPACITY), WP_ARRAY_INIT_NONE), \ + wpArrayWithCapacity(VALUE_TYPE, wpMiscUtilsU64RoundUpPow2(CAPACITY), WP_ARRAY_INIT_NONE), \ + wpArrayWithCapacity(WpHashBucket, wpMiscUtilsU64RoundUpPow2(CAPACITY), WP_ARRAY_INIT_FILLED), \ + WP_HASH_TABLE_MAGIC, \ + wpMiscUtilsU64RoundUpPow2(CAPACITY) - 1, \ + 0.0f, \ + true, \ + }) +#define wpHashMapCustomHasher(KEY_TYPE, VALUE_TYPE, HASHER, U8_STREAM_ENCODER, KEY_EQ_TEST, \ + CAPACITY) \ + (WpHashMap{ \ + HASHER, \ + U8_STREAM_ENCODER, \ + KEY_EQ_TEST, \ + wpArrayWithCapacity(KEY_TYPE, wpMiscUtilsU64RoundUpPow2(CAPACITY), WP_ARRAY_INIT_NONE), \ + wpArrayWithCapacity(VALUE_TYPE, wpMiscUtilsU64RoundUpPow2(CAPACITY), WP_ARRAY_INIT_NONE), \ + wpArrayWithCapacity(WpHashBucket, wpMiscUtilsU64RoundUpPow2(CAPACITY), WP_ARRAY_INIT_FILLED), \ + WP_HASH_TABLE_MAGIC, \ + wpMiscUtilsU64RoundUpPow2(CAPACITY) - 1, \ + 0.0f, \ + true, \ + }) +#else +#define wpHashMap(KEY_TYPE, VALUE_TYPE, U8_STREAM_ENCODER, KEY_EQ_TEST, CAPACITY) \ + ((WpHashMap){ \ + .hasher = WP_HASH_TABLE_DEFAULT_HASHER, \ + .encoder = U8_STREAM_ENCODER, \ + .key_eq = KEY_EQ_TEST, \ + .keys = wpArrayWithCapacity(KEY_TYPE, wpMiscUtilsU64RoundUpPow2(CAPACITY), \ + WP_ARRAY_INIT_NONE), \ + .values = wpArrayWithCapacity(VALUE_TYPE, wpMiscUtilsU64RoundUpPow2(CAPACITY), \ + WP_ARRAY_INIT_NONE), \ + .buckets = wpArrayWithCapacity(WpHashBucket, wpMiscUtilsU64RoundUpPow2(CAPACITY), \ + WP_ARRAY_INIT_FILLED), \ + .magic = WP_HASH_TABLE_MAGIC, \ + .bucket_mask = wpMiscUtilsU64RoundUpPow2(CAPACITY) - 1, \ + .has_values = true, \ + }) +#define wpHashMapCustomHasher(KEY_TYPE, VALUE_TYPE, HASHER, U8_STREAM_ENCODER, KEY_EQ_TEST, \ + CAPACITY) \ + ((WpHashMap){ \ + .hasher = HASHER, \ + .encoder = U8_STREAM_ENCODER, \ + .key_eq = KEY_EQ_TEST, \ + .keys = wpArrayWithCapacity(KEY_TYPE, wpMiscUtilsU64RoundUpPow2(CAPACITY), \ + WP_ARRAY_INIT_NONE), \ + .values = wpArrayWithCapacity(VALUE_TYPE, wpMiscUtilsU64RoundUpPow2(CAPACITY), \ + WP_ARRAY_INIT_NONE), \ + .buckets = wpArrayWithCapacity(WpHashBucket, wpMiscUtilsU64RoundUpPow2(CAPACITY), \ + WP_ARRAY_INIT_FILLED), \ + .magic = WP_HASH_TABLE_MAGIC, \ + .bucket_mask = wpMiscUtilsU64RoundUpPow2(CAPACITY) - 1, \ + .has_values = true, \ + }) +#endif + +#define wpHashMapAlloc(KEY_TYPE, VALUE_TYPE, ALLOCATOR_PTR, U8_STREAM_ENCODER, KEY_EQ_TEST, \ + CAPACITY) \ + hashMapAlloc(ALLOCATOR_PTR, U8_STREAM_ENCODER, KEY_EQ_TEST, CAPACITY, sizeof(KEY_TYPE), \ + sizeof(VALUE_TYPE)) +#define wpHashMapCustomHasherAlloc(KEY_TYPE, VALUE_TYPE, ALLOCATOR_PTR, HASHER, U8_STREAM_ENCODER, \ + KEY_EQ_TEST, CAPACITY) \ + hashMapAllocWithCustomHasher(ALLOCATOR_PTR, HASHER, U8_STREAM_ENCODER, KEY_EQ_TEST, CAPACITY, \ + sizeof(KEY_TYPE), sizeof(VALUE_TYPE)) +#define wpHashMapKeys(KEY_TYPE, MAP_PTR) ((KEY_TYPE *)hashTableKeys(MAP_PTR, sizeof(KEY_TYPE))) +#define wpHashMapValues(VALUE_TYPE, MAP_PTR) \ + ((VALUE_TYPE *)hashTableValues(MAP_PTR, sizeof(VALUE_TYPE))) +#define wpHashMapCapacity(MAP_PTR) hashTableCapacity(MAP_PTR) +#define wpHashMapCount(MAP_PTR) hashTableCount(MAP_PTR) +#define wpHashMapKeySize(MAP_PTR) hashTableKeySize(MAP_PTR) +#define wpHashMapValueSize(MAP_PTR) hashTableValueSize(MAP_PTR) +#define wpHashMapFind(KEY_TYPE, MAP_PTR, KEY_PTR) hashMapFind(MAP_PTR, KEY_PTR, sizeof(KEY_TYPE)) +#define wpHashMapInsertCapped(KEY_TYPE, VALUE_TYPE, MAP_PTR, KEY_PTR, VALUE_PTR) \ + hashMapInsertCapped(MAP_PTR, KEY_PTR, VALUE_PTR, sizeof(KEY_TYPE), sizeof(VALUE_TYPE)) +#define wpHashMapInsertOrUpdateCapped(KEY_TYPE, VALUE_TYPE, MAP_PTR, KEY_PTR, VALUE_PTR) \ + hashMapInsertOrUpdateCapped(MAP_PTR, KEY_PTR, VALUE_PTR, sizeof(KEY_TYPE), sizeof(VALUE_TYPE)) +#define wpHashMapUpdate(KEY_TYPE, VALUE_TYPE, MAP_PTR, KEY_PTR, VALUE_PTR) \ + hashMapUpdate(MAP_PTR, KEY_PTR, VALUE_PTR, sizeof(KEY_TYPE), sizeof(VALUE_TYPE)) +#define wpHashMapInsertAlloc(KEY_TYPE, VALUE_TYPE, ALLOCATOR_PTR, MAP_PTR, KEY_PTR, VALUE_PTR) \ + hashMapInsertAlloc(ALLOCATOR_PTR, MAP_PTR, KEY_PTR, VALUE_PTR, sizeof(KEY_TYPE), \ + sizeof(VALUE_TYPE)) +#define wpHashMapInsertOrUpdateAlloc(KEY_TYPE, VALUE_TYPE, ALLOCATOR_PTR, MAP_PTR, KEY_PTR, \ + VALUE_PTR) \ + hashMapInsertOrUpdateAlloc(ALLOCATOR_PTR, MAP_PTR, KEY_PTR, VALUE_PTR, sizeof(KEY_TYPE), \ + sizeof(VALUE_TYPE)) +#define wpHashMapRemove(KEY_TYPE, VALUE_TYPE, MAP_PTR, KEY_PTR) \ + hashMapRemove(MAP_PTR, KEY_PTR, sizeof(KEY_TYPE), sizeof(VALUE_TYPE)) +#define wpHashMapClear(KEY_TYPE, VALUE_TYPE, MAP_PTR) \ + hashTableClear(MAP_PTR, sizeof(KEY_TYPE), sizeof(VALUE_TYPE)) +#define wpHashMapDealloc(KEY_TYPE, VALUE_TYPE, ALLOCATOR_PTR, MAP_PTR) \ + hashTableDealloc(ALLOCATOR_PTR, MAP_PTR, sizeof(KEY_TYPE), sizeof(VALUE_TYPE)) + +WpHashMap hashMapAlloc(const WpAllocator *allocator, WpU8StreamEncoder encoder, + WpKeyEqualTest key_eq, u64 capacity, u64 key_size, u64 value_size); +WpHashMap hashMapAllocWithCustomHasher(const WpAllocator *allocator, WpHasher hasher, + WpU8StreamEncoder encoder, WpKeyEqualTest key_eq, + u64 capacity, u64 key_size, u64 value_size); +WpHashLookupResult hashMapFind(WpHashMap *map, void *key, u64 key_size); +void hashMapUpdate(WpHashMap *map, void *key, void *value, u64 key_size, u64 value_size); +void hashMapInsertCapped(WpHashMap *map, void *key, void *value, u64 key_size, u64 value_size); +void hashMapInsertOrUpdateCapped(WpHashMap *map, void *key, void *value, u64 key_size, + u64 value_size); +void hashMapInsertAlloc(const WpAllocator *allocator, WpHashMap *map, void *key, + void *value, u64 key_size, u64 value_size); +void hashMapInsertOrUpdateAlloc(const WpAllocator *allocator, WpHashMap *map, void *key, + void *value, u64 key_size, u64 value_size); +void hashMapRemove(WpHashMap *map, void *key, u64 key_size, u64 value_size); + +#ifdef WP_PLATFORM_CPP +END_C_LINKAGE +#endif // !WP_PLATFORM_CPP + +#endif // !MAP_H diff --git a/src/base/hash/set/set.h b/src/base/hash/set/set.h index 782d12a..67250c1 100644 --- a/src/base/hash/set/set.h +++ b/src/base/hash/set/set.h @@ -73,16 +73,16 @@ typedef WpHashTable WpHashSet; CAPACITY) \ hashSetAllocWithCustomHasher(ALLOCATOR_PTR, HASHER, U8_STREAM_ENCODER, ITEM_EQ_TEST, CAPACITY, \ sizeof(TYPE)) +#define wpHashSetItems(TYPE, SET_PTR) ((TYPE *)hashTableKeys(SET_PTR, sizeof(TYPE))) +#define wpHashSetCapacity(SET_PTR) hashTableCapacity(SET_PTR) +#define wpHashSetCount(SET_PTR) hashTableCount(SET_PTR) +#define wpHashSetItemSize(SET_PTR) hashTableKeySize(SET_PTR) #define wpHashSetFind(TYPE, SET_PTR, ITEM_PTR) hashSetFind(SET_PTR, ITEM_PTR, sizeof(TYPE)) #define wpHashSetInsertCapped(TYPE, SET_PTR, ITEM_PTR) \ hashSetInsertCapped(SET_PTR, ITEM_PTR, sizeof(TYPE)) #define wpHashSetInsertAlloc(TYPE, ALLOCATOR_PTR, SET_PTR, ITEM_PTR) \ hashSetInsertAlloc(ALLOCATOR_PTR, SET_PTR, ITEM_PTR, sizeof(TYPE)) #define wpHashSetRemove(TYPE, SET_PTR, ITEM_PTR) hashSetRemove(SET_PTR, ITEM_PTR, sizeof(TYPE)) -#define wpHashSetItems(TYPE, SET_PTR) ((TYPE *)hashTableKeys(SET_PTR, sizeof(TYPE))) -#define wpHashSetCapacity(SET_PTR) hashTableCapacity(SET_PTR) -#define wpHashSetCount(SET_PTR) hashTableCount(SET_PTR) -#define wpHashSetItemSize(SET_PTR) hashTableKeySize(SET_PTR) #define wpHashSetClear(TYPE, SET_PTR) hashTableClear(SET_PTR, sizeof(TYPE), 0) #define wpHashSetDealloc(TYPE, ALLOCATOR_PTR, SET_PTR) \ hashTableDealloc(ALLOCATOR_PTR, SET_PTR, sizeof(TYPE), 0) diff --git a/src/base/wapp_base.c b/src/base/wapp_base.c index 10671aa..83c8685 100644 --- a/src/base/wapp_base.c +++ b/src/base/wapp_base.c @@ -11,6 +11,7 @@ #include "hash/hasher/siphash.c" #include "hash/table/table.c" #include "hash/set/set.c" +#include "hash/map/map.c" #include "queue/queue.c" #include "stream/stream.c" #include "mem/allocator/mem_allocator.c" diff --git a/src/base/wapp_base.h b/src/base/wapp_base.h index 84b6168..9d2ba89 100644 --- a/src/base/wapp_base.h +++ b/src/base/wapp_base.h @@ -10,6 +10,7 @@ #include "hash/hasher/siphash.h" #include "hash/table/table.h" #include "hash/set/set.h" +#include "hash/map/map.h" #include "queue/queue.h" #include "stream/stream.h" #include "mem/allocator/mem_allocator.h" diff --git a/tests/hashmap/test_hashmap.c b/tests/hashmap/test_hashmap.c new file mode 100644 index 0000000..633cf55 --- /dev/null +++ b/tests/hashmap/test_hashmap.c @@ -0,0 +1,266 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "wapp.h" +#include "test_hashmap.h" + +wp_persist inline WpU8Stream encoder(void *data); +wp_persist inline b8 key_eq(void *a, void *b); + +WpTestFuncResult test_hashmap(void) { + b8 result = true; + WpHashMap map = wpHashMap(WpStr8, i32, encoder, key_eq, 8); + result = result && wpHashMapCapacity(&map) == 8 && wpHashMapCount(&map) == 0; + map = wpHashMap(WpStr8, i32, encoder, key_eq, 3); + result = result && wpHashMapCapacity(&map) == 4 && wpHashMapCount(&map) == 0; + return wpTesterResult(result); +} + +WpTestFuncResult test_hashmap_alloc(void) { + WpAllocator arena = wpMemArenaAllocatorInit(KiB(4)); + b8 result = true; + WpHashMap map = wpHashMapAlloc(WpStr8, i32, &arena, encoder, key_eq, 8); + result = result && wpHashMapCapacity(&map) == 8 && wpHashMapCount(&map) == 0; + map = wpHashMapAlloc(WpStr8, i32, &arena, encoder, key_eq, 3); + result = result && wpHashMapCapacity(&map) == 4 && wpHashMapCount(&map) == 0; + + wpMemArenaAllocatorDestroy(&arena); + + return wpTesterResult(result); +} + +WpTestFuncResult test_hashmap_get_key_size(void) { + b8 result = true; + WpHashMap map = wpHashMap(WpStr8, i32, encoder, key_eq, 8); + result = result && wpHashMapKeySize(&map) == sizeof(WpStr8); + return wpTesterResult(result); +} + +WpTestFuncResult test_hashmap_get_value_size(void) { + b8 result = true; + WpHashMap map = wpHashMap(WpStr8, i32, encoder, key_eq, 8); + result = result && wpHashMapValueSize(&map) == sizeof(i32); + return wpTesterResult(result); +} + +WpTestFuncResult test_hashmap_find(void) { + b8 result = true; + WpHashMap map = wpHashMap(WpStr8, i32, encoder, key_eq, 8); + WpStr8Array keys = wpArray( + WpStr8, + wpStr8Lit("key1"), + wpStr8Lit("key2"), + wpStr8Lit("key3"), + wpStr8Lit("key4") + ); + WpI32Array values = wpArray(i32, 1, 2, 3, 4); + + for (u64 i = 0; i < wpArrayCount(keys); ++i) { + wpHashMapInsertCapped(WpStr8, i32, &map, &keys[i], &values[i]); + } + + WpHashLookupResult res = wpHashMapFind(WpStr8, &map, &keys[3]); + + result = result && res.found && res.ref.dist_and_tag > 0; + + WpStr8 key = wpStr8Lit("key0"); + res = wpHashMapFind(WpStr8, &map, &key); + result = result && !res.found; + + return wpTesterResult(result); +} + +WpTestFuncResult test_hashmap_update(void) { + b8 result = true; + WpHashMap map = wpHashMap(WpStr8, i32, encoder, key_eq, 4); + WpStr8Array keys = wpArray( + WpStr8, + wpStr8Lit("key1"), + wpStr8Lit("key2"), + wpStr8Lit("key3"), + wpStr8Lit("key4") + ); + WpI32Array values = wpArray(i32, 1, 2, 3, 4); + + for (u64 i = 0; i < wpArrayCount(keys); ++i) { + wpHashMapInsertCapped(WpStr8, i32, &map, &keys[i], &values[i]); + } + + i32 new_value = 0; + wpHashMapUpdate(WpStr8, i32, &map, &keys[1], &new_value); + result = result && wpHashMapCapacity(&map) == 4 && wpHashMapCount(&map) == 3 && + wpHashMapValues(i32, &map)[1] == new_value; + + return wpTesterResult(result); +} + +WpTestFuncResult test_hashmap_insert_capped(void) { + b8 result = true; + WpHashMap map = wpHashMap(WpStr8, i32, encoder, key_eq, 4); + WpStr8Array keys = wpArray( + WpStr8, + wpStr8Lit("key1"), + wpStr8Lit("key2"), + wpStr8Lit("key3"), + wpStr8Lit("key4") + ); + WpI32Array values = wpArray(i32, 1, 2, 3, 4); + + for (u64 i = 0; i < wpArrayCount(keys); ++i) { + wpHashMapInsertCapped(WpStr8, i32, &map, &keys[i], &values[i]); + } + + result = result && wpHashMapCapacity(&map) == 4 && wpHashMapCount(&map) == 3; + for (u64 i = 0; i < 3; ++i) { + result = result && wpStr8Equal(wpArrayGet(WpStr8, map.keys, i), &keys[i]); + } + + return wpTesterResult(result); +} + +WpTestFuncResult test_hashmap_insert_or_update_capped(void) { + b8 result = true; + WpHashMap map = wpHashMap(WpStr8, i32, encoder, key_eq, 4); + WpStr8Array keys = wpArray( + WpStr8, + wpStr8Lit("key1"), + wpStr8Lit("key2"), + wpStr8Lit("key3"), + wpStr8Lit("key4") + ); + WpI32Array values = wpArray(i32, 1, 2, 3, 4); + + for (u64 i = 0; i < wpArrayCount(keys); ++i) { + wpHashMapInsertOrUpdateCapped(WpStr8, i32, &map, &keys[i], &values[i]); + } + + result = result && wpHashMapCapacity(&map) == 4 && wpHashMapCount(&map) == 3; + for (u64 i = 0; i < 3; ++i) { + result = result && wpStr8Equal(&wpHashMapKeys(WpStr8, &map)[i], &keys[i]); + } + + i32 new_value = 0; + wpHashMapInsertOrUpdateCapped(WpStr8, i32, &map, &keys[1], &new_value); + result = result && wpHashMapCapacity(&map) == 4 && wpHashMapCount(&map) == 3 && + wpHashMapValues(i32, &map)[1] == new_value; + + return wpTesterResult(result); +} + +WpTestFuncResult test_hashmap_insert_alloc(void) { + WpAllocator arena = wpMemArenaAllocatorInit(KiB(4)); + b8 result = true; + WpHashMap map = wpHashMap(WpStr8, i32, encoder, key_eq, 4); + WpStr8Array keys = wpArray( + WpStr8, + wpStr8Lit("key1"), + wpStr8Lit("key2"), + wpStr8Lit("key3"), + wpStr8Lit("key4") + ); + WpI32Array values = wpArray(i32, 1, 2, 3, 4); + + WpStr8Array map_keys = map.keys; + + for (u64 i = 0; i < wpArrayCount(keys); ++i) { + wpHashMapInsertAlloc(WpStr8, i32, &arena, &map, &keys[i], &values[i]); + } + + result = result && map.keys != map_keys && wpHashMapCapacity(&map) == 8 && wpHashMapCount(&map) == 4; + for (u64 i = 0; i < wpArrayCount(keys); ++i) { + result = result && wpStr8Equal(&wpHashMapKeys(WpStr8, &map)[i], &keys[i]) && + wpHashMapValues(i32, &map)[i] == values[i]; + } + + wpMemArenaAllocatorDestroy(&arena); + + return wpTesterResult(result); +} + +WpTestFuncResult test_hashmap_insert_or_update_alloc(void) { + WpAllocator arena = wpMemArenaAllocatorInit(KiB(4)); + b8 result = true; + WpHashMap map = wpHashMap(WpStr8, i32, encoder, key_eq, 4); + WpStr8Array keys = wpArray( + WpStr8, + wpStr8Lit("key1"), + wpStr8Lit("key2"), + wpStr8Lit("key3"), + wpStr8Lit("key4") + ); + WpI32Array values = wpArray(i32, 1, 2, 3, 4); + + for (u64 i = 0; i < wpArrayCount(keys); ++i) { + wpHashMapInsertOrUpdateAlloc(WpStr8, i32, &arena, &map, &keys[i], &values[i]); + } + + result = result && wpHashMapCapacity(&map) == 8 && wpHashMapCount(&map) == 4; + for (u64 i = 0; i < wpArrayCount(keys); ++i) { + result = result && wpStr8Equal(&wpHashMapKeys(WpStr8, &map)[i], &keys[i]); + } + + i32 new_value = 0; + wpHashMapInsertOrUpdateAlloc(WpStr8, i32, &arena, &map, &keys[1], &new_value); + result = result && wpHashMapCapacity(&map) == 8 && wpHashMapCount(&map) == 4 && + wpHashMapValues(i32, &map)[1] == new_value; + + wpMemArenaAllocatorDestroy(&arena); + + return wpTesterResult(result); +} + +WpTestFuncResult test_hashmap_remove(void) { + b8 result = true; + WpHashMap map = wpHashMap(WpStr8, i32, encoder, key_eq, 4); + WpStr8Array keys = wpArray( + WpStr8, + wpStr8Lit("key1"), + wpStr8Lit("key2"), + wpStr8Lit("key3"), + wpStr8Lit("key4") + ); + WpI32Array values = wpArray(i32, 1, 2, 3, 4); + + for (u64 i = 0; i < wpArrayCount(keys); ++i) { + wpHashMapInsertCapped(WpStr8, i32, &map, &keys[i], &values[i]); + } + + wpHashMapRemove(WpStr8, i32, &map, &keys[0]); + + result = result && wpHashMapCapacity(&map) == 4 && wpHashMapCount(&map) == 2; + + return wpTesterResult(result); +} + +WpTestFuncResult test_hashmap_clear(void) { + b8 result = true; + WpHashMap map = wpHashMap(WpStr8, i32, encoder, key_eq, 8); + WpStr8Array keys = wpArray( + WpStr8, + wpStr8Lit("key1"), + wpStr8Lit("key2"), + wpStr8Lit("key3"), + wpStr8Lit("key4") + ); + WpI32Array values = wpArray(i32, 1, 2, 3, 4); + + for (u64 i = 0; i < wpArrayCount(keys); ++i) { + wpHashMapInsertCapped(WpStr8, i32, &map, &keys[i], &values[i]); + } + + wpHashMapClear(WpStr8, i32, &map); + + result = result && wpHashMapCount(&map) == 0; + + return wpTesterResult(result); +} + +wp_persist inline WpU8Stream encoder(void *data) { + WpStr8 *str = (WpStr8 *)data; + return wpStream(u8, str->buf, str->size); +} + +wp_persist inline b8 key_eq(void *a, void *b) { + WpStr8 *a_str = (WpStr8 *)a; + WpStr8 *b_str = (WpStr8 *)b; + return wpStr8Equal(a_str, b_str); +} diff --git a/tests/hashmap/test_hashmap.cc b/tests/hashmap/test_hashmap.cc new file mode 100644 index 0000000..2f6ca8e --- /dev/null +++ b/tests/hashmap/test_hashmap.cc @@ -0,0 +1,250 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "wapp.h" +#include "test_hashmap.h" + +wp_persist inline WpU8Stream encoder(void *data); +wp_persist inline b8 key_eq(void *a, void *b); + +WpTestFuncResult test_hashmap(void) { + b8 result = true; + WpHashMap map = wpHashMap(WpStr8, i32, encoder, key_eq, 8); + result = result && wpHashMapCapacity(&map) == 8 && wpHashMapCount(&map) == 0; + map = wpHashMap(WpStr8, i32, encoder, key_eq, 3); + result = result && wpHashMapCapacity(&map) == 4 && wpHashMapCount(&map) == 0; + return wpTesterResult(result); +} + +WpTestFuncResult test_hashmap_alloc(void) { + WpAllocator arena = wpMemArenaAllocatorInit(KiB(4)); + b8 result = true; + WpHashMap map = wpHashMapAlloc(WpStr8, i32, &arena, encoder, key_eq, 8); + result = result && wpHashMapCapacity(&map) == 8 && wpHashMapCount(&map) == 0; + map = wpHashMapAlloc(WpStr8, i32, &arena, encoder, key_eq, 3); + result = result && wpHashMapCapacity(&map) == 4 && wpHashMapCount(&map) == 0; + + wpMemArenaAllocatorDestroy(&arena); + + return wpTesterResult(result); +} + +WpTestFuncResult test_hashmap_get_key_size(void) { + b8 result = true; + WpHashMap map = wpHashMap(WpStr8, i32, encoder, key_eq, 8); + result = result && wpHashMapKeySize(&map) == sizeof(WpStr8); + return wpTesterResult(result); +} + +WpTestFuncResult test_hashmap_get_value_size(void) { + b8 result = true; + WpHashMap map = wpHashMap(WpStr8, i32, encoder, key_eq, 8); + result = result && wpHashMapValueSize(&map) == sizeof(i32); + return wpTesterResult(result); +} + +WpTestFuncResult test_hashmap_find(void) { + b8 result = true; + WpHashMap map = wpHashMap(WpStr8, i32, encoder, key_eq, 8); + WpStr8 key1 = wpStr8Lit("key1"); + WpStr8 key2 = wpStr8Lit("key2"); + WpStr8 key3 = wpStr8Lit("key3"); + WpStr8 key4 = wpStr8Lit("key4"); + WpStr8Array keys = wpArray(WpStr8, key1, key2, key3, key4); + WpI32Array values = wpArray(i32, 1, 2, 3, 4); + + for (u64 i = 0; i < wpArrayCount(keys); ++i) { + wpHashMapInsertCapped(WpStr8, i32, &map, &keys[i], &values[i]); + } + + WpHashLookupResult res = wpHashMapFind(WpStr8, &map, &keys[3]); + + result = result && res.found && res.ref.dist_and_tag > 0; + + WpStr8 key = wpStr8Lit("key0"); + res = wpHashMapFind(WpStr8, &map, &key); + result = result && !res.found; + + return wpTesterResult(result); +} + +WpTestFuncResult test_hashmap_update(void) { + b8 result = true; + WpHashMap map = wpHashMap(WpStr8, i32, encoder, key_eq, 4); + WpStr8 key1 = wpStr8Lit("key1"); + WpStr8 key2 = wpStr8Lit("key2"); + WpStr8 key3 = wpStr8Lit("key3"); + WpStr8 key4 = wpStr8Lit("key4"); + WpStr8Array keys = wpArray(WpStr8, key1, key2, key3, key4); + WpI32Array values = wpArray(i32, 1, 2, 3, 4); + + for (u64 i = 0; i < wpArrayCount(keys); ++i) { + wpHashMapInsertCapped(WpStr8, i32, &map, &keys[i], &values[i]); + } + + i32 new_value = 0; + wpHashMapUpdate(WpStr8, i32, &map, &keys[1], &new_value); + result = result && wpHashMapCapacity(&map) == 4 && wpHashMapCount(&map) == 3 && + wpHashMapValues(i32, &map)[1] == new_value; + + return wpTesterResult(result); +} + +WpTestFuncResult test_hashmap_insert_capped(void) { + b8 result = true; + WpHashMap map = wpHashMap(WpStr8, i32, encoder, key_eq, 4); + WpStr8 key1 = wpStr8Lit("key1"); + WpStr8 key2 = wpStr8Lit("key2"); + WpStr8 key3 = wpStr8Lit("key3"); + WpStr8 key4 = wpStr8Lit("key4"); + WpStr8Array keys = wpArray(WpStr8, key1, key2, key3, key4); + WpI32Array values = wpArray(i32, 1, 2, 3, 4); + + for (u64 i = 0; i < wpArrayCount(keys); ++i) { + wpHashMapInsertCapped(WpStr8, i32, &map, &keys[i], &values[i]); + } + + result = result && wpHashMapCapacity(&map) == 4 && wpHashMapCount(&map) == 3; + for (u64 i = 0; i < 3; ++i) { + result = result && wpStr8Equal(wpArrayGet(WpStr8, map.keys, i), &keys[i]); + } + + return wpTesterResult(result); +} + +WpTestFuncResult test_hashmap_insert_or_update_capped(void) { + b8 result = true; + WpHashMap map = wpHashMap(WpStr8, i32, encoder, key_eq, 4); + WpStr8 key1 = wpStr8Lit("key1"); + WpStr8 key2 = wpStr8Lit("key2"); + WpStr8 key3 = wpStr8Lit("key3"); + WpStr8 key4 = wpStr8Lit("key4"); + WpStr8Array keys = wpArray(WpStr8, key1, key2, key3, key4); + WpI32Array values = wpArray(i32, 1, 2, 3, 4); + + for (u64 i = 0; i < wpArrayCount(keys); ++i) { + wpHashMapInsertOrUpdateCapped(WpStr8, i32, &map, &keys[i], &values[i]); + } + + result = result && wpHashMapCapacity(&map) == 4 && wpHashMapCount(&map) == 3; + for (u64 i = 0; i < 3; ++i) { + result = result && wpStr8Equal(&wpHashMapKeys(WpStr8, &map)[i], &keys[i]); + } + + i32 new_value = 0; + wpHashMapInsertOrUpdateCapped(WpStr8, i32, &map, &keys[1], &new_value); + result = result && wpHashMapCapacity(&map) == 4 && wpHashMapCount(&map) == 3 && + wpHashMapValues(i32, &map)[1] == new_value; + + return wpTesterResult(result); +} + +WpTestFuncResult test_hashmap_insert_alloc(void) { + WpAllocator arena = wpMemArenaAllocatorInit(KiB(4)); + b8 result = true; + WpHashMap map = wpHashMap(WpStr8, i32, encoder, key_eq, 4); + WpStr8 key1 = wpStr8Lit("key1"); + WpStr8 key2 = wpStr8Lit("key2"); + WpStr8 key3 = wpStr8Lit("key3"); + WpStr8 key4 = wpStr8Lit("key4"); + WpStr8Array keys = wpArray(WpStr8, key1, key2, key3, key4); + WpI32Array values = wpArray(i32, 1, 2, 3, 4); + + WpStr8Array map_keys = (WpStr8Array)map.keys; + + for (u64 i = 0; i < wpArrayCount(keys); ++i) { + wpHashMapInsertAlloc(WpStr8, i32, &arena, &map, &keys[i], &values[i]); + } + + result = result && map.keys != map_keys && wpHashMapCapacity(&map) == 8 && wpHashMapCount(&map) == 4; + for (u64 i = 0; i < wpArrayCount(keys); ++i) { + result = result && wpStr8Equal(&wpHashMapKeys(WpStr8, &map)[i], &keys[i]) && + wpHashMapValues(i32, &map)[i] == values[i]; + } + + wpMemArenaAllocatorDestroy(&arena); + + return wpTesterResult(result); +} + +WpTestFuncResult test_hashmap_insert_or_update_alloc(void) { + WpAllocator arena = wpMemArenaAllocatorInit(KiB(4)); + b8 result = true; + WpHashMap map = wpHashMap(WpStr8, i32, encoder, key_eq, 4); + WpStr8 key1 = wpStr8Lit("key1"); + WpStr8 key2 = wpStr8Lit("key2"); + WpStr8 key3 = wpStr8Lit("key3"); + WpStr8 key4 = wpStr8Lit("key4"); + WpStr8Array keys = wpArray(WpStr8, key1, key2, key3, key4); + WpI32Array values = wpArray(i32, 1, 2, 3, 4); + + for (u64 i = 0; i < wpArrayCount(keys); ++i) { + wpHashMapInsertOrUpdateAlloc(WpStr8, i32, &arena, &map, &keys[i], &values[i]); + } + + result = result && wpHashMapCapacity(&map) == 8 && wpHashMapCount(&map) == 4; + for (u64 i = 0; i < wpArrayCount(keys); ++i) { + result = result && wpStr8Equal(&wpHashMapKeys(WpStr8, &map)[i], &keys[i]); + } + + i32 new_value = 0; + wpHashMapInsertOrUpdateAlloc(WpStr8, i32, &arena, &map, &keys[1], &new_value); + result = result && wpHashMapCapacity(&map) == 8 && wpHashMapCount(&map) == 4 && + wpHashMapValues(i32, &map)[1] == new_value; + + wpMemArenaAllocatorDestroy(&arena); + + return wpTesterResult(result); +} + +WpTestFuncResult test_hashmap_remove(void) { + b8 result = true; + WpHashMap map = wpHashMap(WpStr8, i32, encoder, key_eq, 4); + WpStr8 key1 = wpStr8Lit("key1"); + WpStr8 key2 = wpStr8Lit("key2"); + WpStr8 key3 = wpStr8Lit("key3"); + WpStr8 key4 = wpStr8Lit("key4"); + WpStr8Array keys = wpArray(WpStr8, key1, key2, key3, key4); + WpI32Array values = wpArray(i32, 1, 2, 3, 4); + + for (u64 i = 0; i < wpArrayCount(keys); ++i) { + wpHashMapInsertCapped(WpStr8, i32, &map, &keys[i], &values[i]); + } + + wpHashMapRemove(WpStr8, i32, &map, &keys[0]); + + result = result && wpHashMapCapacity(&map) == 4 && wpHashMapCount(&map) == 2; + + return wpTesterResult(result); +} + +WpTestFuncResult test_hashmap_clear(void) { + b8 result = true; + WpHashMap map = wpHashMap(WpStr8, i32, encoder, key_eq, 8); + WpStr8 key1 = wpStr8Lit("key1"); + WpStr8 key2 = wpStr8Lit("key2"); + WpStr8 key3 = wpStr8Lit("key3"); + WpStr8 key4 = wpStr8Lit("key4"); + WpStr8Array keys = wpArray(WpStr8, key1, key2, key3, key4); + WpI32Array values = wpArray(i32, 1, 2, 3, 4); + + for (u64 i = 0; i < wpArrayCount(keys); ++i) { + wpHashMapInsertCapped(WpStr8, i32, &map, &keys[i], &values[i]); + } + + wpHashMapClear(WpStr8, i32, &map); + + result = result && wpHashMapCount(&map) == 0; + + return wpTesterResult(result); +} + +wp_persist inline WpU8Stream encoder(void *data) { + WpStr8 *str = (WpStr8 *)data; + return wpStream(u8, str->buf, str->size); +} + +wp_persist inline b8 key_eq(void *a, void *b) { + WpStr8 *a_str = (WpStr8 *)a; + WpStr8 *b_str = (WpStr8 *)b; + return wpStr8Equal(a_str, b_str); +} diff --git a/tests/hashmap/test_hashmap.h b/tests/hashmap/test_hashmap.h new file mode 100644 index 0000000..3a36dd6 --- /dev/null +++ b/tests/hashmap/test_hashmap.h @@ -0,0 +1,21 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef TEST_HASHMAP_H +#define TEST_HASHMAP_H + +#include "wapp.h" + +WpTestFuncResult test_hashmap(void); +WpTestFuncResult test_hashmap_alloc(void); +WpTestFuncResult test_hashmap_get_key_size(void); +WpTestFuncResult test_hashmap_get_value_size(void); +WpTestFuncResult test_hashmap_find(void); +WpTestFuncResult test_hashmap_update(void); +WpTestFuncResult test_hashmap_insert_capped(void); +WpTestFuncResult test_hashmap_insert_or_update_capped(void); +WpTestFuncResult test_hashmap_insert_alloc(void); +WpTestFuncResult test_hashmap_insert_or_update_alloc(void); +WpTestFuncResult test_hashmap_remove(void); +WpTestFuncResult test_hashmap_clear(void); + +#endif // !TEST_HASHMAP_H diff --git a/tests/hashset/test_hashset.c b/tests/hashset/test_hashset.c index f626fb3..ac8c13c 100644 --- a/tests/hashset/test_hashset.c +++ b/tests/hashset/test_hashset.c @@ -1,6 +1,7 @@ // vim:fileencoding=utf-8:foldmethod=marker #include "wapp.h" +#include "test_hashset.h" wp_persist inline WpU8Stream encoder(void *data); wp_persist inline b8 key_eq(void *a, void *b); @@ -27,6 +28,13 @@ WpTestFuncResult test_hashset_alloc(void) { return wpTesterResult(result); } +WpTestFuncResult test_hashset_get_item_size(void) { + b8 result = true; + WpHashSet set = wpHashSet(WpStr8, encoder, key_eq, 8); + result = result && wpHashSetItemSize(&set) == sizeof(WpStr8); + return wpTesterResult(result); +} + WpTestFuncResult test_hashset_find(void) { b8 result = true; WpHashSet set = wpHashSet(WpStr8, encoder, key_eq, 8); @@ -70,7 +78,7 @@ WpTestFuncResult test_hashset_insert_capped(void) { result = result && wpHashSetCapacity(&set) == 4 && wpHashSetCount(&set) == 3; for (u64 i = 0; i < 3; ++i) { - result = result && wpStr8Equal(wpArrayGet(WpStr8, set.keys, i), &keys[i]); + result = result && wpStr8Equal(&wpHashSetItems(WpStr8, &set)[i], &keys[i]); } return wpTesterResult(result); @@ -95,7 +103,7 @@ WpTestFuncResult test_hashset_insert_alloc(void) { } result = result && set.keys != set_keys && wpHashSetCapacity(&set) == 8 && wpHashSetCount(&set) == 4; - for (u64 i = 0; i < 3; ++i) { + for (u64 i = 0; i < wpArrayCount(keys); ++i) { result = result && wpStr8Equal(&wpHashSetItems(WpStr8, &set)[i], &keys[i]); } diff --git a/tests/hashset/test_hashset.cc b/tests/hashset/test_hashset.cc index 7441d2b..f921a08 100644 --- a/tests/hashset/test_hashset.cc +++ b/tests/hashset/test_hashset.cc @@ -1,6 +1,7 @@ // vim:fileencoding=utf-8:foldmethod=marker #include "wapp.h" +#include "test_hashset.h" wp_persist inline WpU8Stream encoder(void *data); wp_persist inline b8 key_eq(void *a, void *b); @@ -27,6 +28,13 @@ WpTestFuncResult test_hashset_alloc(void) { return wpTesterResult(result); } +WpTestFuncResult test_hashset_get_item_size(void) { + b8 result = true; + WpHashSet set = wpHashSet(WpStr8, encoder, key_eq, 8); + result = result && wpHashSetItemSize(&set) == sizeof(WpStr8); + return wpTesterResult(result); +} + WpTestFuncResult test_hashset_find(void) { b8 result = true; WpHashSet set = wpHashSet(WpStr8, encoder, key_eq, 8); @@ -66,7 +74,7 @@ WpTestFuncResult test_hashset_insert_capped(void) { result = result && wpHashSetCapacity(&set) == 4 && wpHashSetCount(&set) == 3; for (u64 i = 0; i < 3; ++i) { - result = result && wpStr8Equal(wpArrayGet(WpStr8, set.keys, i), &keys[i]); + result = result && wpStr8Equal(&wpHashSetItems(WpStr8, &set)[i], &keys[i]); } return wpTesterResult(result); @@ -89,7 +97,7 @@ WpTestFuncResult test_hashset_insert_alloc(void) { } result = result && set.keys != set_keys && wpHashSetCapacity(&set) == 8 && wpHashSetCount(&set) == 4; - for (u64 i = 0; i < 3; ++i) { + for (u64 i = 0; i < wpArrayCount(keys); ++i) { result = result && wpStr8Equal(&wpHashSetItems(WpStr8, &set)[i], &keys[i]); } diff --git a/tests/hashset/test_hashset.h b/tests/hashset/test_hashset.h index 608330c..49b6291 100644 --- a/tests/hashset/test_hashset.h +++ b/tests/hashset/test_hashset.h @@ -7,6 +7,7 @@ WpTestFuncResult test_hashset(void); WpTestFuncResult test_hashset_alloc(void); +WpTestFuncResult test_hashset_get_item_size(void); WpTestFuncResult test_hashset_find(void); WpTestFuncResult test_hashset_insert_capped(void); WpTestFuncResult test_hashset_insert_alloc(void); diff --git a/tests/wapptest.c b/tests/wapptest.c index aed2675..503c29b 100644 --- a/tests/wapptest.c +++ b/tests/wapptest.c @@ -10,6 +10,7 @@ #include "test_file.h" #include "test_hasher.h" #include "test_hashset.h" +#include "test_hashmap.h" #include "test_shell_commander.h" #include "wapp.h" #include @@ -108,11 +109,24 @@ int main(void) { test_siphash_128, test_hashset, test_hashset_alloc, + test_hashset_get_item_size, test_hashset_find, test_hashset_insert_capped, test_hashset_insert_alloc, test_hashset_remove, test_hashset_clear, + test_hashmap, + test_hashmap_alloc, + test_hashmap_get_key_size, + test_hashmap_get_value_size, + test_hashmap_find, + test_hashmap_update, + test_hashmap_insert_capped, + test_hashmap_insert_or_update_capped, + test_hashmap_insert_alloc, + test_hashmap_insert_or_update_alloc, + test_hashmap_remove, + test_hashmap_clear, test_commander_cmd_success, test_commander_cmd_failure, test_commander_cmd_out_buf_success, diff --git a/tests/wapptest.cc b/tests/wapptest.cc index aed2675..503c29b 100644 --- a/tests/wapptest.cc +++ b/tests/wapptest.cc @@ -10,6 +10,7 @@ #include "test_file.h" #include "test_hasher.h" #include "test_hashset.h" +#include "test_hashmap.h" #include "test_shell_commander.h" #include "wapp.h" #include @@ -108,11 +109,24 @@ int main(void) { test_siphash_128, test_hashset, test_hashset_alloc, + test_hashset_get_item_size, test_hashset_find, test_hashset_insert_capped, test_hashset_insert_alloc, test_hashset_remove, test_hashset_clear, + test_hashmap, + test_hashmap_alloc, + test_hashmap_get_key_size, + test_hashmap_get_value_size, + test_hashmap_find, + test_hashmap_update, + test_hashmap_insert_capped, + test_hashmap_insert_or_update_capped, + test_hashmap_insert_alloc, + test_hashmap_insert_or_update_alloc, + test_hashmap_remove, + test_hashmap_clear, test_commander_cmd_success, test_commander_cmd_failure, test_commander_cmd_out_buf_success,