2021-12-10 20:22:16 +00:00
|
|
|
#include <assert.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
#include "hashmap.h"
|
|
|
|
|
#include "resizable.h"
|
|
|
|
|
|
2022-01-22 14:55:09 +00:00
|
|
|
struct apfl_hashmap_bucket {
|
2021-12-10 20:22:16 +00:00
|
|
|
void *keys;
|
|
|
|
|
void *values;
|
|
|
|
|
size_t len;
|
|
|
|
|
size_t cap;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define FNV_PRIME 1099511628211U
|
|
|
|
|
|
|
|
|
|
apfl_hash
|
|
|
|
|
apfl_hash_fnv1a_add(const void *data, size_t len, apfl_hash hash)
|
|
|
|
|
{
|
|
|
|
|
for (size_t i = 0; i < len; i++) {
|
|
|
|
|
uint8_t byte = ((uint8_t *)data)[i];
|
|
|
|
|
hash ^= byte;
|
|
|
|
|
hash *= FNV_PRIME;
|
|
|
|
|
}
|
|
|
|
|
return hash;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apfl_hash
|
|
|
|
|
apfl_hash_fnv1a(const void *data, size_t len)
|
|
|
|
|
{
|
|
|
|
|
return apfl_hash_fnv1a_add(data, len, APFL_HASH_FNV1A_INIT);
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-22 14:55:09 +00:00
|
|
|
#define HAS_CALLBACK(map, cb) ((map).callbacks.cb != NULL)
|
|
|
|
|
#define INVOKE_CALLBACK(map, cb, ...) (map).callbacks.cb((map).callbacks.opaque, __VA_ARGS__)
|
2021-12-10 20:22:16 +00:00
|
|
|
|
|
|
|
|
static bool
|
2022-01-22 14:55:09 +00:00
|
|
|
keys_eq(const struct apfl_hashmap map, const void *a, const void *b)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
|
|
|
|
if (HAS_CALLBACK(map, keys_eq)) {
|
|
|
|
|
return INVOKE_CALLBACK(map, keys_eq, a, b);
|
|
|
|
|
} else {
|
2022-01-22 14:55:09 +00:00
|
|
|
return memcmp(a, b, map.keysize) == 0;
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static apfl_hash
|
2022-01-22 14:55:09 +00:00
|
|
|
calc_hash(const struct apfl_hashmap map, const void *key)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
|
|
|
|
if (HAS_CALLBACK(map, calc_hash)) {
|
|
|
|
|
return INVOKE_CALLBACK(map, calc_hash, key);
|
|
|
|
|
} else {
|
2022-01-22 14:55:09 +00:00
|
|
|
return apfl_hash_fnv1a(key, map.keysize);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2022-01-22 14:55:09 +00:00
|
|
|
destroy_key(const struct apfl_hashmap map, void *key)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
|
|
|
|
if (HAS_CALLBACK(map, destroy_key)) {
|
|
|
|
|
INVOKE_CALLBACK(map, destroy_key, key);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2022-01-22 14:55:09 +00:00
|
|
|
destroy_value(const struct apfl_hashmap map, void *value)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
|
|
|
|
if (HAS_CALLBACK(map, destroy_value)) {
|
|
|
|
|
INVOKE_CALLBACK(map, destroy_value, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-05 20:54:37 +00:00
|
|
|
static void
|
2022-01-22 14:55:09 +00:00
|
|
|
copy_key(const struct apfl_hashmap map, void *dest, void *src)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
|
|
|
|
if (HAS_CALLBACK(map, copy_key)) {
|
2022-01-05 20:54:37 +00:00
|
|
|
INVOKE_CALLBACK(map, copy_key, dest, src);
|
2021-12-10 20:22:16 +00:00
|
|
|
} else {
|
2022-01-22 14:55:09 +00:00
|
|
|
memcpy(dest, src, map.keysize);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-05 20:54:37 +00:00
|
|
|
static void
|
2022-01-22 14:55:09 +00:00
|
|
|
copy_value(const struct apfl_hashmap map, void *dest, void *src)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
|
|
|
|
if (HAS_CALLBACK(map, copy_value)) {
|
2022-01-05 20:54:37 +00:00
|
|
|
INVOKE_CALLBACK(map, copy_value, dest, src);
|
2021-12-10 20:22:16 +00:00
|
|
|
} else {
|
2022-01-22 14:55:09 +00:00
|
|
|
memcpy(dest, src, map.valsize);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define CAP_GROW 5
|
|
|
|
|
static_assert(CAP_GROW >= 1, "CAP_GROW must be at least 1");
|
|
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
|
calc_new_cap(size_t old_cap)
|
|
|
|
|
{
|
|
|
|
|
return old_cap + CAP_GROW;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define KVADDR(base, elemsize, off) (((char*)(base)) + ((elemsize)*(off)))
|
|
|
|
|
|
|
|
|
|
static bool
|
2022-01-22 14:55:09 +00:00
|
|
|
find_key_in_bucket(const struct apfl_hashmap map, struct apfl_hashmap_bucket *bucket, const void *key, size_t *off)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-01-22 14:55:09 +00:00
|
|
|
size_t keysize = map.keysize;
|
2021-12-10 20:22:16 +00:00
|
|
|
|
|
|
|
|
for (size_t i = 0; i < bucket->len; i++) {
|
|
|
|
|
if (keys_eq(map, key, KVADDR(bucket->keys, keysize, i))) {
|
|
|
|
|
*off = i;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
2022-01-22 14:55:09 +00:00
|
|
|
set_in_bucket(struct apfl_hashmap map, struct apfl_hashmap_bucket *bucket, void *key, void *value)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-01-22 14:55:09 +00:00
|
|
|
size_t keysize = map.keysize;
|
|
|
|
|
size_t valsize = map.valsize;
|
2021-12-10 20:22:16 +00:00
|
|
|
|
|
|
|
|
size_t i;
|
|
|
|
|
if (find_key_in_bucket(map, bucket, key, &i)) {
|
|
|
|
|
void *dest = KVADDR(bucket->values, valsize, i);
|
|
|
|
|
destroy_value(map, dest);
|
|
|
|
|
|
2022-01-05 20:54:37 +00:00
|
|
|
copy_value(map, dest, value);
|
|
|
|
|
return true;
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bucket->len <= bucket->cap) {
|
|
|
|
|
size_t new_cap = calc_new_cap(bucket->cap);
|
|
|
|
|
|
|
|
|
|
void *newmem;
|
|
|
|
|
|
|
|
|
|
newmem = realloc(bucket->keys, new_cap * keysize);
|
|
|
|
|
if (newmem == NULL) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
bucket->keys = newmem;
|
|
|
|
|
|
|
|
|
|
newmem = realloc(bucket->values, new_cap * valsize);
|
|
|
|
|
if (newmem == NULL) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
bucket->values = newmem;
|
|
|
|
|
|
|
|
|
|
bucket->cap = new_cap;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-05 20:54:37 +00:00
|
|
|
copy_key(map, KVADDR(bucket->keys, keysize, bucket->len), key);
|
|
|
|
|
copy_value(map, KVADDR(bucket->values, valsize, bucket->len), value);
|
2021-12-10 20:22:16 +00:00
|
|
|
|
|
|
|
|
bucket->len++;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
2022-01-22 14:55:09 +00:00
|
|
|
get_in_bucket(const struct apfl_hashmap map, struct apfl_hashmap_bucket *bucket, const void *key, void *value)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
|
|
|
|
size_t i;
|
|
|
|
|
if (!find_key_in_bucket(map, bucket, key, &i)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (value != NULL) {
|
2022-01-22 14:55:09 +00:00
|
|
|
size_t valsize = map.valsize;
|
2022-01-05 20:54:37 +00:00
|
|
|
copy_value(map, value, KVADDR(bucket->values, valsize, i));
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-22 14:55:09 +00:00
|
|
|
static struct apfl_hashmap_bucket *
|
|
|
|
|
bucket_by_key(struct apfl_hashmap map, const void *key)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
|
|
|
|
apfl_hash hash = calc_hash(map, key);
|
2022-01-22 14:55:09 +00:00
|
|
|
return &map.buckets[hash % map.nbuckets];
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2022-01-22 14:55:09 +00:00
|
|
|
delete_in_bucket(struct apfl_hashmap map, struct apfl_hashmap_bucket *bucket, const void *key)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
|
|
|
|
size_t i;
|
|
|
|
|
if (!find_key_in_bucket(map, bucket, key, &i)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-22 14:55:09 +00:00
|
|
|
size_t keysize = map.keysize;
|
|
|
|
|
size_t valsize = map.valsize;
|
2021-12-10 20:22:16 +00:00
|
|
|
|
|
|
|
|
destroy_key(map, KVADDR(bucket->keys, keysize, i));
|
|
|
|
|
destroy_value(map, KVADDR(bucket->values, valsize, i));
|
|
|
|
|
|
|
|
|
|
assert(bucket->len >= (i+1));
|
|
|
|
|
memmove(
|
|
|
|
|
KVADDR(bucket->keys, keysize, i),
|
|
|
|
|
KVADDR(bucket->keys, keysize, i+1),
|
|
|
|
|
(bucket->len - (i+1)) * keysize
|
|
|
|
|
);
|
|
|
|
|
memmove(
|
|
|
|
|
KVADDR(bucket->values, valsize, i),
|
|
|
|
|
KVADDR(bucket->values, valsize, i+1),
|
|
|
|
|
(bucket->len - (i+1)) * valsize
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
assert(bucket->len > 0); // if len == 0, we would not have found an entry
|
|
|
|
|
bucket->len--;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-22 14:55:09 +00:00
|
|
|
#define INITIAL_NBUCKETS 16
|
2021-12-10 20:22:16 +00:00
|
|
|
|
2022-01-22 14:55:09 +00:00
|
|
|
static bool
|
|
|
|
|
hashmap_init(
|
|
|
|
|
struct apfl_hashmap *map,
|
|
|
|
|
struct apfl_hashmap_callbacks callbacks,
|
|
|
|
|
size_t nbuckets,
|
|
|
|
|
size_t keysize,
|
|
|
|
|
size_t valsize
|
|
|
|
|
) {
|
2021-12-10 20:22:16 +00:00
|
|
|
map->callbacks = callbacks;
|
|
|
|
|
map->keysize = keysize;
|
|
|
|
|
map->valsize = valsize;
|
2022-01-04 20:51:44 +00:00
|
|
|
map->nbuckets = nbuckets;
|
2022-01-22 14:55:09 +00:00
|
|
|
if ((map->buckets = malloc(sizeof(struct apfl_hashmap_bucket) * nbuckets)) == NULL) {
|
|
|
|
|
return false;
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-04 20:51:44 +00:00
|
|
|
for (size_t i = 0; i < nbuckets; i++) {
|
2022-01-22 14:55:09 +00:00
|
|
|
map->buckets[i] = (struct apfl_hashmap_bucket) {
|
2021-12-10 20:22:16 +00:00
|
|
|
.keys = NULL,
|
|
|
|
|
.values = NULL,
|
|
|
|
|
.len = 0,
|
|
|
|
|
.cap = 0,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-22 14:55:09 +00:00
|
|
|
return true;
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-22 14:55:09 +00:00
|
|
|
bool
|
|
|
|
|
apfl_hashmap_init(
|
|
|
|
|
struct apfl_hashmap *map,
|
|
|
|
|
struct apfl_hashmap_callbacks callbacks,
|
|
|
|
|
size_t keysize,
|
|
|
|
|
size_t valsize
|
|
|
|
|
) {
|
|
|
|
|
return hashmap_init(map, callbacks, INITIAL_NBUCKETS, keysize, valsize);
|
2022-01-04 20:51:44 +00:00
|
|
|
}
|
|
|
|
|
|
2021-12-10 20:22:16 +00:00
|
|
|
void
|
2022-01-22 14:55:09 +00:00
|
|
|
apfl_hashmap_delete(struct apfl_hashmap *map, const void *key)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-01-22 14:55:09 +00:00
|
|
|
delete_in_bucket(*map, bucket_by_key(*map, key), key);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
2022-01-22 14:55:09 +00:00
|
|
|
apfl_hashmap_get(const struct apfl_hashmap *map, const void *key, void *value)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-01-22 14:55:09 +00:00
|
|
|
return get_in_bucket(*map, bucket_by_key(*map, key), key, value);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
2022-01-22 14:55:09 +00:00
|
|
|
apfl_hashmap_set(struct apfl_hashmap *map, void *key, void *value)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-01-22 14:55:09 +00:00
|
|
|
return set_in_bucket(*map, bucket_by_key(*map, key), key, value);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2022-01-22 14:55:09 +00:00
|
|
|
destroy_bucket(const struct apfl_hashmap map, struct apfl_hashmap_bucket *bucket)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
|
|
|
|
for (size_t i = 0; i < bucket->len; i++) {
|
2022-01-22 14:55:09 +00:00
|
|
|
destroy_key(map, KVADDR(bucket->keys, map.keysize, i));
|
|
|
|
|
destroy_value(map, KVADDR(bucket->values, map.valsize, i));
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
free(bucket->keys);
|
|
|
|
|
free(bucket->values);
|
|
|
|
|
bucket->len = 0;
|
|
|
|
|
bucket->cap = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-04 20:51:44 +00:00
|
|
|
size_t
|
2022-01-22 14:55:09 +00:00
|
|
|
apfl_hashmap_count(const struct apfl_hashmap map)
|
2022-01-04 20:51:44 +00:00
|
|
|
{
|
|
|
|
|
size_t count = 0;
|
2022-01-22 14:55:09 +00:00
|
|
|
for (size_t i = 0; i < map.nbuckets; i++) {
|
|
|
|
|
count += map.buckets[i].len;
|
2022-01-04 20:51:44 +00:00
|
|
|
}
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-10 20:22:16 +00:00
|
|
|
void
|
2022-01-22 14:55:09 +00:00
|
|
|
apfl_hashmap_deinit(struct apfl_hashmap *map)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
|
|
|
|
if (map == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (map->buckets != NULL) {
|
|
|
|
|
for (size_t i = 0; i < map->nbuckets; i++) {
|
2022-01-22 14:55:09 +00:00
|
|
|
destroy_bucket(*map, &map->buckets[i]);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
free(map->buckets);
|
|
|
|
|
}
|
2022-01-22 14:55:09 +00:00
|
|
|
}
|
2021-12-10 20:22:16 +00:00
|
|
|
|
2022-01-22 14:55:09 +00:00
|
|
|
struct apfl_hashmap
|
|
|
|
|
apfl_hashmap_move(struct apfl_hashmap *src)
|
|
|
|
|
{
|
|
|
|
|
struct apfl_hashmap out = *src;
|
|
|
|
|
src->buckets = NULL;
|
|
|
|
|
src->nbuckets = 0;
|
|
|
|
|
return out;
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-22 14:55:09 +00:00
|
|
|
bool
|
|
|
|
|
apfl_hashmap_copy(struct apfl_hashmap *dst, struct apfl_hashmap src)
|
2022-01-04 20:51:44 +00:00
|
|
|
{
|
2022-01-22 14:55:09 +00:00
|
|
|
size_t keysize = src.keysize;
|
|
|
|
|
size_t valsize = src.valsize;
|
2022-01-04 20:51:44 +00:00
|
|
|
|
2022-01-22 14:55:09 +00:00
|
|
|
if (!hashmap_init(dst, src.callbacks, src.nbuckets, keysize, valsize)) {
|
|
|
|
|
return false;
|
2022-01-04 20:51:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < dst->nbuckets; i++) {
|
2022-01-22 14:55:09 +00:00
|
|
|
struct apfl_hashmap_bucket *srcbucket = &src.buckets[i];
|
|
|
|
|
struct apfl_hashmap_bucket *dstbucket = &dst->buckets[i];
|
2022-01-04 20:51:44 +00:00
|
|
|
size_t len = srcbucket->len;
|
|
|
|
|
|
2022-01-15 22:01:23 +00:00
|
|
|
if (len == 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-04 20:51:44 +00:00
|
|
|
dstbucket->keys = malloc(keysize * len);
|
|
|
|
|
dstbucket->values = malloc(valsize * len);
|
|
|
|
|
if (dstbucket->keys == NULL || dstbucket->values == NULL) {
|
|
|
|
|
free(dstbucket->keys);
|
|
|
|
|
dstbucket->keys = NULL;
|
|
|
|
|
free(dstbucket->values);
|
|
|
|
|
dstbucket->values = NULL;
|
|
|
|
|
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dstbucket->cap = len;
|
|
|
|
|
|
2022-01-15 22:01:23 +00:00
|
|
|
for (size_t j = 0; j < len; j++) {
|
2022-01-05 20:54:37 +00:00
|
|
|
copy_key(
|
2022-01-22 14:55:09 +00:00
|
|
|
*dst,
|
2022-01-15 22:01:23 +00:00
|
|
|
KVADDR(dstbucket->keys, keysize, j),
|
|
|
|
|
KVADDR(srcbucket->keys, keysize, j)
|
2022-01-05 20:54:37 +00:00
|
|
|
);
|
2022-01-04 20:51:44 +00:00
|
|
|
|
2022-01-05 20:54:37 +00:00
|
|
|
copy_value(
|
2022-01-22 14:55:09 +00:00
|
|
|
*dst,
|
2022-01-15 22:01:23 +00:00
|
|
|
KVADDR(dstbucket->values, valsize, j),
|
|
|
|
|
KVADDR(srcbucket->values, valsize, j)
|
2022-01-05 20:54:37 +00:00
|
|
|
);
|
2022-01-15 22:01:23 +00:00
|
|
|
|
|
|
|
|
dstbucket->len++;
|
2022-01-04 20:51:44 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-15 22:01:23 +00:00
|
|
|
return dst;
|
|
|
|
|
|
2022-01-04 20:51:44 +00:00
|
|
|
fail:
|
2022-01-22 14:55:09 +00:00
|
|
|
apfl_hashmap_deinit(dst);
|
|
|
|
|
return false;
|
2022-01-04 20:51:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-12-10 20:22:16 +00:00
|
|
|
static void
|
2022-01-22 14:55:09 +00:00
|
|
|
cursor_skip_empty_buckets(struct apfl_hashmap_cursor *cur)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-01-22 14:55:09 +00:00
|
|
|
struct apfl_hashmap *map = cur->map;
|
2021-12-10 20:22:16 +00:00
|
|
|
while (cur->bucket < map->nbuckets && map->buckets[cur->bucket].len == 0) {
|
|
|
|
|
cur->bucket++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-22 14:55:09 +00:00
|
|
|
struct apfl_hashmap_cursor
|
|
|
|
|
apfl_hashmap_get_cursor(struct apfl_hashmap *map)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-01-22 14:55:09 +00:00
|
|
|
struct apfl_hashmap_cursor cursor = {
|
|
|
|
|
.map = map,
|
|
|
|
|
.i = 0,
|
|
|
|
|
.bucket = 0,
|
|
|
|
|
};
|
|
|
|
|
cursor_skip_empty_buckets(&cursor);
|
2021-12-10 20:22:16 +00:00
|
|
|
return cursor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
2022-01-22 14:55:09 +00:00
|
|
|
apfl_hashmap_cursor_is_end(struct apfl_hashmap_cursor cursor)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-01-22 14:55:09 +00:00
|
|
|
return cursor.bucket >= cursor.map->nbuckets;
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-22 14:55:09 +00:00
|
|
|
static struct apfl_hashmap_bucket *
|
|
|
|
|
cursor_get_bucket(struct apfl_hashmap_cursor cursor)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
|
|
|
|
return apfl_hashmap_cursor_is_end(cursor)
|
|
|
|
|
? NULL
|
2022-01-22 14:55:09 +00:00
|
|
|
: &cursor.map->buckets[cursor.bucket];
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2022-01-22 14:55:09 +00:00
|
|
|
apfl_hashmap_cursor_next(struct apfl_hashmap_cursor *cursor)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2022-01-22 14:55:09 +00:00
|
|
|
struct apfl_hashmap_bucket *bucket = cursor_get_bucket(*cursor);
|
2021-12-10 20:22:16 +00:00
|
|
|
|
|
|
|
|
if (bucket == NULL) {
|
|
|
|
|
return; // End already reached
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cursor->i++;
|
|
|
|
|
if (cursor->i < bucket->len) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cursor->bucket++;
|
|
|
|
|
cursor->i = 0;
|
|
|
|
|
cursor_skip_empty_buckets(cursor);
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-22 14:55:09 +00:00
|
|
|
#define CURSOR_GET(cursor, out, bucketmemb, sizememb, copy) \
|
|
|
|
|
struct apfl_hashmap_bucket *bucket = cursor_get_bucket(cursor); \
|
|
|
|
|
\
|
|
|
|
|
if (bucket == NULL) { \
|
|
|
|
|
return; /* End already reached */ \
|
|
|
|
|
} \
|
|
|
|
|
\
|
|
|
|
|
if (cursor.i >= bucket->len) { \
|
|
|
|
|
return; \
|
|
|
|
|
} \
|
|
|
|
|
\
|
|
|
|
|
size_t size = cursor.map->sizememb; \
|
|
|
|
|
\
|
|
|
|
|
copy( \
|
|
|
|
|
*cursor.map, \
|
|
|
|
|
out, \
|
|
|
|
|
KVADDR(bucket->bucketmemb, size, cursor.i) \
|
|
|
|
|
); \
|
2021-12-10 20:22:16 +00:00
|
|
|
|
2022-01-05 20:54:37 +00:00
|
|
|
void
|
2022-01-22 14:55:09 +00:00
|
|
|
apfl_hashmap_cursor_get_key(struct apfl_hashmap_cursor cursor, void *key)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
|
|
|
|
CURSOR_GET(cursor, key, keys, keysize, copy_key)
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-05 20:54:37 +00:00
|
|
|
void
|
2022-01-22 14:55:09 +00:00
|
|
|
apfl_hashmap_cursor_get_value(struct apfl_hashmap_cursor cursor, void *value)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
|
|
|
|
CURSOR_GET(cursor, value, values, valsize, copy_value)
|
|
|
|
|
}
|