Compare commits
10 Commits
ac5baef3b8
..
v2.5.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 66acbaad9e | |||
| 180292289d | |||
| 68640dc5e2 | |||
| 20e890964f | |||
| c215486442 | |||
| 9881c0ac9a | |||
| 17be4841e6 | |||
| 421728c137 | |||
| b014ffa3b1 | |||
| 8da18774f0 |
+12
-7
@@ -7,18 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [2.5.0] - 2026-09-24
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- `wpMiscUtilsRotl64` utility
|
- `wpMiscUtilsRotl64` utility
|
||||||
- `WpHasher` interface
|
- `WpHasher` interface
|
||||||
|
- `wpCustomHasher` constructor for custom hasher with user-defined context
|
||||||
- `wpHasherGetHash128` and `wpHasherGetHash64` utilities
|
- `wpHasherGetHash128` and `wpHasherGetHash64` utilities
|
||||||
- `wpMur3HasherData` to setup the MurmurHash3 data struct
|
- `wpX64Mur3Hasher` and `wpX64Mur3HasherWithSeed` constructors for the hasher interface
|
||||||
- `wpX64Mur3Hasher` constructor for the hasher interface
|
- `wpX64Mur3GetHash128` implementation for the x64 128-bit variant of the MurmurHash3 algorithm
|
||||||
- `wpX64Mur3Hasher128` implementation for the x64 128-bit variant of the MurmurHash3 algorithm
|
- `wpSip24Hasher`, `wpSip48Hasher`, `wpSip24HasherWithKey` and `wpSip48HasherWithKey` constructors for the hasher interface
|
||||||
- `wpSip24HasherData` and `wpSip48HasherData` to setup the siphash data structs for the different variants
|
- `wpSipGetHash128` implementation for the 128 variant of siphash
|
||||||
- `wpSipHasher` constructor for the hasher interface
|
- `wpHashSet`, `wpHashSetCustomHasher`, `wpHashSetAlloc` and `wpHashSetCustomHasherAlloc` constructors
|
||||||
- `wpSipHasher128` implementation for the 128 variant of siphash
|
- `wpHashSetFind`, `wpHashSetInsertCapped`, `wpHashSetInsertAlloc`, `wpHashSetRemove`, `wpHashSetItems`, `wpHashSetCapacity`, `wpHashSetCount`, `wpHashSetItemSize`, `wpHashSetClear` and `wpHashSetDealloc` utilities
|
||||||
- Add `wpU8Const`, `wpU16Const`, `wpU32Const`, `wpU64Const`, `wpI8Const`, `wpI16Const`, `wpI32Const`, and `wpI64Const`
|
- `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
|
- `wpPrngXorshiftInitWithSeed` to allow for user control with PRNG
|
||||||
- `wpPrngXorshift256InRange`, `wpPrngXorshift256ssInRange`, `wpPrngXorshift256pInRange`, `wpPrngXorshift256Choice`, `wpPrngXorshift256ssChoice` and `wpPrngXorshift256pChoice` utilities
|
- `wpPrngXorshift256InRange`, `wpPrngXorshift256ssInRange`, `wpPrngXorshift256pInRange`, `wpPrngXorshift256Choice`, `wpPrngXorshift256ssChoice` and `wpPrngXorshift256pChoice` utilities
|
||||||
- `WpSplitmix64State`
|
- `WpSplitmix64State`
|
||||||
|
|||||||
@@ -204,11 +204,15 @@ void arrayRemoveSwapEnd(WpArray array, u64 index, u64 item_size) {
|
|||||||
if (header->count == 0 || index > header->count) { return; }
|
if (header->count == 0 || index > header->count) { return; }
|
||||||
|
|
||||||
u64 last_item_index = header->count - 1;
|
u64 last_item_index = header->count - 1;
|
||||||
void *cur_item = arrayGetItemWithOffset(array, header->item_size * index);
|
|
||||||
void *last_item = arrayGetItemWithOffset(array, header->item_size * last_item_index);
|
|
||||||
|
|
||||||
memcpy(cur_item, last_item, header->item_size);
|
if (index < last_item_index) {
|
||||||
memset(last_item, 0, header->item_size);
|
void *cur_item = arrayGetItemWithOffset(array, header->item_size * index);
|
||||||
|
void *last_item = arrayGetItemWithOffset(array, header->item_size * last_item_index);
|
||||||
|
|
||||||
|
memcpy(cur_item, last_item, header->item_size);
|
||||||
|
memset(last_item, 0, header->item_size);
|
||||||
|
}
|
||||||
|
|
||||||
--(header->count);
|
--(header->count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,13 +3,23 @@
|
|||||||
#include "hasher.h"
|
#include "hasher.h"
|
||||||
#include "../../../common/assert/assert.h"
|
#include "../../../common/assert/assert.h"
|
||||||
|
|
||||||
WpU128Hash wpHasherGetHash128(const WpHasher *hasher, WpU8Stream *stream) {
|
WpHasher wpCustomHasher(WpHashFunc func, void *ctx) {
|
||||||
|
return (WpHasher){
|
||||||
|
.func = func,
|
||||||
|
.ctx = (WpHasherCtx){
|
||||||
|
.type = WP_HASHER_CTX_USER_DEFINED,
|
||||||
|
.user = ctx,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
WpU128 wpHasherGetHash128(const WpHasher *hasher, WpU8Stream *stream) {
|
||||||
wpDebugAssert(hasher != NULL && stream != NULL, "hasher and stream shouldn't be NULL");
|
wpDebugAssert(hasher != NULL && stream != NULL, "hasher and stream shouldn't be NULL");
|
||||||
return hasher->func(stream, hasher->data);
|
return hasher->func(stream, hasher->ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 wpHasherGetHash64(const WpHasher *hasher, WpU8Stream *stream) {
|
u64 wpHasherGetHash64(const WpHasher *hasher, WpU8Stream *stream) {
|
||||||
wpDebugAssert(hasher != NULL && stream != NULL, "hasher and stream shouldn't be NULL");
|
wpDebugAssert(hasher != NULL && stream != NULL, "hasher and stream shouldn't be NULL");
|
||||||
WpU128Hash result = hasher->func(stream, hasher->data);
|
WpU128 result = hasher->func(stream, hasher->ctx);
|
||||||
return result.hash[0] ^ result.hash[1];
|
return result.value[0] ^ result.value[1];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,24 +4,49 @@
|
|||||||
#define HASHER_H
|
#define HASHER_H
|
||||||
|
|
||||||
#include "../../stream/stream.h"
|
#include "../../stream/stream.h"
|
||||||
|
#include "../../../common/aliases/aliases.h"
|
||||||
|
|
||||||
#ifdef WP_PLATFORM_CPP
|
#ifdef WP_PLATFORM_CPP
|
||||||
BEGIN_C_LINKAGE
|
BEGIN_C_LINKAGE
|
||||||
#endif // !WP_PLATFORM_CPP
|
#endif // !WP_PLATFORM_CPP
|
||||||
|
|
||||||
typedef struct WpU128Hash {
|
typedef struct WpU128 {
|
||||||
u64 hash[2];
|
u64 value[2];
|
||||||
} WpU128Hash;
|
} WpU128;
|
||||||
|
|
||||||
typedef WpU128Hash (*WpHashFunc)(WpU8Stream *stream, void *hasher_data);
|
typedef enum WpHasherCtxType {
|
||||||
|
WP_HASHER_CTX_MURMUR3,
|
||||||
|
WP_HASHER_CTX_SIPHASH,
|
||||||
|
WP_HASHER_CTX_USER_DEFINED,
|
||||||
|
|
||||||
|
COUNT_HASHER_CTX,
|
||||||
|
} WpHasherCtxType;
|
||||||
|
|
||||||
|
typedef struct WpSiphashCtx {
|
||||||
|
WpU128 key;
|
||||||
|
u64 compression;
|
||||||
|
u64 finalisation;
|
||||||
|
} WpSiphashCtx;
|
||||||
|
|
||||||
|
typedef struct WpHasherCtx {
|
||||||
|
WpHasherCtxType type;
|
||||||
|
union {
|
||||||
|
WpSiphashCtx siphash;
|
||||||
|
void *user;
|
||||||
|
u32 murmur3;
|
||||||
|
};
|
||||||
|
} WpHasherCtx;
|
||||||
|
|
||||||
|
typedef WpU128 (*WpHashFunc)(WpU8Stream *stream, WpHasherCtx ctx);
|
||||||
|
|
||||||
typedef struct WpHasher {
|
typedef struct WpHasher {
|
||||||
WpHashFunc func;
|
WpHashFunc func;
|
||||||
void *data;
|
WpHasherCtx ctx;
|
||||||
} WpHasher;
|
} WpHasher;
|
||||||
|
|
||||||
WpU128Hash wpHasherGetHash128(const WpHasher *hasher, WpU8Stream *stream);
|
WpHasher wpCustomHasher(WpHashFunc func, void *ctx);
|
||||||
u64 wpHasherGetHash64 (const WpHasher *hasher, WpU8Stream *stream);
|
WpU128 wpHasherGetHash128(const WpHasher *hasher, WpU8Stream *stream);
|
||||||
|
u64 wpHasherGetHash64(const WpHasher *hasher, WpU8Stream *stream);
|
||||||
|
|
||||||
#ifdef WP_PLATFORM_CPP
|
#ifdef WP_PLATFORM_CPP
|
||||||
END_C_LINKAGE
|
END_C_LINKAGE
|
||||||
|
|||||||
@@ -5,22 +5,34 @@
|
|||||||
#include "../../../common/aliases/aliases.h"
|
#include "../../../common/aliases/aliases.h"
|
||||||
#include "../../../common/assert/assert.h"
|
#include "../../../common/assert/assert.h"
|
||||||
#include "../../../common/misc/misc_utils.h"
|
#include "../../../common/misc/misc_utils.h"
|
||||||
|
#include "../../../prng/xorshift/xorshift.h"
|
||||||
|
|
||||||
wp_intern inline void validateMur3HasherData(const WpMur3HasherData *io);
|
wp_intern inline void validateMur3HasherCtx(const WpHasherCtx *ctx);
|
||||||
wp_intern inline u64 fmix64(u64 k);
|
wp_intern inline u64 fmix64(u64 k);
|
||||||
|
|
||||||
WpHasher wpX64Mur3Hasher(WpMur3HasherData *data) {
|
WpHasher wpX64Mur3Hasher(void) {
|
||||||
return (WpHasher){ .func = wpX64Mur3Hasher128, .data = (void *)data };
|
WpXor256State state = wpPrngXorshiftInit();
|
||||||
|
u32 seed = (u32)wpPrngXorshift256(&state);
|
||||||
|
return wpX64Mur3HasherWithSeed(seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
WpU128Hash wpX64Mur3Hasher128(WpU8Stream *bytes, void *hasher_io) {
|
WpHasher wpX64Mur3HasherWithSeed(u32 seed) {
|
||||||
WpMur3HasherData *io = (WpMur3HasherData *)hasher_io;
|
return (WpHasher){
|
||||||
validateMur3HasherData(io);
|
.func = wpX64Mur3GetHash128,
|
||||||
|
.ctx = (WpHasherCtx){
|
||||||
|
.type = WP_HASHER_CTX_MURMUR3,
|
||||||
|
.murmur3 = seed,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
WpU128 wpX64Mur3GetHash128(WpU8Stream *bytes, WpHasherCtx ctx) {
|
||||||
|
validateMur3HasherCtx(&ctx);
|
||||||
|
|
||||||
const i32 nblocks = (bytes->count * bytes->item_size) / 16;
|
const i32 nblocks = (bytes->count * bytes->item_size) / 16;
|
||||||
|
|
||||||
u64 h1 = io->seed;
|
u64 h1 = ctx.murmur3;
|
||||||
u64 h2 = io->seed;
|
u64 h2 = ctx.murmur3;
|
||||||
|
|
||||||
const u64 c1 = wpU64Const(0x87c37b91114253d5);
|
const u64 c1 = wpU64Const(0x87c37b91114253d5);
|
||||||
const u64 c2 = wpU64Const(0x4cf5ad432745937f);
|
const u64 c2 = wpU64Const(0x4cf5ad432745937f);
|
||||||
@@ -96,11 +108,11 @@ WpU128Hash wpX64Mur3Hasher128(WpU8Stream *bytes, void *hasher_io) {
|
|||||||
h1 += h2;
|
h1 += h2;
|
||||||
h2 += h1;
|
h2 += h1;
|
||||||
|
|
||||||
return (WpU128Hash){ .hash = { [0] = h1, [1] = h2 } };
|
return (WpU128){ .value = { [0] = h1, [1] = h2 } };
|
||||||
}
|
}
|
||||||
|
|
||||||
wp_intern inline void validateMur3HasherData(const WpMur3HasherData *data) {
|
wp_intern inline void validateMur3HasherCtx(const WpHasherCtx *ctx) {
|
||||||
wpRuntimeAssert(data->magic == WP_MUR3_HASHER_MAGIC, "Invalid Murmur3 hash data");
|
wpRuntimeAssert(ctx->type == WP_HASHER_CTX_MURMUR3, "Invalid Murmur3 hash context");
|
||||||
}
|
}
|
||||||
|
|
||||||
wp_intern inline u64 fmix64(u64 k) {
|
wp_intern inline u64 fmix64(u64 k) {
|
||||||
|
|||||||
@@ -17,21 +17,9 @@
|
|||||||
BEGIN_C_LINKAGE
|
BEGIN_C_LINKAGE
|
||||||
#endif // !WP_PLATFORM_CPP
|
#endif // !WP_PLATFORM_CPP
|
||||||
|
|
||||||
#define WP_MUR3_HASHER_MAGIC wpU64Const(0x57504d4841534833)
|
WpHasher wpX64Mur3Hasher(void);
|
||||||
|
WpHasher wpX64Mur3HasherWithSeed(u32 seed);
|
||||||
typedef struct WpMur3HasherData {
|
WpU128 wpX64Mur3GetHash128(WpU8Stream *bytes, WpHasherCtx ctx);
|
||||||
u64 magic;
|
|
||||||
u32 seed;
|
|
||||||
} WpMur3HasherData;
|
|
||||||
|
|
||||||
#ifdef WP_PLATFORM_CPP
|
|
||||||
#define wpMur3HasherData(SEED) (WpMur3HasherData{WP_MUR3_HASHER_MAGIC, SEED})
|
|
||||||
#else
|
|
||||||
#define wpMur3HasherData(SEED) ((WpMur3HasherData){ .magic = WP_MUR3_HASHER_MAGIC, .seed = SEED })
|
|
||||||
#endif
|
|
||||||
|
|
||||||
WpHasher wpX64Mur3Hasher(WpMur3HasherData *data);
|
|
||||||
WpU128Hash wpX64Mur3Hasher128(WpU8Stream *bytes, void *hasher_io);
|
|
||||||
|
|
||||||
#ifdef WP_PLATFORM_CPP
|
#ifdef WP_PLATFORM_CPP
|
||||||
END_C_LINKAGE
|
END_C_LINKAGE
|
||||||
|
|||||||
@@ -5,10 +5,11 @@
|
|||||||
#include "../../../common/aliases/aliases.h"
|
#include "../../../common/aliases/aliases.h"
|
||||||
#include "../../../common/assert/assert.h"
|
#include "../../../common/assert/assert.h"
|
||||||
#include "../../../common/misc/misc_utils.h"
|
#include "../../../common/misc/misc_utils.h"
|
||||||
|
#include "../../../prng/xorshift/xorshift.h"
|
||||||
|
|
||||||
wp_intern inline void siphash(WpStream *bytes, const WpSipHasherData *params, u8 *out, u64 outlen);
|
wp_intern inline void siphash(WpStream *bytes, const WpHasherCtx *ctx, u8 *out, u64 outlen);
|
||||||
wp_intern inline void validateSipHasherData(const WpSipHasherData *io);
|
wp_intern inline void validateSipHasherCtx(const WpHasherCtx *ctx);
|
||||||
wp_intern inline b8 checkRounds(const WpSipHasherData *params);
|
wp_intern inline b8 checkRounds(const WpHasherCtx *ctx);
|
||||||
|
|
||||||
#define _u32To8LE(p, v) \
|
#define _u32To8LE(p, v) \
|
||||||
(p)[0] = (u8)((v)); \
|
(p)[0] = (u8)((v)); \
|
||||||
@@ -44,24 +45,50 @@ wp_intern inline b8 checkRounds(const WpSipHasherData *params);
|
|||||||
v2 = wpMiscUtilsRotl64(v2, 32); \
|
v2 = wpMiscUtilsRotl64(v2, 32); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
WpHasher wpSipHasher(WpSipHasherData *data) {
|
WpHasher wpSip24Hasher(void) {
|
||||||
return (WpHasher){ .func = wpSipHasher128, .data = (void *)data };
|
WpXor256State state = wpPrngXorshiftInit();
|
||||||
|
WpU128 key = { .value = { wpPrngXorshift256(&state), wpPrngXorshift256(&state) } };
|
||||||
|
return wpSip24HasherWithKey(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
WpU128Hash wpSipHasher128(WpStream *bytes, void *hasher_data) {
|
WpHasher wpSip48Hasher(void) {
|
||||||
WpSipHasherData *data = (WpSipHasherData *)hasher_data;
|
WpXor256State state = wpPrngXorshiftInit();
|
||||||
validateSipHasherData(data);
|
WpU128 key = { .value = { wpPrngXorshift256(&state), wpPrngXorshift256(&state) } };
|
||||||
WpU128Hash output = {0};
|
return wpSip48HasherWithKey(key);
|
||||||
siphash(bytes, data, (u8 *)&output.hash, sizeof(u64) * 2);
|
}
|
||||||
|
|
||||||
|
WpHasher wpSip24HasherWithKey(WpU128 key) {
|
||||||
|
return (WpHasher){
|
||||||
|
.func = wpSipGetHash128,
|
||||||
|
.ctx = (WpHasherCtx){
|
||||||
|
.type = WP_HASHER_CTX_SIPHASH,
|
||||||
|
.siphash = (WpSiphashCtx){ .key = key, .compression = 2, .finalisation = 4 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
WpHasher wpSip48HasherWithKey(WpU128 key) {
|
||||||
|
return (WpHasher){
|
||||||
|
.func = wpSipGetHash128,
|
||||||
|
.ctx = (WpHasherCtx){
|
||||||
|
.type = WP_HASHER_CTX_SIPHASH,
|
||||||
|
.siphash = (WpSiphashCtx){ .key = key, .compression = 4, .finalisation = 8 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
WpU128 wpSipGetHash128(WpStream *bytes, WpHasherCtx ctx) {
|
||||||
|
validateSipHasherCtx(&ctx);
|
||||||
|
WpU128 output = {0};
|
||||||
|
siphash(bytes, &ctx, (u8 *)&output.value, sizeof(u64) * 2);
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of the 128-variant of the siphash algorithm
|
* Implementation of the 128-variant of the siphash algorithm
|
||||||
*/
|
*/
|
||||||
wp_intern inline void siphash(WpStream *bytes, const WpSipHasherData *params, u8 *out,
|
wp_intern inline void siphash(WpStream *bytes, const WpHasherCtx *ctx, u8 *out, u64 outlen) {
|
||||||
u64 outlen) {
|
const u8 *kk = (const u8 *)&ctx->siphash.key;
|
||||||
const u8 *kk = (const u8 *)¶ms->key;
|
|
||||||
u64 inlen = bytes->count * bytes->item_size;
|
u64 inlen = bytes->count * bytes->item_size;
|
||||||
|
|
||||||
wpRuntimeAssert(outlen == 16, "outlen should be 16");
|
wpRuntimeAssert(outlen == 16, "outlen should be 16");
|
||||||
@@ -89,7 +116,7 @@ wp_intern inline void siphash(WpStream *bytes, const WpSipHasherData *params, u8
|
|||||||
m = _u8To64LE(data);
|
m = _u8To64LE(data);
|
||||||
v3 ^= m;
|
v3 ^= m;
|
||||||
|
|
||||||
for (i = 0; i < params->compression; ++i) {
|
for (i = 0; i < ctx->siphash.compression; ++i) {
|
||||||
_sipRound;
|
_sipRound;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +152,7 @@ wp_intern inline void siphash(WpStream *bytes, const WpSipHasherData *params, u8
|
|||||||
|
|
||||||
v3 ^= b;
|
v3 ^= b;
|
||||||
|
|
||||||
for (i = 0; i < params->compression; ++i) {
|
for (i = 0; i < ctx->siphash.compression; ++i) {
|
||||||
_sipRound;
|
_sipRound;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +160,7 @@ wp_intern inline void siphash(WpStream *bytes, const WpSipHasherData *params, u8
|
|||||||
|
|
||||||
v2 ^= 0xee;
|
v2 ^= 0xee;
|
||||||
|
|
||||||
for (i = 0; i < params->finalisation; ++i) {
|
for (i = 0; i < ctx->siphash.finalisation; ++i) {
|
||||||
_sipRound;
|
_sipRound;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +169,7 @@ wp_intern inline void siphash(WpStream *bytes, const WpSipHasherData *params, u8
|
|||||||
|
|
||||||
v1 ^= 0xdd;
|
v1 ^= 0xdd;
|
||||||
|
|
||||||
for (i = 0; i < params->finalisation; ++i) {
|
for (i = 0; i < ctx->siphash.finalisation; ++i) {
|
||||||
_sipRound;
|
_sipRound;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,14 +177,14 @@ wp_intern inline void siphash(WpStream *bytes, const WpSipHasherData *params, u8
|
|||||||
_u64To8LE(out + 8, b);
|
_u64To8LE(out + 8, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
wp_intern inline void validateSipHasherData(const WpSipHasherData *data) {
|
wp_intern inline void validateSipHasherCtx(const WpHasherCtx *ctx) {
|
||||||
b8 magic_match = data->magic == WP_SIP_HASHER_MAGIC;
|
b8 magic_match = ctx->type == WP_HASHER_CTX_SIPHASH;
|
||||||
b8 rounds = checkRounds(data);
|
b8 rounds = checkRounds(ctx);
|
||||||
wpRuntimeAssert(magic_match && rounds, "Invalid Siphash data");
|
wpRuntimeAssert(magic_match && rounds, "Invalid Siphash context");
|
||||||
}
|
}
|
||||||
|
|
||||||
wp_intern inline b8 checkRounds(const WpSipHasherData *data) {
|
wp_intern inline b8 checkRounds(const WpHasherCtx *ctx) {
|
||||||
b8 rounds24 = data->compression == 2 && data->finalisation == 4;
|
b8 rounds24 = ctx->siphash.compression == 2 && ctx->siphash.finalisation == 4;
|
||||||
b8 rounds48 = data->compression == 4 && data->finalisation == 8;
|
b8 rounds48 = ctx->siphash.compression == 4 && ctx->siphash.finalisation == 8;
|
||||||
return rounds24 || rounds48;
|
return rounds24 || rounds48;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// vim:fileencoding=utf-8:foldmethod=marker
|
// vim:fileencoding=utf-8:foldmethod=marker
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementations of the SipHash-2-4-128, SipHash4-8-128, SipHash-2-4-64 and SipHash4-8-64
|
* Implementations of the SipHash-2-4-128, SipHash4-8-128 algorithms based on
|
||||||
* algorithms based on https://github.com/veorq/SipHash/blob/master/siphash.c
|
* https://github.com/veorq/SipHash/blob/master/siphash.c
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef SIPHASH_H
|
#ifndef SIPHASH_H
|
||||||
@@ -10,47 +10,17 @@
|
|||||||
|
|
||||||
#include "hasher.h"
|
#include "hasher.h"
|
||||||
#include "../../stream/stream.h"
|
#include "../../stream/stream.h"
|
||||||
#include "../../../common/aliases/aliases.h"
|
|
||||||
#include "../../../common/platform/platform.h"
|
#include "../../../common/platform/platform.h"
|
||||||
|
|
||||||
#ifdef WP_PLATFORM_CPP
|
#ifdef WP_PLATFORM_CPP
|
||||||
BEGIN_C_LINKAGE
|
BEGIN_C_LINKAGE
|
||||||
#endif // !WP_PLATFORM_CPP
|
#endif // !WP_PLATFORM_CPP
|
||||||
|
|
||||||
#define WP_SIP_HASHER_MAGIC wpU64Const(0x5750534950485348)
|
WpHasher wpSip24Hasher(void);
|
||||||
|
WpHasher wpSip48Hasher(void);
|
||||||
typedef struct WpSipHasherKey {
|
WpHasher wpSip24HasherWithKey(WpU128 key);
|
||||||
u64 lo;
|
WpHasher wpSip48HasherWithKey(WpU128 key);
|
||||||
u64 hi;
|
WpU128 wpSipGetHash128(WpU8Stream *bytes, WpHasherCtx ctx);
|
||||||
} WpSipHasherKey;
|
|
||||||
|
|
||||||
typedef struct WpSipHasherData {
|
|
||||||
WpSipHasherKey key;
|
|
||||||
u64 magic;
|
|
||||||
u64 compression;
|
|
||||||
u64 finalisation;
|
|
||||||
} WpSipHasherData;
|
|
||||||
|
|
||||||
#ifdef WP_PLATFORM_CPP
|
|
||||||
#define wpSip24HasherData(KEY) (WpSipHasherData{(KEY), WP_SIP_HASHER_MAGIC, 2, 4})
|
|
||||||
#define wpSip48HasherData(KEY) (WpSipHasherData{(KEY), WP_SIP_HASHER_MAGIC, 4, 8})
|
|
||||||
#else
|
|
||||||
#define wpSip24HasherData(KEY) ((WpSipHasherData){ \
|
|
||||||
.key = (KEY), \
|
|
||||||
.magic = WP_SIP_HASHER_MAGIC, \
|
|
||||||
.compression = 2, \
|
|
||||||
.finalisation = 4, \
|
|
||||||
})
|
|
||||||
#define wpSip48HasherData(KEY) ((WpSipHasherData){ \
|
|
||||||
.key = (KEY), \
|
|
||||||
.magic = WP_SIP_HASHER_MAGIC, \
|
|
||||||
.compression = 4, \
|
|
||||||
.finalisation = 8, \
|
|
||||||
})
|
|
||||||
#endif
|
|
||||||
|
|
||||||
WpHasher wpSipHasher(WpSipHasherData *data);
|
|
||||||
WpU128Hash wpSipHasher128(WpU8Stream *bytes, void *hasher_io);
|
|
||||||
|
|
||||||
#ifdef WP_PLATFORM_CPP
|
#ifdef WP_PLATFORM_CPP
|
||||||
END_C_LINKAGE
|
END_C_LINKAGE
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
// vim:fileencoding=utf-8:foldmethod=marker
|
||||||
|
|
||||||
|
#include "set.h"
|
||||||
|
#include "../table/table.h"
|
||||||
|
|
||||||
|
WpHashSet hashSetAlloc(const WpAllocator *allocator, WpU8StreamEncoder encoder, WpKeyEqualTest key_eq,
|
||||||
|
u64 capacity, u64 key_size) {
|
||||||
|
return hashSetAllocWithCustomHasher(allocator, WP_HASH_TABLE_DEFAULT_HASHER, encoder, key_eq,
|
||||||
|
capacity, key_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
WpHashSet hashSetAllocWithCustomHasher(const WpAllocator *allocator, WpHasher hasher,
|
||||||
|
WpU8StreamEncoder encoder, WpKeyEqualTest key_eq, u64 capacity,
|
||||||
|
u64 key_size) {
|
||||||
|
WpHashSet set = {0};
|
||||||
|
hashTableAlloc(allocator, &set, hasher, encoder, key_eq, capacity, key_size, 0, false);
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
WpHashLookupResult hashSetFind(WpHashSet *set, void *item, u64 item_size) {
|
||||||
|
u64 hash = hashTableCalcHash(set, item, item_size);
|
||||||
|
WpHashBucketRef ref = hashTableBucketRef(set, hash);
|
||||||
|
return hashTableLookup(set, item, ref, item_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hashSetInsertCapped(WpHashSet *set, void *item, u64 item_size) {
|
||||||
|
WpHashLookupResult result = hashSetFind(set, item, item_size);
|
||||||
|
if (!result.found && set->load_factor < WP_HASH_MAX_LOAD_FACTOR) {
|
||||||
|
hashTableInsert(set, item, NULL, result.ref, item_size, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hashSetInsertAlloc(const WpAllocator *allocator, WpHashSet *set, void *item, u64 item_size) {
|
||||||
|
WpHashLookupResult result = hashSetFind(set, item, item_size);
|
||||||
|
if (!result.found) {
|
||||||
|
if (set->load_factor >= WP_HASH_MAX_LOAD_FACTOR) {
|
||||||
|
hashTableGrow(allocator, set, item_size, 0);
|
||||||
|
// Rerun item lookup after growing
|
||||||
|
result = hashSetFind(set, item, item_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
hashTableInsert(set, item, NULL, result.ref, item_size, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hashSetRemove(WpHashSet *set, void *item, u64 item_size) {
|
||||||
|
WpHashLookupResult result = hashSetFind(set, item, item_size);
|
||||||
|
if (result.found) {
|
||||||
|
hashTableRemove(set, result.ref, item_size, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
// vim:fileencoding=utf-8:foldmethod=marker
|
||||||
|
|
||||||
|
#ifndef SET_H
|
||||||
|
#define SET_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 WpHashSet;
|
||||||
|
|
||||||
|
#ifdef WP_PLATFORM_CPP
|
||||||
|
#define wpHashSet(TYPE, U8_STREAM_ENCODER, ITEM_EQ_TEST, CAPACITY) \
|
||||||
|
(WpHashSet{ \
|
||||||
|
WP_HASH_TABLE_DEFAULT_HASHER, \
|
||||||
|
U8_STREAM_ENCODER, \
|
||||||
|
ITEM_EQ_TEST, \
|
||||||
|
wpArrayWithCapacity(TYPE, wpMiscUtilsU64RoundUpPow2(CAPACITY), WP_ARRAY_INIT_NONE), \
|
||||||
|
NULL, \
|
||||||
|
wpArrayWithCapacity(WpHashBucket, wpMiscUtilsU64RoundUpPow2(CAPACITY), WP_ARRAY_INIT_FILLED), \
|
||||||
|
WP_HASH_TABLE_MAGIC, \
|
||||||
|
wpMiscUtilsU64RoundUpPow2(CAPACITY) - 1, \
|
||||||
|
0.0f, \
|
||||||
|
false, \
|
||||||
|
})
|
||||||
|
#define wpHashSetCustomHasher(TYPE, HASHER, U8_STREAM_ENCODER, ITEM_EQ_TEST, CAPACITY) \
|
||||||
|
(WpHashSet{ \
|
||||||
|
HASHER, \
|
||||||
|
U8_STREAM_ENCODER, \
|
||||||
|
ITEM_EQ_TEST, \
|
||||||
|
wpArrayWithCapacity(TYPE, wpMiscUtilsU64RoundUpPow2(CAPACITY), WP_ARRAY_INIT_NONE), \
|
||||||
|
NULL, \
|
||||||
|
wpArrayWithCapacity(WpHashBucket, wpMiscUtilsU64RoundUpPow2(CAPACITY), WP_ARRAY_INIT_FILLED), \
|
||||||
|
WP_HASH_TABLE_MAGIC, \
|
||||||
|
wpMiscUtilsU64RoundUpPow2(CAPACITY) - 1, \
|
||||||
|
0.0f, \
|
||||||
|
false, \
|
||||||
|
})
|
||||||
|
#else
|
||||||
|
#define wpHashSet(TYPE, U8_STREAM_ENCODER, ITEM_EQ_TEST, CAPACITY) \
|
||||||
|
((WpHashSet){ \
|
||||||
|
.hasher = WP_HASH_TABLE_DEFAULT_HASHER, \
|
||||||
|
.encoder = U8_STREAM_ENCODER, \
|
||||||
|
.key_eq = ITEM_EQ_TEST, \
|
||||||
|
.keys = wpArrayWithCapacity(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, \
|
||||||
|
})
|
||||||
|
#define wpHashSetCustomHasher(TYPE, HASHER, U8_STREAM_ENCODER, ITEM_EQ_TEST, CAPACITY) \
|
||||||
|
((WpHashSet){ \
|
||||||
|
.hasher = HASHER, \
|
||||||
|
.encoder = U8_STREAM_ENCODER, \
|
||||||
|
.key_eq = ITEM_EQ_TEST, \
|
||||||
|
.keys = wpArrayWithCapacity(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, \
|
||||||
|
})
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define wpHashSetAlloc(TYPE, ALLOCATOR_PTR, U8_STREAM_ENCODER, ITEM_EQ_TEST, CAPACITY) \
|
||||||
|
hashSetAlloc(ALLOCATOR_PTR, U8_STREAM_ENCODER, ITEM_EQ_TEST, CAPACITY, sizeof(TYPE))
|
||||||
|
#define wpHashSetCustomHasherAlloc(TYPE, ALLOCATOR_PTR, HASHER, U8_STREAM_ENCODER, ITEM_EQ_TEST, \
|
||||||
|
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 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)
|
||||||
|
|
||||||
|
WpHashSet hashSetAlloc(const WpAllocator *allocator, WpU8StreamEncoder encoder,
|
||||||
|
WpKeyEqualTest key_eq, u64 capacity, u64 key_size);
|
||||||
|
WpHashSet hashSetAllocWithCustomHasher(const WpAllocator *allocator, WpHasher hasher,
|
||||||
|
WpU8StreamEncoder encoder, WpKeyEqualTest key_eq,
|
||||||
|
u64 capacity, u64 key_size);
|
||||||
|
WpHashLookupResult hashSetFind(WpHashSet *set, void *item, u64 item_size);
|
||||||
|
void hashSetInsertCapped(WpHashSet *set, void *item, u64 item_size);
|
||||||
|
void hashSetInsertAlloc(const WpAllocator *allocator, WpHashSet *set, void *item,
|
||||||
|
u64 item_size);
|
||||||
|
void hashSetRemove(WpHashSet *set, void *item, u64 item_size);
|
||||||
|
|
||||||
|
#ifdef WP_PLATFORM_CPP
|
||||||
|
END_C_LINKAGE
|
||||||
|
#endif // !WP_PLATFORM_CPP
|
||||||
|
|
||||||
|
#endif // !SET_H
|
||||||
@@ -0,0 +1,292 @@
|
|||||||
|
// vim:fileencoding=utf-8:foldmethod=marker
|
||||||
|
|
||||||
|
#include "table.h"
|
||||||
|
#include "../hasher/hasher.h"
|
||||||
|
#include "../../mem/allocator/mem_allocator.h"
|
||||||
|
#include "../../stream/stream.h"
|
||||||
|
#include "../../../common/aliases/aliases.h"
|
||||||
|
#include "../../../common/assert/assert.h"
|
||||||
|
#include "../../../common/misc/misc_utils.h"
|
||||||
|
|
||||||
|
wp_persist inline void *hashTableGetKeyFromBucket(WpHashTable *table, WpHashBucket bucket, u64 key_size);
|
||||||
|
wp_persist inline void *hashTableGetValueFromBucket(WpHashTable *table, WpHashBucket bucket, u64 value_size);
|
||||||
|
wp_persist inline u64 hashTableGetDist(u64 dist_and_tag);
|
||||||
|
wp_persist inline u64 hashTableNextBucket(WpHashTable *table, u64 bucket_index);
|
||||||
|
wp_persist inline u64 hashTableDistInc(u64 dist_and_tag);
|
||||||
|
wp_persist inline u64 hashTableDistDec(u64 dist_and_tag);
|
||||||
|
wp_persist inline void hashTableValidate(const WpHashTable *table, u64 key_size, u64 value_size);
|
||||||
|
|
||||||
|
void hashTableAlloc(const WpAllocator *allocator, WpHashTable *table, WpHasher hasher,
|
||||||
|
WpU8StreamEncoder encoder, WpKeyEqualTest key_eq, u64 capacity, u64 key_size,
|
||||||
|
u64 value_size, b8 has_values) {
|
||||||
|
wpDebugAssert(allocator != NULL && table != NULL && encoder != NULL && key_eq != NULL,
|
||||||
|
"`allocator`, `table`, `encoder` and `key_eq` should not be NULL");
|
||||||
|
|
||||||
|
u64 table_capacity = wpMiscUtilsU64RoundUpPow2(capacity);
|
||||||
|
table->hasher = hasher;
|
||||||
|
table->encoder = encoder;
|
||||||
|
table->key_eq = key_eq;
|
||||||
|
table->keys = arrayAllocCapacity(allocator, table_capacity, WP_ARRAY_INIT_NONE, key_size);;
|
||||||
|
table->buckets = wpArrayAllocCapacity(WpHashBucket, allocator, table_capacity, WP_ARRAY_INIT_FILLED);
|
||||||
|
table->magic = WP_HASH_TABLE_MAGIC;
|
||||||
|
table->bucket_mask = table_capacity - 1;
|
||||||
|
table->load_factor = WP_HASH_MAX_LOAD_FACTOR;
|
||||||
|
table->has_values = has_values;
|
||||||
|
|
||||||
|
wpRuntimeAssert(table->keys != NULL && table->buckets != NULL, "Failed to allocate keys, buckets or both");
|
||||||
|
|
||||||
|
if (has_values) {
|
||||||
|
table->values = arrayAllocCapacity(allocator, table_capacity, WP_ARRAY_INIT_NONE, value_size);;
|
||||||
|
wpRuntimeAssert(table->values != NULL, "Failed to allocate values");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 hashTableCalcHash(WpHashTable *table, void *key, u64 key_size) {
|
||||||
|
hashTableValidate(table, key_size, 0);
|
||||||
|
wpDebugAssert(key != NULL, "`key` should not be NULL");
|
||||||
|
WpU8Stream stream = table->encoder(key);
|
||||||
|
return wpHasherGetHash64(&table->hasher, &stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
WpHashBucketRef hashTableBucketRef(WpHashTable *table, u64 hash) {
|
||||||
|
hashTableValidate(table, 0, 0);
|
||||||
|
return (WpHashBucketRef){
|
||||||
|
.dist_and_tag = WP_HASH_DIST_INC | (hash & WP_HASH_TAG_MASK),
|
||||||
|
.bucket_index = hash & table->bucket_mask,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void *hashTableKeys(WpHashTable *table, u64 key_size) {
|
||||||
|
hashTableValidate(table, key_size, 0);
|
||||||
|
return table->keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *hashTableValues(WpHashTable *table, u64 value_size) {
|
||||||
|
hashTableValidate(table, 0, value_size);
|
||||||
|
return table->values;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *hashTableGetKeyFromBucketRef(WpHashTable *table, WpHashBucketRef ref, u64 key_size) {
|
||||||
|
return hashTableGetKeyFromBucket(table, table->buckets[ref.bucket_index], key_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *hashTableGetValueFromBucketRef(WpHashTable *table, WpHashBucketRef ref, u64 value_size) {
|
||||||
|
return hashTableGetValueFromBucket(table, table->buckets[ref.bucket_index], value_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 hashTableCapacity(WpHashTable *table) {
|
||||||
|
hashTableValidate(table, 0, 0);
|
||||||
|
return wpArrayCapacity(table->keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 hashTableCount(WpHashTable *table) {
|
||||||
|
hashTableValidate(table, 0, 0);
|
||||||
|
return wpArrayCount(table->keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 hashTableKeySize(WpHashTable *table) {
|
||||||
|
hashTableValidate(table, 0, 0);
|
||||||
|
return wpArrayItemSize(table->keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 hashTableValueSize(WpHashTable *table) {
|
||||||
|
hashTableValidate(table, 0, 0);
|
||||||
|
return table->has_values ? wpArrayItemSize(table->values) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hashTableGrow(const WpAllocator *allocator, WpHashTable *table, u64 key_size, u64 value_size) {
|
||||||
|
hashTableValidate(table, key_size, value_size);
|
||||||
|
wpDebugAssert(allocator != NULL, "`allocator` should not be NULL");
|
||||||
|
|
||||||
|
u64 capacity = wpMiscUtilsU64RoundUpPow2(hashTableCapacity(table) * 2);
|
||||||
|
u64 mask = capacity - wpU64Const(1);
|
||||||
|
WpArray keys = arrayAllocCapacity(allocator, capacity, WP_ARRAY_INIT_NONE, key_size);
|
||||||
|
WpArray values = NULL;
|
||||||
|
WpHashBucketArray buckets = wpArrayAllocCapacity(WpHashBucket, allocator, capacity, WP_ARRAY_INIT_FILLED);
|
||||||
|
|
||||||
|
wpRuntimeAssert(keys != NULL && buckets != NULL, "Failed to allocate keys, buckets or both");
|
||||||
|
|
||||||
|
if (table->has_values) {
|
||||||
|
values = arrayAllocCapacity(allocator, capacity, WP_ARRAY_INIT_NONE, value_size);
|
||||||
|
wpRuntimeAssert(values != NULL, "Failed to allocate values");
|
||||||
|
}
|
||||||
|
|
||||||
|
WpArray old_keys = table->keys;
|
||||||
|
WpArray old_values = table->values;
|
||||||
|
|
||||||
|
table->keys = keys;
|
||||||
|
table->values = values;
|
||||||
|
table->buckets = buckets;
|
||||||
|
table->bucket_mask = mask;
|
||||||
|
|
||||||
|
for (u64 i = 0; i < wpArrayCount(old_keys); ++i) {
|
||||||
|
void *key = arrayGet(old_keys, i, key_size);
|
||||||
|
void *value = table->has_values ? arrayGet(old_values, i, value_size) : NULL;
|
||||||
|
u64 hash = hashTableCalcHash(table, key, key_size);
|
||||||
|
WpHashBucketRef ref = hashTableBucketRef(table, hash);
|
||||||
|
hashTableInsert(table, key, value, ref, key_size, value_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WpHashLookupResult hashTableLookup(WpHashTable *table, void *key, WpHashBucketRef ref, u64 key_size) {
|
||||||
|
hashTableValidate(table, key_size, 0);
|
||||||
|
wpDebugAssert(key != NULL, "`key` should not be NULL");
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
WpHashBucket *bucket = &table->buckets[ref.bucket_index];
|
||||||
|
|
||||||
|
if (ref.dist_and_tag == bucket->dist_and_tag) {
|
||||||
|
b8 key_equal = table->key_eq(key, arrayGet(table->keys, bucket->item_index, key_size));
|
||||||
|
return (WpHashLookupResult){
|
||||||
|
.ref.dist_and_tag = ref.dist_and_tag,
|
||||||
|
.ref.bucket_index = ref.bucket_index,
|
||||||
|
.found = key_equal,
|
||||||
|
};
|
||||||
|
} else if (hashTableGetDist(ref.dist_and_tag) > hashTableGetDist(bucket->dist_and_tag)) {
|
||||||
|
return (WpHashLookupResult){
|
||||||
|
.ref.dist_and_tag = ref.dist_and_tag,
|
||||||
|
.ref.bucket_index = ref.bucket_index,
|
||||||
|
.found = false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ref.dist_and_tag = hashTableDistInc(ref.dist_and_tag);
|
||||||
|
ref.bucket_index = hashTableNextBucket(table, ref.bucket_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hashTableUpdate(WpHashTable *table, void *value, WpHashBucketRef ref, u64 value_size) {
|
||||||
|
hashTableValidate(table, 0, value_size);
|
||||||
|
if (!(table->has_values)) {
|
||||||
|
wpRuntimeAssert(0, "Attempting to update a hash table that has no values");
|
||||||
|
}
|
||||||
|
wpDebugAssert(value != NULL, "`value` should not be NULL");
|
||||||
|
|
||||||
|
WpHashBucket *bucket = &table->buckets[ref.bucket_index];
|
||||||
|
arraySet(table->values, bucket->item_index, value, value_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hashTableInsert(WpHashTable *table, void *key, void *value, WpHashBucketRef ref, u64 key_size,
|
||||||
|
u64 value_size) {
|
||||||
|
hashTableValidate(table, key_size, value_size);
|
||||||
|
wpDebugAssert(key != NULL, "`key` should not be NULL");
|
||||||
|
|
||||||
|
u64 index = wpArrayCount(table->keys);
|
||||||
|
while (true) {
|
||||||
|
WpHashBucket *bucket = &table->buckets[ref.bucket_index];
|
||||||
|
|
||||||
|
if (bucket->dist_and_tag == 0) {
|
||||||
|
bucket->dist_and_tag = ref.dist_and_tag;
|
||||||
|
bucket->item_index = index;
|
||||||
|
break;
|
||||||
|
} else if (hashTableGetDist(ref.dist_and_tag) > hashTableGetDist(bucket->dist_and_tag)) {
|
||||||
|
wpMiscUtilsSwap(u64, &ref.dist_and_tag, &bucket->dist_and_tag);
|
||||||
|
wpMiscUtilsSwap(u64, &index, &bucket->item_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
ref.dist_and_tag = hashTableDistInc(ref.dist_and_tag);
|
||||||
|
ref.bucket_index = hashTableNextBucket(table, ref.bucket_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
arrayAppendCapped(table->keys, key, key_size);
|
||||||
|
if (table->has_values) {
|
||||||
|
arrayAppendCapped(table->values, value, value_size);
|
||||||
|
}
|
||||||
|
table->load_factor = (f32)wpArrayCount(table->keys) / (f32)wpArrayCapacity(table->keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hashTableRemove(WpHashTable *table, WpHashBucketRef ref, u64 key_size, u64 value_size) {
|
||||||
|
hashTableValidate(table, key_size, value_size);
|
||||||
|
|
||||||
|
WpHashBucket *bucket = &table->buckets[ref.bucket_index];
|
||||||
|
|
||||||
|
u64 last_item_index = wpArrayCount(table->keys) - 1;
|
||||||
|
|
||||||
|
if (bucket->item_index < last_item_index) {
|
||||||
|
// Update the bucket for the last item before swapping
|
||||||
|
u64 hash = hashTableCalcHash(table, arrayGet(table->keys, last_item_index, key_size), key_size);
|
||||||
|
|
||||||
|
WpHashLookupResult result = hashTableLookup(table, arrayGet(table->keys, last_item_index, key_size),
|
||||||
|
hashTableBucketRef(table, hash), key_size);
|
||||||
|
wpRuntimeAssert(result.found, "Hash table item should exist but it doesn't");
|
||||||
|
|
||||||
|
table->buckets[result.ref.bucket_index].item_index = bucket->item_index;
|
||||||
|
|
||||||
|
arrayRemoveSwapEnd(table->keys, bucket->item_index, key_size);
|
||||||
|
if (table->has_values) {
|
||||||
|
arrayRemoveSwapEnd(table->values, bucket->item_index, value_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (bucket->dist_and_tag > 0) {
|
||||||
|
u64 next_index = hashTableNextBucket(table, ref.bucket_index);
|
||||||
|
WpHashBucket *next = &table->buckets[next_index];
|
||||||
|
|
||||||
|
if (hashTableGetDist(next->dist_and_tag) > WP_HASH_DIST_INC) {
|
||||||
|
bucket->dist_and_tag = hashTableDistDec(next->dist_and_tag);
|
||||||
|
bucket->item_index = next->item_index;
|
||||||
|
|
||||||
|
ref.bucket_index = next_index;
|
||||||
|
bucket = &table->buckets[ref.bucket_index];
|
||||||
|
} else {
|
||||||
|
bucket->dist_and_tag = 0;
|
||||||
|
bucket->item_index = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hashTableClear(WpHashTable *table, u64 key_size, u64 value_size) {
|
||||||
|
hashTableValidate(table, key_size, value_size);
|
||||||
|
arrayClear(table->keys, key_size);
|
||||||
|
if (table->has_values) {
|
||||||
|
arrayClear(table->values, value_size);
|
||||||
|
}
|
||||||
|
wpArrayZero(WpHashBucket, table->buckets);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hashTableDealloc(const WpAllocator *allocator, WpHashTable *table, u64 key_size, u64 value_size) {
|
||||||
|
wpDebugAssert(allocator != NULL, "`allocator` should not be NULL");
|
||||||
|
hashTableValidate(table, key_size, value_size);
|
||||||
|
arrayDealloc(allocator, table->keys, key_size);
|
||||||
|
wpArrayDealloc(WpHashBucket, allocator, table->buckets);
|
||||||
|
if (table->has_values) {
|
||||||
|
arrayDealloc(allocator, table->values, value_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_persist inline void *hashTableGetKeyFromBucket(WpHashTable *table, WpHashBucket bucket, u64 key_size) {
|
||||||
|
hashTableValidate(table, key_size, 0);
|
||||||
|
return arrayGet(table->keys, bucket.item_index, key_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_persist inline void *hashTableGetValueFromBucket(WpHashTable *table, WpHashBucket bucket, u64 value_size) {
|
||||||
|
hashTableValidate(table, 0, value_size);
|
||||||
|
return table->has_values ? arrayGet(table->values, bucket.item_index, value_size) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_persist inline u64 hashTableGetDist(u64 dist_and_tag) {
|
||||||
|
return dist_and_tag & (~WP_HASH_TAG_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_persist inline u64 hashTableNextBucket(WpHashTable *table, u64 bucket_index) {
|
||||||
|
return (++bucket_index) & table->bucket_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_persist inline u64 hashTableDistInc(u64 dist_and_tag) {
|
||||||
|
return dist_and_tag + WP_HASH_DIST_INC;
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_persist inline u64 hashTableDistDec(u64 dist_and_tag) {
|
||||||
|
return dist_and_tag - WP_HASH_DIST_INC;
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_persist inline void hashTableValidate(const WpHashTable *table, u64 key_size, u64 value_size) {
|
||||||
|
wpDebugAssert(table != NULL, "`table` should not be NULL");
|
||||||
|
wpRuntimeAssert(WP_HASH_TABLE_MAGIC == table->magic, "`table` is not a valid wapp hash table");
|
||||||
|
if (key_size > 0) {
|
||||||
|
wpRuntimeAssert(key_size == wpArrayItemSize(table->keys), "Invalid key type provided");
|
||||||
|
}
|
||||||
|
if (value_size > 0) {
|
||||||
|
wpRuntimeAssert(value_size == wpArrayItemSize(table->values), "Invalid value type provided");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
// vim:fileencoding=utf-8:foldmethod=marker
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A hash table implementation with Robin Hood open addressing for collision resolution.
|
||||||
|
*
|
||||||
|
* The implementation is based on a version of this C++ implementation:
|
||||||
|
* https://github.com/martinus/unordered_dense
|
||||||
|
*
|
||||||
|
* Extra references:
|
||||||
|
* - https://faultlore.com/blah/robinhood-part-1/
|
||||||
|
* - https://blog.hieunt.me/blog/robinhood-hashing
|
||||||
|
*
|
||||||
|
* IMPORTANT:
|
||||||
|
* This is the internal hash table implementation and it is not intended to be used directly.
|
||||||
|
* Please check ../map/map.h and ../set/set.h for the public APIs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TABLE_H
|
||||||
|
#define TABLE_H
|
||||||
|
|
||||||
|
#include "../hasher/hasher.h"
|
||||||
|
#include "../hasher/murmur3.h"
|
||||||
|
#include "../../array/array.h"
|
||||||
|
#include "../../mem/allocator/mem_allocator.h"
|
||||||
|
#include "../../stream/stream.h"
|
||||||
|
#include "../../../common/aliases/aliases.h"
|
||||||
|
#include "../../../common/platform/platform.h"
|
||||||
|
|
||||||
|
#ifdef WP_PLATFORM_CPP
|
||||||
|
BEGIN_C_LINKAGE
|
||||||
|
#endif // !WP_PLATFORM_CPP
|
||||||
|
|
||||||
|
#define WP_HASH_TABLE_MAGIC wpU64Const(0x57504854424c)
|
||||||
|
#define WP_HASH_TAG_BITS wpU64Const(32)
|
||||||
|
#define WP_HASH_DIST_INC (wpU64Const(1) << WP_HASH_TAG_BITS)
|
||||||
|
#define WP_HASH_TAG_MASK (WP_HASH_DIST_INC - wpU64Const(1))
|
||||||
|
#define WP_HASH_MAX_LOAD_FACTOR 0.65f
|
||||||
|
#define WP_HASH_TABLE_DEFAULT_HASHER wpX64Mur3Hasher()
|
||||||
|
|
||||||
|
typedef WpU8Stream (*WpU8StreamEncoder)(void *data);
|
||||||
|
typedef b8 (*WpKeyEqualTest)(void *a, void *b);
|
||||||
|
|
||||||
|
typedef struct WpHashBucket {
|
||||||
|
u64 dist_and_tag;
|
||||||
|
u64 item_index;
|
||||||
|
} WpHashBucket;
|
||||||
|
typedef WpHashBucket *WpHashBucketArray;
|
||||||
|
|
||||||
|
typedef struct WpHashTable {
|
||||||
|
WpHasher hasher;
|
||||||
|
WpU8StreamEncoder encoder;
|
||||||
|
WpKeyEqualTest key_eq;
|
||||||
|
WpArray keys;
|
||||||
|
WpArray values;
|
||||||
|
WpHashBucketArray buckets;
|
||||||
|
u64 magic;
|
||||||
|
u64 bucket_mask;
|
||||||
|
f32 load_factor;
|
||||||
|
b8 has_values;
|
||||||
|
} WpHashTable;
|
||||||
|
|
||||||
|
typedef struct WpHashBucketRef {
|
||||||
|
u64 dist_and_tag;
|
||||||
|
u64 bucket_index;
|
||||||
|
} WpHashBucketRef;
|
||||||
|
|
||||||
|
typedef struct WpHashLookupResult {
|
||||||
|
WpHashBucketRef ref;
|
||||||
|
b8 found;
|
||||||
|
} WpHashLookupResult;
|
||||||
|
|
||||||
|
void hashTableAlloc(const WpAllocator *allocator, WpHashTable *table,
|
||||||
|
WpHasher hasher, WpU8StreamEncoder encoder,
|
||||||
|
WpKeyEqualTest key_eq, u64 capacity, u64 key_size,
|
||||||
|
u64 value_size, b8 has_values);
|
||||||
|
u64 hashTableCalcHash(WpHashTable *table, void *key, u64 key_size);
|
||||||
|
WpHashBucketRef hashTableBucketRef(WpHashTable *table, u64 hash);
|
||||||
|
void *hashTableKeys(WpHashTable *table, u64 key_size);
|
||||||
|
void *hashTableValues(WpHashTable *table, u64 value_size);
|
||||||
|
void *hashTableGetKeyFromBucketRef(WpHashTable *table, WpHashBucketRef ref, u64 key_size);
|
||||||
|
void *hashTableGetValueFromBucketRef(WpHashTable *table, WpHashBucketRef ref, u64 value_size);
|
||||||
|
u64 hashTableCapacity(WpHashTable *table);
|
||||||
|
u64 hashTableCount(WpHashTable *table);
|
||||||
|
u64 hashTableKeySize(WpHashTable *table);
|
||||||
|
u64 hashTableValueSize(WpHashTable *table);
|
||||||
|
void hashTableGrow(const WpAllocator *allocator, WpHashTable *table, u64 key_size,
|
||||||
|
u64 value_size);
|
||||||
|
WpHashLookupResult hashTableLookup(WpHashTable *table, void *key, WpHashBucketRef ref, u64 key_size);
|
||||||
|
void hashTableUpdate(WpHashTable *table, void *value, WpHashBucketRef ref, u64 value_size);
|
||||||
|
void hashTableInsert(WpHashTable *table, void *key, void *value, WpHashBucketRef ref,
|
||||||
|
u64 key_size, u64 value_size);
|
||||||
|
void hashTableRemove(WpHashTable *table, WpHashBucketRef ref, u64 key_size, u64 value_size);
|
||||||
|
void hashTableClear(WpHashTable *table, u64 key_size, u64 value_size);
|
||||||
|
void hashTableDealloc(const WpAllocator *allocator, WpHashTable *table, u64 key_size,
|
||||||
|
u64 value_size);
|
||||||
|
|
||||||
|
#ifdef WP_PLATFORM_CPP
|
||||||
|
END_C_LINKAGE
|
||||||
|
#endif // !WP_PLATFORM_CPP
|
||||||
|
|
||||||
|
#endif // !TABLE_H
|
||||||
@@ -9,6 +9,9 @@
|
|||||||
#include "hash/hasher/hasher.c"
|
#include "hash/hasher/hasher.c"
|
||||||
#include "hash/hasher/murmur3.c"
|
#include "hash/hasher/murmur3.c"
|
||||||
#include "hash/hasher/siphash.c"
|
#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 "queue/queue.c"
|
||||||
#include "stream/stream.c"
|
#include "stream/stream.c"
|
||||||
#include "mem/allocator/mem_allocator.c"
|
#include "mem/allocator/mem_allocator.c"
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
#include "hash/hasher/hasher.h"
|
#include "hash/hasher/hasher.h"
|
||||||
#include "hash/hasher/murmur3.h"
|
#include "hash/hasher/murmur3.h"
|
||||||
#include "hash/hasher/siphash.h"
|
#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 "queue/queue.h"
|
||||||
#include "stream/stream.h"
|
#include "stream/stream.h"
|
||||||
#include "mem/allocator/mem_allocator.h"
|
#include "mem/allocator/mem_allocator.h"
|
||||||
|
|||||||
@@ -31,24 +31,29 @@ BEGIN_C_LINKAGE
|
|||||||
#define U64_RSHIFT_OR_8(X) (((u64)X) | (((u64)X) >> 8))
|
#define U64_RSHIFT_OR_8(X) (((u64)X) | (((u64)X) >> 8))
|
||||||
#define U64_RSHIFT_OR_16(X) (((u64)X) | (((u64)X) >> 16))
|
#define U64_RSHIFT_OR_16(X) (((u64)X) | (((u64)X) >> 16))
|
||||||
#define U64_RSHIFT_OR_32(X) (((u64)X) | (((u64)X) >> 32))
|
#define U64_RSHIFT_OR_32(X) (((u64)X) | (((u64)X) >> 32))
|
||||||
#define wpMiscUtilsU64RoundUpPow2(X) ( \
|
#define wpMiscUtilsU64RoundUpPow2(X) ( \
|
||||||
( \
|
( \
|
||||||
U64_RSHIFT_OR_32( \
|
U64_RSHIFT_OR_32( \
|
||||||
U64_RSHIFT_OR_16( \
|
U64_RSHIFT_OR_16( \
|
||||||
U64_RSHIFT_OR_8( \
|
U64_RSHIFT_OR_8( \
|
||||||
U64_RSHIFT_OR_4( \
|
U64_RSHIFT_OR_4( \
|
||||||
U64_RSHIFT_OR_2( \
|
U64_RSHIFT_OR_2( \
|
||||||
U64_RSHIFT_OR_1(X - 1) \
|
U64_RSHIFT_OR_1(X - 1) \
|
||||||
) \
|
) \
|
||||||
) \
|
) \
|
||||||
) \
|
) \
|
||||||
) \
|
) \
|
||||||
) \
|
) \
|
||||||
) + 1 \
|
) + 1 \
|
||||||
)
|
)
|
||||||
|
|
||||||
#define wpMiscUtilsIsPowerOfTwo(NUM) ((NUM & (NUM - 1)) == 0)
|
#define wpMiscUtilsIsPowerOfTwo(NUM) ((NUM & (NUM - 1)) == 0)
|
||||||
#define wpMiscUtilsOffsetPointer(PTR, OFFSET) ((void *)((uptr)(PTR) + (OFFSET)))
|
#define wpMiscUtilsOffsetPointer(PTR, OFFSET) ((void *)((uptr)(PTR) + (OFFSET)))
|
||||||
|
#define wpMiscUtilsSwap(TYPE, A_PTR, B_PTR) do { \
|
||||||
|
TYPE tmp = *(A_PTR); \
|
||||||
|
*(A_PTR) = *(B_PTR); \
|
||||||
|
*(B_PTR) = tmp; \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
wp_intern inline u64 wpMiscUtilsRotl64(u64 x, i8 rotation) {
|
wp_intern inline u64 wpMiscUtilsRotl64(u64 x, i8 rotation) {
|
||||||
return (x << rotation) | (x >> (64 - rotation));
|
return (x << rotation) | (x >> (64 - rotation));
|
||||||
|
|||||||
+21
-21
@@ -12,7 +12,7 @@ WpTestFuncResult test_murmur3(void) {
|
|||||||
WpI32Array arr = wpArray(i32, 1, 2, 3, 4, 5);
|
WpI32Array arr = wpArray(i32, 1, 2, 3, 4, 5);
|
||||||
u64 num = 287324;
|
u64 num = 287324;
|
||||||
|
|
||||||
WpMur3HasherData data = wpMur3HasherData(873923);
|
WpHasher hasher = wpX64Mur3HasherWithSeed(873923);
|
||||||
|
|
||||||
WpU8Stream str_bytes = wpStream(u8, str.buf, str.size);
|
WpU8Stream str_bytes = wpStream(u8, str.buf, str.size);
|
||||||
WpU8Stream arr_bytes = wpStream(u8, (u8 *)arr, wpArrayCount(arr) * wpArrayItemSize(arr));
|
WpU8Stream arr_bytes = wpStream(u8, (u8 *)arr, wpArrayCount(arr) * wpArrayItemSize(arr));
|
||||||
@@ -21,22 +21,22 @@ WpTestFuncResult test_murmur3(void) {
|
|||||||
u64 hash[2] = {0};
|
u64 hash[2] = {0};
|
||||||
|
|
||||||
// Sring hash
|
// Sring hash
|
||||||
WpU128Hash mur_hash = wpX64Mur3Hasher128(&str_bytes, (void *)&data);
|
WpU128 mur_hash = hasher.func(&str_bytes, hasher.ctx);
|
||||||
MurmurHash3_x64_128(str_bytes.data, str_bytes.count * str_bytes.item_size, data.seed, (void *)hash);
|
MurmurHash3_x64_128(str_bytes.data, str_bytes.count * str_bytes.item_size, hasher.ctx.murmur3, (void *)hash);
|
||||||
|
|
||||||
result = result && mur_hash.hash[0] == hash[0] && mur_hash.hash[1] == hash[1];
|
result = result && mur_hash.value[0] == hash[0] && mur_hash.value[1] == hash[1];
|
||||||
|
|
||||||
// Array hash
|
// Array hash
|
||||||
mur_hash = wpX64Mur3Hasher128(&arr_bytes, (void *)&data);
|
mur_hash = hasher.func(&arr_bytes, hasher.ctx);
|
||||||
MurmurHash3_x64_128(arr_bytes.data, arr_bytes.count * arr_bytes.item_size, data.seed, (void *)hash);
|
MurmurHash3_x64_128(arr_bytes.data, arr_bytes.count * arr_bytes.item_size, hasher.ctx.murmur3, (void *)hash);
|
||||||
|
|
||||||
result = result && mur_hash.hash[0] == hash[0] && mur_hash.hash[1] == hash[1];
|
result = result && mur_hash.value[0] == hash[0] && mur_hash.value[1] == hash[1];
|
||||||
|
|
||||||
// Number hash
|
// Number hash
|
||||||
mur_hash = wpX64Mur3Hasher128(&num_bytes, (void *)&data);
|
mur_hash = hasher.func(&num_bytes, hasher.ctx);
|
||||||
MurmurHash3_x64_128(num_bytes.data, num_bytes.count * num_bytes.item_size, data.seed, (void *)hash);
|
MurmurHash3_x64_128(num_bytes.data, num_bytes.count * num_bytes.item_size, hasher.ctx.murmur3, (void *)hash);
|
||||||
|
|
||||||
result = result && mur_hash.hash[0] == hash[0] && mur_hash.hash[1] == hash[1];
|
result = result && mur_hash.value[0] == hash[0] && mur_hash.value[1] == hash[1];
|
||||||
|
|
||||||
return wpTesterResult(result);
|
return wpTesterResult(result);
|
||||||
}
|
}
|
||||||
@@ -48,8 +48,8 @@ WpTestFuncResult test_siphash_128(void) {
|
|||||||
WpI32Array arr = wpArray(i32, 1, 2, 3, 4, 5);
|
WpI32Array arr = wpArray(i32, 1, 2, 3, 4, 5);
|
||||||
u64 num = 287324;
|
u64 num = 287324;
|
||||||
|
|
||||||
WpSipHasherKey key = { .lo = 238742, .hi = 671734 };
|
WpU128 key = { .value = { 238742, 671734 } };
|
||||||
WpSipHasherData data = wpSip24HasherData(key);
|
WpHasher hasher = wpSip24HasherWithKey(key);
|
||||||
|
|
||||||
WpU8Stream str_bytes = wpStream(u8, str.buf, str.size);
|
WpU8Stream str_bytes = wpStream(u8, str.buf, str.size);
|
||||||
WpU8Stream arr_bytes = wpStream(u8, (u8 *)arr, wpArrayCount(arr) * wpArrayItemSize(arr));
|
WpU8Stream arr_bytes = wpStream(u8, (u8 *)arr, wpArrayCount(arr) * wpArrayItemSize(arr));
|
||||||
@@ -58,25 +58,25 @@ WpTestFuncResult test_siphash_128(void) {
|
|||||||
u64 hash[2] = {0};
|
u64 hash[2] = {0};
|
||||||
|
|
||||||
// Sring hash
|
// Sring hash
|
||||||
WpU128Hash sip_hash = wpSipHasher128(&str_bytes, (void *)&data);
|
WpU128 sip_hash = hasher.func(&str_bytes, hasher.ctx);
|
||||||
siphash(str_bytes.data, str_bytes.count * str_bytes.item_size, (void *)&data.key,
|
siphash(str_bytes.data, str_bytes.count * str_bytes.item_size, (void *)&hasher.ctx.siphash.key,
|
||||||
(u8 *)hash, sizeof(u64) * 2);
|
(u8 *)hash, sizeof(u64) * 2);
|
||||||
|
|
||||||
result = result && sip_hash.hash[0] == hash[0] && sip_hash.hash[1] == hash[1];
|
result = result && sip_hash.value[0] == hash[0] && sip_hash.value[1] == hash[1];
|
||||||
|
|
||||||
// Array hash
|
// Array hash
|
||||||
sip_hash = wpSipHasher128(&arr_bytes, (void *)&data);
|
sip_hash = hasher.func(&arr_bytes, hasher.ctx);
|
||||||
siphash(arr_bytes.data, arr_bytes.count * arr_bytes.item_size, (void *)&data.key,
|
siphash(arr_bytes.data, arr_bytes.count * arr_bytes.item_size, (void *)&hasher.ctx.siphash.key,
|
||||||
(u8 *)hash, sizeof(u64) * 2);
|
(u8 *)hash, sizeof(u64) * 2);
|
||||||
|
|
||||||
result = result && sip_hash.hash[0] == hash[0] && sip_hash.hash[1] == hash[1];
|
result = result && sip_hash.value[0] == hash[0] && sip_hash.value[1] == hash[1];
|
||||||
|
|
||||||
// Number hash
|
// Number hash
|
||||||
sip_hash = wpSipHasher128(&num_bytes, (void *)&data);
|
sip_hash = hasher.func(&num_bytes, hasher.ctx);
|
||||||
siphash(num_bytes.data, num_bytes.count * num_bytes.item_size, (void *)&data.key,
|
siphash(num_bytes.data, num_bytes.count * num_bytes.item_size, (void *)&hasher.ctx.siphash.key,
|
||||||
(u8 *)hash, sizeof(u64) * 2);
|
(u8 *)hash, sizeof(u64) * 2);
|
||||||
|
|
||||||
result = result && sip_hash.hash[0] == hash[0] && sip_hash.hash[1] == hash[1];
|
result = result && sip_hash.value[0] == hash[0] && sip_hash.value[1] == hash[1];
|
||||||
|
|
||||||
return wpTesterResult(result);
|
return wpTesterResult(result);
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-21
@@ -12,7 +12,7 @@ WpTestFuncResult test_murmur3(void) {
|
|||||||
WpI32Array arr = wpArray(i32, 1, 2, 3, 4, 5);
|
WpI32Array arr = wpArray(i32, 1, 2, 3, 4, 5);
|
||||||
u64 num = 287324;
|
u64 num = 287324;
|
||||||
|
|
||||||
WpMur3HasherData data = wpMur3HasherData(873923);
|
WpHasher hasher = wpX64Mur3HasherWithSeed(873923);
|
||||||
|
|
||||||
WpU8Stream str_bytes = wpStream(u8, str.buf, str.size);
|
WpU8Stream str_bytes = wpStream(u8, str.buf, str.size);
|
||||||
WpU8Stream arr_bytes = wpStream(u8, (u8 *)arr, wpArrayCount(arr) * wpArrayItemSize(arr));
|
WpU8Stream arr_bytes = wpStream(u8, (u8 *)arr, wpArrayCount(arr) * wpArrayItemSize(arr));
|
||||||
@@ -21,22 +21,22 @@ WpTestFuncResult test_murmur3(void) {
|
|||||||
u64 hash[2] = {0};
|
u64 hash[2] = {0};
|
||||||
|
|
||||||
// Sring hash
|
// Sring hash
|
||||||
WpU128Hash mur_hash = wpX64Mur3Hasher128(&str_bytes, (void *)&data);
|
WpU128 mur_hash = hasher.func(&str_bytes, hasher.ctx);
|
||||||
MurmurHash3_x64_128(str_bytes.data, str_bytes.count * str_bytes.item_size, data.seed, (void *)hash);
|
MurmurHash3_x64_128(str_bytes.data, str_bytes.count * str_bytes.item_size, hasher.ctx.murmur3, (void *)hash);
|
||||||
|
|
||||||
result = result && mur_hash.hash[0] == hash[0] && mur_hash.hash[1] == hash[1];
|
result = result && mur_hash.value[0] == hash[0] && mur_hash.value[1] == hash[1];
|
||||||
|
|
||||||
// Array hash
|
// Array hash
|
||||||
mur_hash = wpX64Mur3Hasher128(&arr_bytes, (void *)&data);
|
mur_hash = hasher.func(&arr_bytes, hasher.ctx);
|
||||||
MurmurHash3_x64_128(arr_bytes.data, arr_bytes.count * arr_bytes.item_size, data.seed, (void *)hash);
|
MurmurHash3_x64_128(arr_bytes.data, arr_bytes.count * arr_bytes.item_size, hasher.ctx.murmur3, (void *)hash);
|
||||||
|
|
||||||
result = result && mur_hash.hash[0] == hash[0] && mur_hash.hash[1] == hash[1];
|
result = result && mur_hash.value[0] == hash[0] && mur_hash.value[1] == hash[1];
|
||||||
|
|
||||||
// Number hash
|
// Number hash
|
||||||
mur_hash = wpX64Mur3Hasher128(&num_bytes, (void *)&data);
|
mur_hash = hasher.func(&num_bytes, hasher.ctx);
|
||||||
MurmurHash3_x64_128(num_bytes.data, num_bytes.count * num_bytes.item_size, data.seed, (void *)hash);
|
MurmurHash3_x64_128(num_bytes.data, num_bytes.count * num_bytes.item_size, hasher.ctx.murmur3, (void *)hash);
|
||||||
|
|
||||||
result = result && mur_hash.hash[0] == hash[0] && mur_hash.hash[1] == hash[1];
|
result = result && mur_hash.value[0] == hash[0] && mur_hash.value[1] == hash[1];
|
||||||
|
|
||||||
return wpTesterResult(result);
|
return wpTesterResult(result);
|
||||||
}
|
}
|
||||||
@@ -48,8 +48,8 @@ WpTestFuncResult test_siphash_128(void) {
|
|||||||
WpI32Array arr = wpArray(i32, 1, 2, 3, 4, 5);
|
WpI32Array arr = wpArray(i32, 1, 2, 3, 4, 5);
|
||||||
u64 num = 287324;
|
u64 num = 287324;
|
||||||
|
|
||||||
WpSipHasherKey key = { 238742, 671734 };
|
WpU128 key = { { 238742, 671734 } };
|
||||||
WpSipHasherData data = wpSip24HasherData(key);
|
WpHasher hasher = wpSip24HasherWithKey(key);
|
||||||
|
|
||||||
WpU8Stream str_bytes = wpStream(u8, str.buf, str.size);
|
WpU8Stream str_bytes = wpStream(u8, str.buf, str.size);
|
||||||
WpU8Stream arr_bytes = wpStream(u8, (u8 *)arr, wpArrayCount(arr) * wpArrayItemSize(arr));
|
WpU8Stream arr_bytes = wpStream(u8, (u8 *)arr, wpArrayCount(arr) * wpArrayItemSize(arr));
|
||||||
@@ -58,25 +58,25 @@ WpTestFuncResult test_siphash_128(void) {
|
|||||||
u64 hash[2] = {0};
|
u64 hash[2] = {0};
|
||||||
|
|
||||||
// Sring hash
|
// Sring hash
|
||||||
WpU128Hash sip_hash = wpSipHasher128(&str_bytes, (void *)&data);
|
WpU128 sip_hash = hasher.func(&str_bytes, hasher.ctx);
|
||||||
siphash(str_bytes.data, str_bytes.count * str_bytes.item_size, (void *)&data.key,
|
siphash(str_bytes.data, str_bytes.count * str_bytes.item_size, (void *)&hasher.ctx.siphash.key,
|
||||||
(u8 *)hash, sizeof(u64) * 2);
|
(u8 *)hash, sizeof(u64) * 2);
|
||||||
|
|
||||||
result = result && sip_hash.hash[0] == hash[0] && sip_hash.hash[1] == hash[1];
|
result = result && sip_hash.value[0] == hash[0] && sip_hash.value[1] == hash[1];
|
||||||
|
|
||||||
// Array hash
|
// Array hash
|
||||||
sip_hash = wpSipHasher128(&arr_bytes, (void *)&data);
|
sip_hash = hasher.func(&arr_bytes, hasher.ctx);
|
||||||
siphash(arr_bytes.data, arr_bytes.count * arr_bytes.item_size, (void *)&data.key,
|
siphash(arr_bytes.data, arr_bytes.count * arr_bytes.item_size, (void *)&hasher.ctx.siphash.key,
|
||||||
(u8 *)hash, sizeof(u64) * 2);
|
(u8 *)hash, sizeof(u64) * 2);
|
||||||
|
|
||||||
result = result && sip_hash.hash[0] == hash[0] && sip_hash.hash[1] == hash[1];
|
result = result && sip_hash.value[0] == hash[0] && sip_hash.value[1] == hash[1];
|
||||||
|
|
||||||
// Number hash
|
// Number hash
|
||||||
sip_hash = wpSipHasher128(&num_bytes, (void *)&data);
|
sip_hash = hasher.func(&num_bytes, hasher.ctx);
|
||||||
siphash(num_bytes.data, num_bytes.count * num_bytes.item_size, (void *)&data.key,
|
siphash(num_bytes.data, num_bytes.count * num_bytes.item_size, (void *)&hasher.ctx.siphash.key,
|
||||||
(u8 *)hash, sizeof(u64) * 2);
|
(u8 *)hash, sizeof(u64) * 2);
|
||||||
|
|
||||||
result = result && sip_hash.hash[0] == hash[0] && sip_hash.hash[1] == hash[1];
|
result = result && sip_hash.value[0] == hash[0] && sip_hash.value[1] == hash[1];
|
||||||
|
|
||||||
return wpTesterResult(result);
|
return wpTesterResult(result);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
// 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);
|
||||||
|
|
||||||
|
WpTestFuncResult test_hashset(void) {
|
||||||
|
b8 result = true;
|
||||||
|
WpHashSet set = wpHashSet(WpStr8, encoder, key_eq, 8);
|
||||||
|
result = result && wpHashSetCapacity(&set) == 8 && wpHashSetCount(&set) == 0;
|
||||||
|
set = wpHashSet(WpStr8, encoder, key_eq, 3);
|
||||||
|
result = result && wpHashSetCapacity(&set) == 4 && wpHashSetCount(&set) == 0;
|
||||||
|
return wpTesterResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
WpTestFuncResult test_hashset_alloc(void) {
|
||||||
|
WpAllocator arena = wpMemArenaAllocatorInit(KiB(4));
|
||||||
|
b8 result = true;
|
||||||
|
WpHashSet set = wpHashSetAlloc(WpStr8, &arena, encoder, key_eq, 8);
|
||||||
|
result = result && wpHashSetCapacity(&set) == 8 && wpHashSetCount(&set) == 0;
|
||||||
|
set = wpHashSetAlloc(WpStr8, &arena, encoder, key_eq, 3);
|
||||||
|
result = result && wpHashSetCapacity(&set) == 4 && wpHashSetCount(&set) == 0;
|
||||||
|
|
||||||
|
wpMemArenaAllocatorDestroy(&arena);
|
||||||
|
|
||||||
|
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);
|
||||||
|
WpStr8Array keys = wpArray(
|
||||||
|
WpStr8,
|
||||||
|
wpStr8Lit("key1"),
|
||||||
|
wpStr8Lit("key2"),
|
||||||
|
wpStr8Lit("key3"),
|
||||||
|
wpStr8Lit("key4")
|
||||||
|
);
|
||||||
|
|
||||||
|
for (u64 i = 0; i < wpArrayCount(keys); ++i) {
|
||||||
|
wpHashSetInsertCapped(WpStr8, &set, &keys[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
WpHashLookupResult res = wpHashSetFind(WpStr8, &set, &keys[3]);
|
||||||
|
|
||||||
|
result = result && res.found && res.ref.dist_and_tag > 0;
|
||||||
|
|
||||||
|
WpStr8 key = wpStr8Lit("key0");
|
||||||
|
res = wpHashSetFind(WpStr8, &set, &key);
|
||||||
|
result = result && !res.found;
|
||||||
|
|
||||||
|
return wpTesterResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
WpTestFuncResult test_hashset_insert_capped(void) {
|
||||||
|
b8 result = true;
|
||||||
|
WpHashSet set = wpHashSet(WpStr8, encoder, key_eq, 4);
|
||||||
|
WpStr8Array keys = wpArray(
|
||||||
|
WpStr8,
|
||||||
|
wpStr8Lit("key1"),
|
||||||
|
wpStr8Lit("key2"),
|
||||||
|
wpStr8Lit("key3"),
|
||||||
|
wpStr8Lit("key4")
|
||||||
|
);
|
||||||
|
|
||||||
|
for (u64 i = 0; i < wpArrayCount(keys); ++i) {
|
||||||
|
wpHashSetInsertCapped(WpStr8, &set, &keys[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
result = result && wpHashSetCapacity(&set) == 4 && wpHashSetCount(&set) == 3;
|
||||||
|
for (u64 i = 0; i < 3; ++i) {
|
||||||
|
result = result && wpStr8Equal(&wpHashSetItems(WpStr8, &set)[i], &keys[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return wpTesterResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
WpTestFuncResult test_hashset_insert_alloc(void) {
|
||||||
|
WpAllocator arena = wpMemArenaAllocatorInit(KiB(4));
|
||||||
|
b8 result = true;
|
||||||
|
WpHashSet set = wpHashSet(WpStr8, encoder, key_eq, 4);
|
||||||
|
WpStr8Array keys = wpArray(
|
||||||
|
WpStr8,
|
||||||
|
wpStr8Lit("key1"),
|
||||||
|
wpStr8Lit("key2"),
|
||||||
|
wpStr8Lit("key3"),
|
||||||
|
wpStr8Lit("key4")
|
||||||
|
);
|
||||||
|
|
||||||
|
WpStr8Array set_keys = set.keys;
|
||||||
|
|
||||||
|
for (u64 i = 0; i < wpArrayCount(keys); ++i) {
|
||||||
|
wpHashSetInsertAlloc(WpStr8, &arena, &set, &keys[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
result = result && set.keys != set_keys && wpHashSetCapacity(&set) == 8 && wpHashSetCount(&set) == 4;
|
||||||
|
for (u64 i = 0; i < wpArrayCount(keys); ++i) {
|
||||||
|
result = result && wpStr8Equal(&wpHashSetItems(WpStr8, &set)[i], &keys[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
wpMemArenaAllocatorDestroy(&arena);
|
||||||
|
|
||||||
|
return wpTesterResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
WpTestFuncResult test_hashset_remove(void) {
|
||||||
|
b8 result = true;
|
||||||
|
WpHashSet set = wpHashSet(WpStr8, encoder, key_eq, 4);
|
||||||
|
WpStr8Array keys = wpArray(
|
||||||
|
WpStr8,
|
||||||
|
wpStr8Lit("key1"),
|
||||||
|
wpStr8Lit("key2"),
|
||||||
|
wpStr8Lit("key3"),
|
||||||
|
wpStr8Lit("key4")
|
||||||
|
);
|
||||||
|
|
||||||
|
for (u64 i = 0; i < wpArrayCount(keys); ++i) {
|
||||||
|
wpHashSetInsertCapped(WpStr8, &set, &keys[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
wpHashSetRemove(WpStr8, &set, &keys[0]);
|
||||||
|
|
||||||
|
result = result && wpHashSetCapacity(&set) == 4 && wpHashSetCount(&set) == 2;
|
||||||
|
|
||||||
|
return wpTesterResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
WpTestFuncResult test_hashset_clear(void) {
|
||||||
|
b8 result = true;
|
||||||
|
WpHashSet set = wpHashSet(WpStr8, encoder, key_eq, 8);
|
||||||
|
WpStr8Array keys = wpArray(
|
||||||
|
WpStr8,
|
||||||
|
wpStr8Lit("key1"),
|
||||||
|
wpStr8Lit("key2"),
|
||||||
|
wpStr8Lit("key3"),
|
||||||
|
wpStr8Lit("key4")
|
||||||
|
);
|
||||||
|
|
||||||
|
for (u64 i = 0; i < wpArrayCount(keys); ++i) {
|
||||||
|
wpHashSetInsertCapped(WpStr8, &set, &keys[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
wpHashSetClear(WpStr8, &set);
|
||||||
|
|
||||||
|
result = result && wpHashSetCount(&set) == 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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
// 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);
|
||||||
|
|
||||||
|
WpTestFuncResult test_hashset(void) {
|
||||||
|
b8 result = true;
|
||||||
|
WpHashSet set = wpHashSet(WpStr8, encoder, key_eq, 8);
|
||||||
|
result = result && wpHashSetCapacity(&set) == 8 && wpHashSetCount(&set) == 0;
|
||||||
|
set = wpHashSet(WpStr8, encoder, key_eq, 3);
|
||||||
|
result = result && wpHashSetCapacity(&set) == 4 && wpHashSetCount(&set) == 0;
|
||||||
|
return wpTesterResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
WpTestFuncResult test_hashset_alloc(void) {
|
||||||
|
WpAllocator arena = wpMemArenaAllocatorInit(KiB(4));
|
||||||
|
b8 result = true;
|
||||||
|
WpHashSet set = wpHashSetAlloc(WpStr8, &arena, encoder, key_eq, 8);
|
||||||
|
result = result && wpHashSetCapacity(&set) == 8 && wpHashSetCount(&set) == 0;
|
||||||
|
set = wpHashSetAlloc(WpStr8, &arena, encoder, key_eq, 3);
|
||||||
|
result = result && wpHashSetCapacity(&set) == 4 && wpHashSetCount(&set) == 0;
|
||||||
|
|
||||||
|
wpMemArenaAllocatorDestroy(&arena);
|
||||||
|
|
||||||
|
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);
|
||||||
|
WpStr8 key1 = wpStr8Lit("key1");
|
||||||
|
WpStr8 key2 = wpStr8Lit("key2");
|
||||||
|
WpStr8 key3 = wpStr8Lit("key3");
|
||||||
|
WpStr8 key4 = wpStr8Lit("key4");
|
||||||
|
WpStr8Array keys = wpArray(WpStr8, key1, key2, key3, key4);
|
||||||
|
|
||||||
|
for (u64 i = 0; i < wpArrayCount(keys); ++i) {
|
||||||
|
wpHashSetInsertCapped(WpStr8, &set, &keys[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
WpHashLookupResult res = wpHashSetFind(WpStr8, &set, &keys[3]);
|
||||||
|
|
||||||
|
result = result && res.found && res.ref.dist_and_tag > 0;
|
||||||
|
|
||||||
|
WpStr8 key = wpStr8Lit("key0");
|
||||||
|
res = wpHashSetFind(WpStr8, &set, &key);
|
||||||
|
result = result && !res.found;
|
||||||
|
|
||||||
|
return wpTesterResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
WpTestFuncResult test_hashset_insert_capped(void) {
|
||||||
|
b8 result = true;
|
||||||
|
WpHashSet set = wpHashSet(WpStr8, 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);
|
||||||
|
|
||||||
|
for (u64 i = 0; i < wpArrayCount(keys); ++i) {
|
||||||
|
wpHashSetInsertCapped(WpStr8, &set, &keys[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
result = result && wpHashSetCapacity(&set) == 4 && wpHashSetCount(&set) == 3;
|
||||||
|
for (u64 i = 0; i < 3; ++i) {
|
||||||
|
result = result && wpStr8Equal(&wpHashSetItems(WpStr8, &set)[i], &keys[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return wpTesterResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
WpTestFuncResult test_hashset_insert_alloc(void) {
|
||||||
|
WpAllocator arena = wpMemArenaAllocatorInit(KiB(4));
|
||||||
|
b8 result = true;
|
||||||
|
WpHashSet set = wpHashSet(WpStr8, 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);
|
||||||
|
|
||||||
|
WpStr8Array set_keys = (WpStr8Array)set.keys;
|
||||||
|
|
||||||
|
for (u64 i = 0; i < wpArrayCount(keys); ++i) {
|
||||||
|
wpHashSetInsertAlloc(WpStr8, &arena, &set, &keys[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
result = result && set.keys != set_keys && wpHashSetCapacity(&set) == 8 && wpHashSetCount(&set) == 4;
|
||||||
|
for (u64 i = 0; i < wpArrayCount(keys); ++i) {
|
||||||
|
result = result && wpStr8Equal(&wpHashSetItems(WpStr8, &set)[i], &keys[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
wpMemArenaAllocatorDestroy(&arena);
|
||||||
|
|
||||||
|
return wpTesterResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
WpTestFuncResult test_hashset_remove(void) {
|
||||||
|
b8 result = true;
|
||||||
|
WpHashSet set = wpHashSet(WpStr8, 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);
|
||||||
|
|
||||||
|
for (u64 i = 0; i < wpArrayCount(keys); ++i) {
|
||||||
|
wpHashSetInsertCapped(WpStr8, &set, &keys[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
wpHashSetRemove(WpStr8, &set, &keys[0]);
|
||||||
|
|
||||||
|
result = result && wpHashSetCapacity(&set) == 4 && wpHashSetCount(&set) == 2;
|
||||||
|
|
||||||
|
return wpTesterResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
WpTestFuncResult test_hashset_clear(void) {
|
||||||
|
b8 result = true;
|
||||||
|
WpHashSet set = wpHashSet(WpStr8, 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);
|
||||||
|
|
||||||
|
for (u64 i = 0; i < wpArrayCount(keys); ++i) {
|
||||||
|
wpHashSetInsertCapped(WpStr8, &set, &keys[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
wpHashSetClear(WpStr8, &set);
|
||||||
|
|
||||||
|
result = result && wpHashSetCount(&set) == 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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// vim:fileencoding=utf-8:foldmethod=marker
|
||||||
|
|
||||||
|
#ifndef TEST_HASHSET_H
|
||||||
|
#define TEST_HASHSET_H
|
||||||
|
|
||||||
|
#include "wapp.h"
|
||||||
|
|
||||||
|
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);
|
||||||
|
WpTestFuncResult test_hashset_remove(void);
|
||||||
|
WpTestFuncResult test_hashset_clear(void);
|
||||||
|
|
||||||
|
#endif // !TEST_HASHSET_H
|
||||||
@@ -9,6 +9,8 @@
|
|||||||
#include "test_cpath.h"
|
#include "test_cpath.h"
|
||||||
#include "test_file.h"
|
#include "test_file.h"
|
||||||
#include "test_hasher.h"
|
#include "test_hasher.h"
|
||||||
|
#include "test_hashset.h"
|
||||||
|
#include "test_hashmap.h"
|
||||||
#include "test_shell_commander.h"
|
#include "test_shell_commander.h"
|
||||||
#include "wapp.h"
|
#include "wapp.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -105,6 +107,26 @@ int main(void) {
|
|||||||
test_wapp_file_remove,
|
test_wapp_file_remove,
|
||||||
test_murmur3,
|
test_murmur3,
|
||||||
test_siphash_128,
|
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_success,
|
||||||
test_commander_cmd_failure,
|
test_commander_cmd_failure,
|
||||||
test_commander_cmd_out_buf_success,
|
test_commander_cmd_out_buf_success,
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
#include "test_cpath.h"
|
#include "test_cpath.h"
|
||||||
#include "test_file.h"
|
#include "test_file.h"
|
||||||
#include "test_hasher.h"
|
#include "test_hasher.h"
|
||||||
|
#include "test_hashset.h"
|
||||||
|
#include "test_hashmap.h"
|
||||||
#include "test_shell_commander.h"
|
#include "test_shell_commander.h"
|
||||||
#include "wapp.h"
|
#include "wapp.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -105,6 +107,26 @@ int main(void) {
|
|||||||
test_wapp_file_remove,
|
test_wapp_file_remove,
|
||||||
test_murmur3,
|
test_murmur3,
|
||||||
test_siphash_128,
|
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_success,
|
||||||
test_commander_cmd_failure,
|
test_commander_cmd_failure,
|
||||||
test_commander_cmd_out_buf_success,
|
test_commander_cmd_out_buf_success,
|
||||||
|
|||||||
Reference in New Issue
Block a user