hashmap: Don't hide hashmap struct behind a pointer any more
Since the hashmap is meant for internal use only, there is no need to hide the internal data. This also allows us to save some malloc() calls.
This commit is contained in:
parent
afd5d8e1d2
commit
2966d6dc7c
4 changed files with 184 additions and 203 deletions
19
src/eval.c
19
src/eval.c
|
|
@ -15,7 +15,7 @@
|
|||
} while (0)
|
||||
|
||||
struct apfl_ctx_data {
|
||||
apfl_hashmap scope;
|
||||
struct apfl_hashmap scope;
|
||||
|
||||
struct apfl_value *stack;
|
||||
size_t stack_len;
|
||||
|
|
@ -150,10 +150,11 @@ static const struct apfl_hashmap_callbacks scope_hashmap_callbacks = {
|
|||
.copy_value = scope_copy_value,
|
||||
};
|
||||
|
||||
apfl_hashmap
|
||||
scope_new(void)
|
||||
bool
|
||||
scope_init(struct apfl_hashmap *map)
|
||||
{
|
||||
return apfl_hashmap_new(
|
||||
return apfl_hashmap_init(
|
||||
map,
|
||||
scope_hashmap_callbacks,
|
||||
sizeof(apfl_refcounted_string),
|
||||
sizeof(variable)
|
||||
|
|
@ -315,7 +316,7 @@ apfl_ctx_new(void)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if ((ctx->scope = scope_new()) == NULL) {
|
||||
if (!scope_init(&ctx->scope)) {
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -334,7 +335,7 @@ apfl_ctx_destroy(apfl_ctx ctx)
|
|||
return;
|
||||
}
|
||||
|
||||
apfl_hashmap_destroy(ctx->scope);
|
||||
apfl_hashmap_deinit(&ctx->scope);
|
||||
while (ctx->stack_len > 0) {
|
||||
stack_must_drop(ctx, -1);
|
||||
}
|
||||
|
|
@ -347,7 +348,7 @@ static variable
|
|||
ctx_get_var_for_assignment_inner(apfl_ctx ctx, /*borrowed*/ apfl_refcounted_string name)
|
||||
{
|
||||
variable var;
|
||||
if (apfl_hashmap_get(ctx->scope, &name, &var)) {
|
||||
if (apfl_hashmap_get(&ctx->scope, &name, &var)) {
|
||||
return var;
|
||||
}
|
||||
|
||||
|
|
@ -355,7 +356,7 @@ ctx_get_var_for_assignment_inner(apfl_ctx ctx, /*borrowed*/ apfl_refcounted_stri
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (!apfl_hashmap_set(ctx->scope, &name, &var)) {
|
||||
if (!apfl_hashmap_set(&ctx->scope, &name, &var)) {
|
||||
variable_unref(var);
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -375,7 +376,7 @@ static variable
|
|||
ctx_get_var(apfl_ctx ctx, apfl_refcounted_string name)
|
||||
{
|
||||
variable var;
|
||||
bool ok = apfl_hashmap_get(ctx->scope, &name, &var);
|
||||
bool ok = apfl_hashmap_get(&ctx->scope, &name, &var);
|
||||
apfl_refcounted_string_unref(name);
|
||||
return ok ? var : NULL;
|
||||
}
|
||||
|
|
|
|||
249
src/hashmap.c
249
src/hashmap.c
|
|
@ -5,27 +5,13 @@
|
|||
#include "hashmap.h"
|
||||
#include "resizable.h"
|
||||
|
||||
struct bucket {
|
||||
struct apfl_hashmap_bucket {
|
||||
void *keys;
|
||||
void *values;
|
||||
size_t len;
|
||||
size_t cap;
|
||||
};
|
||||
|
||||
struct apfl_hashmap_struct {
|
||||
struct apfl_hashmap_callbacks callbacks;
|
||||
size_t keysize;
|
||||
size_t valsize;
|
||||
size_t nbuckets;
|
||||
struct bucket *buckets;
|
||||
};
|
||||
|
||||
struct apfl_hashmap_cursor_struct {
|
||||
apfl_hashmap map;
|
||||
size_t bucket;
|
||||
size_t i;
|
||||
};
|
||||
|
||||
#define FNV_PRIME 1099511628211U
|
||||
|
||||
apfl_hash
|
||||
|
|
@ -45,31 +31,31 @@ apfl_hash_fnv1a(const void *data, size_t len)
|
|||
return apfl_hash_fnv1a_add(data, len, APFL_HASH_FNV1A_INIT);
|
||||
}
|
||||
|
||||
#define HAS_CALLBACK(map, cb) ((map)->callbacks.cb != NULL)
|
||||
#define INVOKE_CALLBACK(map, cb, ...) (map)->callbacks.cb((map)->callbacks.opaque, __VA_ARGS__)
|
||||
#define HAS_CALLBACK(map, cb) ((map).callbacks.cb != NULL)
|
||||
#define INVOKE_CALLBACK(map, cb, ...) (map).callbacks.cb((map).callbacks.opaque, __VA_ARGS__)
|
||||
|
||||
static bool
|
||||
keys_eq(const apfl_hashmap map, const void *a, const void *b)
|
||||
keys_eq(const struct apfl_hashmap map, const void *a, const void *b)
|
||||
{
|
||||
if (HAS_CALLBACK(map, keys_eq)) {
|
||||
return INVOKE_CALLBACK(map, keys_eq, a, b);
|
||||
} else {
|
||||
return memcmp(a, b, map->keysize) == 0;
|
||||
return memcmp(a, b, map.keysize) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
static apfl_hash
|
||||
calc_hash(apfl_hashmap map, const void *key)
|
||||
calc_hash(const struct apfl_hashmap map, const void *key)
|
||||
{
|
||||
if (HAS_CALLBACK(map, calc_hash)) {
|
||||
return INVOKE_CALLBACK(map, calc_hash, key);
|
||||
} else {
|
||||
return apfl_hash_fnv1a(key, map->keysize);
|
||||
return apfl_hash_fnv1a(key, map.keysize);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_key(apfl_hashmap map, void *key)
|
||||
destroy_key(const struct apfl_hashmap map, void *key)
|
||||
{
|
||||
if (HAS_CALLBACK(map, destroy_key)) {
|
||||
INVOKE_CALLBACK(map, destroy_key, key);
|
||||
|
|
@ -77,7 +63,7 @@ destroy_key(apfl_hashmap map, void *key)
|
|||
}
|
||||
|
||||
static void
|
||||
destroy_value(apfl_hashmap map, void *value)
|
||||
destroy_value(const struct apfl_hashmap map, void *value)
|
||||
{
|
||||
if (HAS_CALLBACK(map, destroy_value)) {
|
||||
INVOKE_CALLBACK(map, destroy_value, value);
|
||||
|
|
@ -85,22 +71,22 @@ destroy_value(apfl_hashmap map, void *value)
|
|||
}
|
||||
|
||||
static void
|
||||
copy_key(apfl_hashmap map, void *dest, void *src)
|
||||
copy_key(const struct apfl_hashmap map, void *dest, void *src)
|
||||
{
|
||||
if (HAS_CALLBACK(map, copy_key)) {
|
||||
INVOKE_CALLBACK(map, copy_key, dest, src);
|
||||
} else {
|
||||
memcpy(dest, src, map->keysize);
|
||||
memcpy(dest, src, map.keysize);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
copy_value(apfl_hashmap map, void *dest, void *src)
|
||||
copy_value(const struct apfl_hashmap map, void *dest, void *src)
|
||||
{
|
||||
if (HAS_CALLBACK(map, copy_value)) {
|
||||
INVOKE_CALLBACK(map, copy_value, dest, src);
|
||||
} else {
|
||||
memcpy(dest, src, map->valsize);
|
||||
memcpy(dest, src, map.valsize);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -116,9 +102,9 @@ calc_new_cap(size_t old_cap)
|
|||
#define KVADDR(base, elemsize, off) (((char*)(base)) + ((elemsize)*(off)))
|
||||
|
||||
static bool
|
||||
find_key_in_bucket(const apfl_hashmap map, struct bucket *bucket, const void *key, size_t *off)
|
||||
find_key_in_bucket(const struct apfl_hashmap map, struct apfl_hashmap_bucket *bucket, const void *key, size_t *off)
|
||||
{
|
||||
size_t keysize = map->keysize;
|
||||
size_t keysize = map.keysize;
|
||||
|
||||
for (size_t i = 0; i < bucket->len; i++) {
|
||||
if (keys_eq(map, key, KVADDR(bucket->keys, keysize, i))) {
|
||||
|
|
@ -131,10 +117,10 @@ find_key_in_bucket(const apfl_hashmap map, struct bucket *bucket, const void *ke
|
|||
}
|
||||
|
||||
static bool
|
||||
set_in_bucket(apfl_hashmap map, struct bucket *bucket, void *key, void *value)
|
||||
set_in_bucket(struct apfl_hashmap map, struct apfl_hashmap_bucket *bucket, void *key, void *value)
|
||||
{
|
||||
size_t keysize = map->keysize;
|
||||
size_t valsize = map->valsize;
|
||||
size_t keysize = map.keysize;
|
||||
size_t valsize = map.valsize;
|
||||
|
||||
size_t i;
|
||||
if (find_key_in_bucket(map, bucket, key, &i)) {
|
||||
|
|
@ -174,7 +160,7 @@ set_in_bucket(apfl_hashmap map, struct bucket *bucket, void *key, void *value)
|
|||
}
|
||||
|
||||
static bool
|
||||
get_in_bucket(const apfl_hashmap map, struct bucket *bucket, const void *key, void *value)
|
||||
get_in_bucket(const struct apfl_hashmap map, struct apfl_hashmap_bucket *bucket, const void *key, void *value)
|
||||
{
|
||||
size_t i;
|
||||
if (!find_key_in_bucket(map, bucket, key, &i)) {
|
||||
|
|
@ -182,30 +168,30 @@ get_in_bucket(const apfl_hashmap map, struct bucket *bucket, const void *key, vo
|
|||
}
|
||||
|
||||
if (value != NULL) {
|
||||
size_t valsize = map->valsize;
|
||||
size_t valsize = map.valsize;
|
||||
copy_value(map, value, KVADDR(bucket->values, valsize, i));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct bucket *
|
||||
bucket_by_key(apfl_hashmap map, const void *key)
|
||||
static struct apfl_hashmap_bucket *
|
||||
bucket_by_key(struct apfl_hashmap map, const void *key)
|
||||
{
|
||||
apfl_hash hash = calc_hash(map, key);
|
||||
return &map->buckets[hash % map->nbuckets];
|
||||
return &map.buckets[hash % map.nbuckets];
|
||||
}
|
||||
|
||||
static void
|
||||
delete_in_bucket(apfl_hashmap map, struct bucket *bucket, const void *key)
|
||||
delete_in_bucket(struct apfl_hashmap map, struct apfl_hashmap_bucket *bucket, const void *key)
|
||||
{
|
||||
size_t i;
|
||||
if (!find_key_in_bucket(map, bucket, key, &i)) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t keysize = map->keysize;
|
||||
size_t valsize = map->valsize;
|
||||
size_t keysize = map.keysize;
|
||||
size_t valsize = map.valsize;
|
||||
|
||||
destroy_key(map, KVADDR(bucket->keys, keysize, i));
|
||||
destroy_value(map, KVADDR(bucket->values, valsize, i));
|
||||
|
|
@ -226,27 +212,26 @@ delete_in_bucket(apfl_hashmap map, struct bucket *bucket, const void *key)
|
|||
bucket->len--;
|
||||
}
|
||||
|
||||
#define INITIAL_NBUCKETS 16 // Must be a power of 2
|
||||
|
||||
static apfl_hashmap
|
||||
hashmap_new(struct apfl_hashmap_callbacks callbacks, size_t nbuckets, size_t keysize, size_t valsize)
|
||||
{
|
||||
apfl_hashmap map = malloc(sizeof(struct apfl_hashmap_struct));
|
||||
if (map == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
#define INITIAL_NBUCKETS 16
|
||||
|
||||
static bool
|
||||
hashmap_init(
|
||||
struct apfl_hashmap *map,
|
||||
struct apfl_hashmap_callbacks callbacks,
|
||||
size_t nbuckets,
|
||||
size_t keysize,
|
||||
size_t valsize
|
||||
) {
|
||||
map->callbacks = callbacks;
|
||||
map->keysize = keysize;
|
||||
map->valsize = valsize;
|
||||
map->nbuckets = nbuckets;
|
||||
map->buckets = malloc(sizeof(struct bucket) * nbuckets);
|
||||
if (map->buckets == NULL) {
|
||||
goto fail;
|
||||
if ((map->buckets = malloc(sizeof(struct apfl_hashmap_bucket) * nbuckets)) == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < nbuckets; i++) {
|
||||
map->buckets[i] = (struct bucket) {
|
||||
map->buckets[i] = (struct apfl_hashmap_bucket) {
|
||||
.keys = NULL,
|
||||
.values = NULL,
|
||||
.len = 0,
|
||||
|
|
@ -254,43 +239,43 @@ hashmap_new(struct apfl_hashmap_callbacks callbacks, size_t nbuckets, size_t key
|
|||
};
|
||||
}
|
||||
|
||||
return map;
|
||||
|
||||
fail:
|
||||
free(map);
|
||||
return NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
apfl_hashmap
|
||||
apfl_hashmap_new(struct apfl_hashmap_callbacks callbacks, size_t keysize, size_t valsize)
|
||||
{
|
||||
return hashmap_new(callbacks, INITIAL_NBUCKETS, keysize, valsize);
|
||||
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);
|
||||
}
|
||||
|
||||
void
|
||||
apfl_hashmap_delete(apfl_hashmap map, const void *key)
|
||||
apfl_hashmap_delete(struct apfl_hashmap *map, const void *key)
|
||||
{
|
||||
delete_in_bucket(map, bucket_by_key(map, key), key);
|
||||
delete_in_bucket(*map, bucket_by_key(*map, key), key);
|
||||
}
|
||||
|
||||
bool
|
||||
apfl_hashmap_get(const apfl_hashmap map, const void *key, void *value)
|
||||
apfl_hashmap_get(const struct apfl_hashmap *map, const void *key, void *value)
|
||||
{
|
||||
return get_in_bucket(map, bucket_by_key(map, key), key, value);
|
||||
return get_in_bucket(*map, bucket_by_key(*map, key), key, value);
|
||||
}
|
||||
|
||||
bool
|
||||
apfl_hashmap_set(apfl_hashmap map, void *key, void *value)
|
||||
apfl_hashmap_set(struct apfl_hashmap *map, void *key, void *value)
|
||||
{
|
||||
return set_in_bucket(map, bucket_by_key(map, key), key, value);
|
||||
return set_in_bucket(*map, bucket_by_key(*map, key), key, value);
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_bucket(apfl_hashmap map, struct bucket *bucket)
|
||||
destroy_bucket(const struct apfl_hashmap map, struct apfl_hashmap_bucket *bucket)
|
||||
{
|
||||
for (size_t i = 0; i < bucket->len; i++) {
|
||||
destroy_key(map, KVADDR(bucket->keys, map->keysize, i));
|
||||
destroy_value(map, KVADDR(bucket->values, map->valsize, i));
|
||||
destroy_key(map, KVADDR(bucket->keys, map.keysize, i));
|
||||
destroy_value(map, KVADDR(bucket->values, map.valsize, i));
|
||||
}
|
||||
free(bucket->keys);
|
||||
free(bucket->values);
|
||||
|
|
@ -299,17 +284,17 @@ destroy_bucket(apfl_hashmap map, struct bucket *bucket)
|
|||
}
|
||||
|
||||
size_t
|
||||
apfl_hashmap_count(const apfl_hashmap map)
|
||||
apfl_hashmap_count(const struct apfl_hashmap map)
|
||||
{
|
||||
size_t count = 0;
|
||||
for (size_t i = 0; i < map->nbuckets; i++) {
|
||||
count += map->buckets[i].len;
|
||||
for (size_t i = 0; i < map.nbuckets; i++) {
|
||||
count += map.buckets[i].len;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void
|
||||
apfl_hashmap_destroy(apfl_hashmap map)
|
||||
apfl_hashmap_deinit(struct apfl_hashmap *map)
|
||||
{
|
||||
if (map == NULL) {
|
||||
return;
|
||||
|
|
@ -317,28 +302,34 @@ apfl_hashmap_destroy(apfl_hashmap map)
|
|||
|
||||
if (map->buckets != NULL) {
|
||||
for (size_t i = 0; i < map->nbuckets; i++) {
|
||||
destroy_bucket(map, &map->buckets[i]);
|
||||
destroy_bucket(*map, &map->buckets[i]);
|
||||
}
|
||||
free(map->buckets);
|
||||
}
|
||||
|
||||
free(map);
|
||||
}
|
||||
|
||||
apfl_hashmap
|
||||
apfl_hashmap_copy(apfl_hashmap src)
|
||||
struct apfl_hashmap
|
||||
apfl_hashmap_move(struct apfl_hashmap *src)
|
||||
{
|
||||
size_t keysize = src->keysize;
|
||||
size_t valsize = src->valsize;
|
||||
struct apfl_hashmap out = *src;
|
||||
src->buckets = NULL;
|
||||
src->nbuckets = 0;
|
||||
return out;
|
||||
}
|
||||
|
||||
apfl_hashmap dst = hashmap_new(src->callbacks, src->nbuckets, keysize, valsize);
|
||||
if (dst == NULL) {
|
||||
return NULL;
|
||||
bool
|
||||
apfl_hashmap_copy(struct apfl_hashmap *dst, struct apfl_hashmap src)
|
||||
{
|
||||
size_t keysize = src.keysize;
|
||||
size_t valsize = src.valsize;
|
||||
|
||||
if (!hashmap_init(dst, src.callbacks, src.nbuckets, keysize, valsize)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < dst->nbuckets; i++) {
|
||||
struct bucket *srcbucket = &src->buckets[i];
|
||||
struct bucket *dstbucket = &dst->buckets[i];
|
||||
struct apfl_hashmap_bucket *srcbucket = &src.buckets[i];
|
||||
struct apfl_hashmap_bucket *dstbucket = &dst->buckets[i];
|
||||
size_t len = srcbucket->len;
|
||||
|
||||
if (len == 0) {
|
||||
|
|
@ -360,13 +351,13 @@ apfl_hashmap_copy(apfl_hashmap src)
|
|||
|
||||
for (size_t j = 0; j < len; j++) {
|
||||
copy_key(
|
||||
dst,
|
||||
*dst,
|
||||
KVADDR(dstbucket->keys, keysize, j),
|
||||
KVADDR(srcbucket->keys, keysize, j)
|
||||
);
|
||||
|
||||
copy_value(
|
||||
dst,
|
||||
*dst,
|
||||
KVADDR(dstbucket->values, valsize, j),
|
||||
KVADDR(srcbucket->values, valsize, j)
|
||||
);
|
||||
|
|
@ -378,52 +369,50 @@ apfl_hashmap_copy(apfl_hashmap src)
|
|||
return dst;
|
||||
|
||||
fail:
|
||||
apfl_hashmap_destroy(dst);
|
||||
return NULL;
|
||||
apfl_hashmap_deinit(dst);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
cursor_skip_empty_buckets(apfl_hashmap_cursor cur)
|
||||
cursor_skip_empty_buckets(struct apfl_hashmap_cursor *cur)
|
||||
{
|
||||
apfl_hashmap map = cur->map;
|
||||
struct apfl_hashmap *map = cur->map;
|
||||
while (cur->bucket < map->nbuckets && map->buckets[cur->bucket].len == 0) {
|
||||
cur->bucket++;
|
||||
}
|
||||
}
|
||||
|
||||
apfl_hashmap_cursor
|
||||
apfl_hashmap_get_cursor(const apfl_hashmap map)
|
||||
struct apfl_hashmap_cursor
|
||||
apfl_hashmap_get_cursor(struct apfl_hashmap *map)
|
||||
{
|
||||
apfl_hashmap_cursor cursor = malloc(sizeof(struct apfl_hashmap_cursor_struct));
|
||||
if (cursor != NULL) {
|
||||
cursor->map = map;
|
||||
cursor->i = 0;
|
||||
cursor->bucket = 0;
|
||||
|
||||
cursor_skip_empty_buckets(cursor);
|
||||
}
|
||||
struct apfl_hashmap_cursor cursor = {
|
||||
.map = map,
|
||||
.i = 0,
|
||||
.bucket = 0,
|
||||
};
|
||||
cursor_skip_empty_buckets(&cursor);
|
||||
return cursor;
|
||||
}
|
||||
|
||||
bool
|
||||
apfl_hashmap_cursor_is_end(apfl_hashmap_cursor cursor)
|
||||
apfl_hashmap_cursor_is_end(struct apfl_hashmap_cursor cursor)
|
||||
{
|
||||
return cursor->bucket >= cursor->map->nbuckets;
|
||||
return cursor.bucket >= cursor.map->nbuckets;
|
||||
}
|
||||
|
||||
static struct bucket *
|
||||
cursor_get_bucket(apfl_hashmap_cursor cursor)
|
||||
static struct apfl_hashmap_bucket *
|
||||
cursor_get_bucket(struct apfl_hashmap_cursor cursor)
|
||||
{
|
||||
return apfl_hashmap_cursor_is_end(cursor)
|
||||
? NULL
|
||||
: &cursor->map->buckets[cursor->bucket];
|
||||
: &cursor.map->buckets[cursor.bucket];
|
||||
}
|
||||
|
||||
void
|
||||
apfl_hashmap_cursor_next(apfl_hashmap_cursor cursor)
|
||||
apfl_hashmap_cursor_next(struct apfl_hashmap_cursor *cursor)
|
||||
{
|
||||
struct bucket *bucket = cursor_get_bucket(cursor);
|
||||
struct apfl_hashmap_bucket *bucket = cursor_get_bucket(*cursor);
|
||||
|
||||
if (bucket == NULL) {
|
||||
return; // End already reached
|
||||
|
|
@ -439,33 +428,33 @@ apfl_hashmap_cursor_next(apfl_hashmap_cursor cursor)
|
|||
cursor_skip_empty_buckets(cursor);
|
||||
}
|
||||
|
||||
#define CURSOR_GET(cursor, out, bucketmemb, sizememb, copy) \
|
||||
struct 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) \
|
||||
); \
|
||||
#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) \
|
||||
); \
|
||||
|
||||
void
|
||||
apfl_hashmap_cursor_get_key(apfl_hashmap_cursor cursor, void *key)
|
||||
apfl_hashmap_cursor_get_key(struct apfl_hashmap_cursor cursor, void *key)
|
||||
{
|
||||
CURSOR_GET(cursor, key, keys, keysize, copy_key)
|
||||
}
|
||||
|
||||
void
|
||||
apfl_hashmap_cursor_get_value(apfl_hashmap_cursor cursor, void *value)
|
||||
apfl_hashmap_cursor_get_value(struct apfl_hashmap_cursor cursor, void *value)
|
||||
{
|
||||
CURSOR_GET(cursor, value, values, valsize, copy_value)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,12 +11,13 @@ extern "C" {
|
|||
|
||||
#define APFL_HASH_FNV1A_INIT 14695981039346656037U // offset_basis for 64bit FNV-1(a)
|
||||
|
||||
// TODO: <stdint.h> is not required to provide uint64_t
|
||||
typedef uint64_t apfl_hash;
|
||||
|
||||
typedef struct apfl_hashmap_struct *apfl_hashmap;
|
||||
|
||||
typedef struct apfl_hashmap_cursor_struct *apfl_hashmap_cursor;
|
||||
struct apfl_hashmap_cursor {
|
||||
struct apfl_hashmap *map;
|
||||
size_t bucket;
|
||||
size_t i;
|
||||
};
|
||||
|
||||
struct apfl_hashmap_callbacks {
|
||||
void *opaque;
|
||||
|
|
@ -43,27 +44,34 @@ struct apfl_hashmap_callbacks {
|
|||
void (*copy_value) (void *opaque, void *dest, void *src);
|
||||
};
|
||||
|
||||
struct apfl_hashmap {
|
||||
struct apfl_hashmap_callbacks callbacks;
|
||||
size_t keysize;
|
||||
size_t valsize;
|
||||
size_t nbuckets;
|
||||
struct apfl_hashmap_bucket *buckets;
|
||||
};
|
||||
|
||||
apfl_hash apfl_hash_fnv1a_add(const void *, size_t len, apfl_hash);
|
||||
apfl_hash apfl_hash_fnv1a(const void *, size_t len);
|
||||
|
||||
apfl_hashmap apfl_hashmap_new(struct apfl_hashmap_callbacks, size_t keysize, size_t valsize);
|
||||
void apfl_hashmap_delete(apfl_hashmap, const void *key);
|
||||
bool apfl_hashmap_get(const apfl_hashmap, const void *key, void *value);
|
||||
bool apfl_hashmap_set(apfl_hashmap, void *key, void *value);
|
||||
size_t apfl_hashmap_count(const apfl_hashmap);
|
||||
void apfl_hashmap_destroy(apfl_hashmap);
|
||||
bool apfl_hashmap_init(struct apfl_hashmap *, struct apfl_hashmap_callbacks, size_t keysize, size_t valsize);
|
||||
void apfl_hashmap_delete(struct apfl_hashmap *, const void *key);
|
||||
bool apfl_hashmap_get(const struct apfl_hashmap *, const void *key, void *value);
|
||||
bool apfl_hashmap_set(struct apfl_hashmap *, void *key, void *value);
|
||||
size_t apfl_hashmap_count(const struct apfl_hashmap);
|
||||
void apfl_hashmap_deinit(struct apfl_hashmap *);
|
||||
|
||||
apfl_hashmap apfl_hashmap_copy(apfl_hashmap src);
|
||||
struct apfl_hashmap apfl_hashmap_move(struct apfl_hashmap *src);
|
||||
bool apfl_hashmap_copy(struct apfl_hashmap *dst, struct apfl_hashmap src);
|
||||
|
||||
#define apfl_hashmap_isset(m, k) (apfl_hashmap_get((m), (k), NULL))
|
||||
|
||||
apfl_hashmap_cursor apfl_hashmap_get_cursor(apfl_hashmap);
|
||||
bool apfl_hashmap_cursor_is_end(apfl_hashmap_cursor);
|
||||
void apfl_hashmap_cursor_next(apfl_hashmap_cursor);
|
||||
void apfl_hashmap_cursor_get_key(apfl_hashmap_cursor, void *key);
|
||||
void apfl_hashmap_cursor_get_value(apfl_hashmap_cursor, void *value);
|
||||
|
||||
#define apfl_hashmap_cursor_destroy(cur) (free(cur))
|
||||
struct apfl_hashmap_cursor apfl_hashmap_get_cursor(struct apfl_hashmap *);
|
||||
bool apfl_hashmap_cursor_is_end(struct apfl_hashmap_cursor);
|
||||
void apfl_hashmap_cursor_next(struct apfl_hashmap_cursor *);
|
||||
void apfl_hashmap_cursor_get_key(struct apfl_hashmap_cursor, void *key);
|
||||
void apfl_hashmap_cursor_get_value(struct apfl_hashmap_cursor, void *value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
75
src/value.c
75
src/value.c
|
|
@ -14,7 +14,7 @@ struct apfl_list_data {
|
|||
|
||||
struct apfl_dict_data {
|
||||
unsigned refcount;
|
||||
apfl_hashmap map;
|
||||
struct apfl_hashmap map;
|
||||
};
|
||||
|
||||
static apfl_hash value_hash(const struct apfl_value);
|
||||
|
|
@ -92,10 +92,11 @@ static const struct apfl_hashmap_callbacks dict_hashmap_callbacks = {
|
|||
.copy_value = dict_copy_key_or_value,
|
||||
};
|
||||
|
||||
static apfl_hashmap
|
||||
new_hashmap_for_dict(void)
|
||||
static bool
|
||||
init_hashmap_for_dict(struct apfl_hashmap *map)
|
||||
{
|
||||
return apfl_hashmap_new(
|
||||
return apfl_hashmap_init(
|
||||
map,
|
||||
dict_hashmap_callbacks,
|
||||
sizeof(struct apfl_value),
|
||||
sizeof(struct apfl_value)
|
||||
|
|
@ -119,7 +120,7 @@ apfl_dict_unref(apfl_dict dict)
|
|||
return;
|
||||
}
|
||||
|
||||
apfl_hashmap_destroy(dict->map);
|
||||
apfl_hashmap_deinit(&dict->map);
|
||||
free(dict);
|
||||
}
|
||||
|
||||
|
|
@ -161,13 +162,10 @@ print(unsigned indent, FILE *out, struct apfl_value value, bool skip_first_inden
|
|||
return;
|
||||
}
|
||||
|
||||
apfl_hashmap_cursor cur = apfl_hashmap_get_cursor(value.dict->map);
|
||||
if (cur == NULL) {
|
||||
return; // TODO: Handle failure in a better way
|
||||
}
|
||||
struct apfl_hashmap_cursor cur = apfl_hashmap_get_cursor(&value.dict->map);
|
||||
|
||||
apfl_print_indented(first_indent, out, "[\n");
|
||||
for (; !apfl_hashmap_cursor_is_end(cur); apfl_hashmap_cursor_next(cur)) {
|
||||
for (; !apfl_hashmap_cursor_is_end(cur); apfl_hashmap_cursor_next(&cur)) {
|
||||
struct apfl_value k, v;
|
||||
apfl_hashmap_cursor_get_key(cur, &k);
|
||||
apfl_hashmap_cursor_get_value(cur, &v);
|
||||
|
|
@ -179,7 +177,6 @@ print(unsigned indent, FILE *out, struct apfl_value value, bool skip_first_inden
|
|||
apfl_value_deinit(&v);
|
||||
fprintf(out, "\n");
|
||||
}
|
||||
apfl_hashmap_cursor_destroy(cur);
|
||||
apfl_print_indented(indent, out, "]");
|
||||
return;
|
||||
}
|
||||
|
|
@ -247,10 +244,15 @@ list_eq(apfl_list a, apfl_list b)
|
|||
}
|
||||
|
||||
static bool
|
||||
dict_eq_inner(apfl_hashmap_cursor cur, const apfl_dict b)
|
||||
dict_eq(const apfl_dict a, const apfl_dict b)
|
||||
{
|
||||
if (a == b) {
|
||||
return true;
|
||||
}
|
||||
|
||||
struct apfl_hashmap_cursor cur = apfl_hashmap_get_cursor(&a->map);
|
||||
size_t total = 0;
|
||||
for (; !apfl_hashmap_cursor_is_end(cur); apfl_hashmap_cursor_next(cur)) {
|
||||
for (; !apfl_hashmap_cursor_is_end(cur); apfl_hashmap_cursor_next(&cur)) {
|
||||
struct apfl_value key;
|
||||
struct apfl_value val;
|
||||
|
||||
|
|
@ -258,7 +260,7 @@ dict_eq_inner(apfl_hashmap_cursor cur, const apfl_dict b)
|
|||
apfl_hashmap_cursor_get_value(cur, &val);
|
||||
|
||||
struct apfl_value other_val;
|
||||
if (!apfl_hashmap_get(b->map, &key, &other_val)) {
|
||||
if (!apfl_hashmap_get(&b->map, &key, &other_val)) {
|
||||
apfl_value_deinit(&key);
|
||||
apfl_value_deinit(&val);
|
||||
return false;
|
||||
|
|
@ -278,24 +280,6 @@ dict_eq_inner(apfl_hashmap_cursor cur, const apfl_dict b)
|
|||
return total == apfl_hashmap_count(b->map);
|
||||
}
|
||||
|
||||
static bool
|
||||
dict_eq(const apfl_dict a, const apfl_dict b)
|
||||
{
|
||||
if (a == b) {
|
||||
return true;
|
||||
}
|
||||
|
||||
apfl_hashmap_cursor cur = apfl_hashmap_get_cursor(a->map);
|
||||
if (cur == NULL) {
|
||||
assert(false); // TODO: It's rather ugly that this can fail and that we return false then!
|
||||
return false;
|
||||
}
|
||||
|
||||
bool eq = dict_eq_inner(cur, b);
|
||||
apfl_hashmap_cursor_destroy(cur);
|
||||
return eq;
|
||||
}
|
||||
|
||||
bool
|
||||
apfl_value_eq(const struct apfl_value a, const struct apfl_value b)
|
||||
{
|
||||
|
|
@ -456,9 +440,8 @@ apfl_editable_list_finalize(apfl_editable_list elist)
|
|||
return list;
|
||||
}
|
||||
|
||||
|
||||
struct apfl_editable_dict_data {
|
||||
apfl_hashmap map;
|
||||
struct apfl_hashmap map;
|
||||
};
|
||||
|
||||
apfl_editable_dict
|
||||
|
|
@ -469,7 +452,7 @@ apfl_editable_dict_new(void)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if ((ed->map = new_hashmap_for_dict()) == NULL) {
|
||||
if (!init_hashmap_for_dict(&ed->map)) {
|
||||
free(ed);
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -492,10 +475,10 @@ apfl_editable_dict_new_from_dict(apfl_dict dict)
|
|||
|
||||
if (dict->refcount == 1) {
|
||||
// We're the only one having a reference to the dict. We can directly use it's hashmap!
|
||||
MOVEPTR(ed->map, dict->map);
|
||||
ed->map = apfl_hashmap_move(&dict->map);
|
||||
} else {
|
||||
// There are other places referencing the dict, we need to create a shallow copy first.
|
||||
if ((ed->map = apfl_hashmap_copy(dict->map)) == NULL) {
|
||||
if (!apfl_hashmap_copy(&ed->map, dict->map)) {
|
||||
free(ed);
|
||||
apfl_dict_unref(dict);
|
||||
return NULL;
|
||||
|
|
@ -513,7 +496,7 @@ apfl_editable_dict_get(
|
|||
struct apfl_value key,
|
||||
struct apfl_value *out
|
||||
) {
|
||||
bool ok = apfl_hashmap_get(ed->map, &key, out);
|
||||
bool ok = apfl_hashmap_get(&ed->map, &key, out);
|
||||
apfl_value_deinit(&key);
|
||||
return ok;
|
||||
}
|
||||
|
|
@ -521,11 +504,11 @@ apfl_editable_dict_get(
|
|||
bool
|
||||
apfl_editable_dict_set(apfl_editable_dict ed, struct apfl_value key, struct apfl_value value)
|
||||
{
|
||||
if (ed == NULL || ed->map == NULL) {
|
||||
if (ed == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ok = apfl_hashmap_set(ed->map, &key, &value);
|
||||
bool ok = apfl_hashmap_set(&ed->map, &key, &value);
|
||||
apfl_value_deinit(&key);
|
||||
apfl_value_deinit(&value);
|
||||
return ok;
|
||||
|
|
@ -534,18 +517,18 @@ apfl_editable_dict_set(apfl_editable_dict ed, struct apfl_value key, struct apfl
|
|||
void
|
||||
apfl_editable_dict_delete(apfl_editable_dict ed, struct apfl_value key)
|
||||
{
|
||||
if (ed == NULL || ed->map == NULL) {
|
||||
if (ed == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
apfl_hashmap_delete(ed->map, &key);
|
||||
apfl_hashmap_delete(&ed->map, &key);
|
||||
apfl_value_deinit(&key);
|
||||
}
|
||||
|
||||
apfl_dict
|
||||
apfl_editable_dict_finalize(apfl_editable_dict ed)
|
||||
{
|
||||
if (ed == NULL || ed->map == NULL) {
|
||||
if (ed == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -556,7 +539,7 @@ apfl_editable_dict_finalize(apfl_editable_dict ed)
|
|||
}
|
||||
|
||||
dict->refcount = 1;
|
||||
MOVEPTR(dict->map, ed->map);
|
||||
dict->map = apfl_hashmap_move(&ed->map);
|
||||
|
||||
free(ed);
|
||||
|
||||
|
|
@ -570,7 +553,7 @@ apfl_editable_dict_destroy(apfl_editable_dict ed)
|
|||
return;
|
||||
}
|
||||
|
||||
apfl_hashmap_destroy(ed->map);
|
||||
apfl_hashmap_deinit(&ed->map);
|
||||
free(ed);
|
||||
}
|
||||
|
||||
|
|
@ -618,7 +601,7 @@ apfl_list_get_item(apfl_list list, size_t index, struct apfl_value *out)
|
|||
bool
|
||||
apfl_dict_get_item(apfl_dict dict, struct apfl_value key, struct apfl_value *out)
|
||||
{
|
||||
bool ok = apfl_hashmap_get(dict->map, &key, out);
|
||||
bool ok = apfl_hashmap_get(&dict->map, &key, out);
|
||||
apfl_dict_unref(dict);
|
||||
apfl_value_deinit(&key);
|
||||
return ok;
|
||||
|
|
|
|||
Loading…
Reference in a new issue