Hashmap: Implement prepared setting
With a prepared set, the potential allocation, which can fail, is split from the actual setting, which can not fail.
This commit is contained in:
parent
40320aaa4a
commit
bb464d8226
2 changed files with 84 additions and 19 deletions
|
|
@ -139,24 +139,54 @@ find_key_in_bucket(const struct apfl_hashmap map, struct apfl_hashmap_bucket *bu
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
set_in_bucket(struct apfl_hashmap map, struct apfl_hashmap_bucket *bucket, void *key, void *value)
|
||||
void
|
||||
apfl_hashmap_set_prepared(struct apfl_hashmap_prepared_set prep, void *value)
|
||||
{
|
||||
size_t keysize = map.keysize;
|
||||
size_t valsize = map.valsize;
|
||||
size_t keysize = prep.map->keysize;
|
||||
size_t valsize = prep.map->valsize;
|
||||
|
||||
if (prep.i < prep.bucket->len) {
|
||||
// Replace
|
||||
void *dest = KVADDR(prep.bucket->values, valsize, prep.i);
|
||||
destroy_value(*prep.map, dest);
|
||||
|
||||
copy_value(*prep.map, dest, value);
|
||||
} else {
|
||||
// Append
|
||||
assert(prep.i == prep.bucket->len);
|
||||
assert(prep.i <= prep.bucket->keys_cap);
|
||||
assert(prep.i <= prep.bucket->values_cap);
|
||||
copy_key(*prep.map, KVADDR(prep.bucket->keys, keysize, prep.i), prep.key);
|
||||
copy_value(*prep.map, KVADDR(prep.bucket->values, valsize, prep.i), value);
|
||||
|
||||
prep.bucket->len++;
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
prepare_set_in_bucket(
|
||||
struct apfl_hashmap *map,
|
||||
struct apfl_hashmap_prepared_set *prep,
|
||||
struct apfl_hashmap_bucket *bucket,
|
||||
void *key
|
||||
) {
|
||||
size_t keysize = map->keysize;
|
||||
size_t valsize = map->valsize;
|
||||
|
||||
size_t i;
|
||||
if (find_key_in_bucket(map, bucket, key, &i)) {
|
||||
void *dest = KVADDR(bucket->values, valsize, i);
|
||||
destroy_value(map, dest);
|
||||
|
||||
copy_value(map, dest, value);
|
||||
if (find_key_in_bucket(*map, bucket, key, &i)) {
|
||||
*prep = (struct apfl_hashmap_prepared_set) {
|
||||
.map = map,
|
||||
.bucket = bucket,
|
||||
.key = key,
|
||||
.i = i,
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
if (bucket->keys_cap != bucket->values_cap) {
|
||||
assert(false); // A set operation was called on the hashmap,
|
||||
// after a previous set operation failed.
|
||||
assert(false); // A prepare set operation was called on the hashmap,
|
||||
// after a previous prepare set operation failed.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -166,7 +196,7 @@ set_in_bucket(struct apfl_hashmap map, struct apfl_hashmap_bucket *bucket, void
|
|||
void *newmem;
|
||||
|
||||
newmem = REALLOC_BYTES(
|
||||
map.allocator,
|
||||
map->allocator,
|
||||
bucket->keys,
|
||||
bucket->keys_cap * keysize,
|
||||
new_cap * keysize
|
||||
|
|
@ -178,7 +208,7 @@ set_in_bucket(struct apfl_hashmap map, struct apfl_hashmap_bucket *bucket, void
|
|||
bucket->keys_cap = new_cap;
|
||||
|
||||
newmem = REALLOC_BYTES(
|
||||
map.allocator,
|
||||
map->allocator,
|
||||
bucket->values,
|
||||
bucket->values_cap * valsize,
|
||||
new_cap * valsize
|
||||
|
|
@ -190,11 +220,12 @@ set_in_bucket(struct apfl_hashmap map, struct apfl_hashmap_bucket *bucket, void
|
|||
bucket->values_cap = new_cap;
|
||||
}
|
||||
|
||||
copy_key(map, KVADDR(bucket->keys, keysize, bucket->len), key);
|
||||
copy_value(map, KVADDR(bucket->values, valsize, bucket->len), value);
|
||||
|
||||
bucket->len++;
|
||||
|
||||
*prep = (struct apfl_hashmap_prepared_set) {
|
||||
.map = map,
|
||||
.bucket = bucket,
|
||||
.key = key,
|
||||
.i = bucket->len,
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -332,7 +363,21 @@ apfl_hashmap_get(const struct apfl_hashmap *map, const void *key, void *value)
|
|||
bool
|
||||
apfl_hashmap_set(struct apfl_hashmap *map, void *key, void *value)
|
||||
{
|
||||
return set_in_bucket(*map, bucket_by_key(*map, key), key, value);
|
||||
struct apfl_hashmap_prepared_set prep;
|
||||
if (!apfl_hashmap_prepare_set(map, &prep, key)) {
|
||||
return false;
|
||||
}
|
||||
apfl_hashmap_set_prepared(prep, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
apfl_hashmap_prepare_set(
|
||||
struct apfl_hashmap *map,
|
||||
struct apfl_hashmap_prepared_set *prep,
|
||||
void *key
|
||||
) {
|
||||
return prepare_set_in_bucket(map, prep, bucket_by_key(*map, key), key);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
|||
|
|
@ -61,6 +61,13 @@ struct apfl_hashmap {
|
|||
struct apfl_hashmap_bucket *buckets;
|
||||
};
|
||||
|
||||
struct apfl_hashmap_prepared_set {
|
||||
struct apfl_hashmap *map;
|
||||
struct apfl_hashmap_bucket *bucket;
|
||||
void *key;
|
||||
size_t i;
|
||||
};
|
||||
|
||||
apfl_hash apfl_hash_fnv1a_add(const void *, size_t len, apfl_hash);
|
||||
apfl_hash apfl_hash_fnv1a(const void *, size_t len);
|
||||
|
||||
|
|
@ -78,6 +85,12 @@ 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 *);
|
||||
|
||||
// Prepares an apfl_hashmap_set operation. If there are no other set operations
|
||||
// between the prepare and the set, this makes sure the set will always succeed.
|
||||
bool apfl_hashmap_prepare_set(struct apfl_hashmap *, struct apfl_hashmap_prepared_set *, void *key);
|
||||
|
||||
void apfl_hashmap_set_prepared(struct apfl_hashmap_prepared_set, void *value);
|
||||
|
||||
struct apfl_hashmap apfl_hashmap_move(struct apfl_hashmap *src);
|
||||
bool apfl_hashmap_copy(struct apfl_hashmap *dst, struct apfl_hashmap src);
|
||||
|
||||
|
|
@ -91,6 +104,13 @@ void apfl_hashmap_cursor_peek_value(struct apfl_hashmap_cursor, void **value_ptr
|
|||
void apfl_hashmap_cursor_get_key(struct apfl_hashmap_cursor, void *key);
|
||||
void apfl_hashmap_cursor_get_value(struct apfl_hashmap_cursor, void *value);
|
||||
|
||||
#define HASHMAP_EACH(map_ptr,cur) \
|
||||
for ( \
|
||||
struct apfl_hashmap_cursor cur = apfl_hashmap_get_cursor(map_ptr); \
|
||||
!apfl_hashmap_cursor_is_end(cur); \
|
||||
apfl_hashmap_cursor_next(&cur) \
|
||||
)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in a new issue