Allow passing stack-allocated structs to ht_new
This commit is contained in:
57
src/ht.c
57
src/ht.c
@ -59,9 +59,9 @@ typedef struct Bucket {
|
||||
* A hash table.
|
||||
*/
|
||||
struct HTTable {
|
||||
const HTTableFunctions *fns; //!< Set of functions to use for hashing, etc.
|
||||
const HTAllocator *alloc; //!< Set of memory allocation functions.
|
||||
const HTThreshold *thresh; //!< Thresholds for resizing, etc.
|
||||
HTTableFunctions fns; //!< Set of functions to use for hashing, etc.
|
||||
HTAllocator alloc; //!< Set of memory allocation functions.
|
||||
HTThreshold thresh; //!< Thresholds for resizing, etc.
|
||||
|
||||
size_t table_size; //<! Number of buckets.
|
||||
size_t count; //<! Number of elements.
|
||||
@ -75,7 +75,7 @@ struct HTTable {
|
||||
* @return The newly allocated memory, or NULL if an error occurred
|
||||
*/
|
||||
static inline void *malloc_for_table(const HTTable *ht, size_t size) {
|
||||
return ht->alloc->malloc(size, ht->alloc->user_data);
|
||||
return ht->alloc.malloc(size, ht->alloc.user_data);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -99,7 +99,7 @@ static void *zeroed_malloc(const HTTable *ht, size_t size) {
|
||||
* @param ptr The pointer to free
|
||||
*/
|
||||
static inline void free_for_table(const HTTable *ht, void *ptr) {
|
||||
ht->alloc->free(ptr, ht->alloc->user_data);
|
||||
ht->alloc.free(ptr, ht->alloc.user_data);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -108,8 +108,8 @@ static inline void free_for_table(const HTTable *ht, void *ptr) {
|
||||
* @param key The key to free
|
||||
*/
|
||||
static inline void free_table_key(const HTTable *ht, void *key) {
|
||||
if (ht->fns->destroy_key) {
|
||||
ht->fns->destroy_key(key, ht->fns->user_data);
|
||||
if (ht->fns.destroy_key) {
|
||||
ht->fns.destroy_key(key, ht->fns.user_data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,13 +119,14 @@ static inline void free_table_key(const HTTable *ht, void *key) {
|
||||
* @param value The value to free
|
||||
*/
|
||||
static inline void free_table_value(const HTTable *ht, void *value) {
|
||||
if (ht->fns->destroy_value) {
|
||||
ht->fns->destroy_value(value, ht->fns->user_data);
|
||||
if (ht->fns.destroy_value) {
|
||||
ht->fns.destroy_value(value, ht->fns.user_data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a new hash table.
|
||||
* Create and return a new hash table. A copy of all of the passed values is
|
||||
* made.
|
||||
* @param fns The HTTableFunctions to use for the table
|
||||
* @param alloc The HTAllocator to use, or NULL to use the standard C malloc and
|
||||
* free
|
||||
@ -144,12 +145,12 @@ HTTable *ht_new(const HTTableFunctions *fns, const HTAllocator *alloc,
|
||||
if (!ht) {
|
||||
return NULL;
|
||||
}
|
||||
ht->fns = fns;
|
||||
ht->alloc = alloc;
|
||||
ht->thresh = thresh;
|
||||
ht->fns = *fns;
|
||||
ht->alloc = *alloc;
|
||||
ht->thresh = *thresh;
|
||||
|
||||
ht->count = 0;
|
||||
ht->table_size = ht->thresh->initial_size;
|
||||
ht->table_size = ht->thresh.initial_size;
|
||||
ht->buckets = zeroed_malloc(ht, sizeof(Bucket *) * ht->table_size);
|
||||
if (!ht->buckets) {
|
||||
free_for_table(ht, ht);
|
||||
@ -281,7 +282,7 @@ bool ht_equal(const HTTable *ht1, const HTTable *ht2) {
|
||||
* @return The growth threshold
|
||||
*/
|
||||
static inline size_t calculate_growth_threshold(HTTable *ht) {
|
||||
return (size_t) (ht->table_size * ht->thresh->growth_threshold);
|
||||
return (size_t) (ht->table_size * ht->thresh.growth_threshold);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -291,7 +292,7 @@ static inline size_t calculate_growth_threshold(HTTable *ht) {
|
||||
* @return The shrink threshold
|
||||
*/
|
||||
static inline size_t calculate_shrink_threshold(HTTable *ht) {
|
||||
return (size_t) (ht->table_size * ht->thresh->shrink_threshold);
|
||||
return (size_t) (ht->table_size * ht->thresh.shrink_threshold);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -301,8 +302,8 @@ static inline size_t calculate_shrink_threshold(HTTable *ht) {
|
||||
* @return Wether the operation succeeded (true means success)
|
||||
*/
|
||||
static bool resize_to(HTTable *ht, size_t new_size) {
|
||||
if (new_size < ht->thresh->initial_size) {
|
||||
new_size = ht->thresh->initial_size;
|
||||
if (new_size < ht->thresh.initial_size) {
|
||||
new_size = ht->thresh.initial_size;
|
||||
}
|
||||
if (ht->table_size == new_size) {
|
||||
return true;
|
||||
@ -331,11 +332,11 @@ static bool resize_to(HTTable *ht, size_t new_size) {
|
||||
static bool maybe_resize(HTTable *ht, size_t delta) {
|
||||
size_t new_count = ht->count + delta;
|
||||
if (new_count >= calculate_growth_threshold(ht)) {
|
||||
size_t new_size = ht->table_size * ht->thresh->growth_factor;
|
||||
size_t new_size = ht->table_size * ht->thresh.growth_factor;
|
||||
return resize_to(ht, new_size);
|
||||
} else if (new_count <= calculate_shrink_threshold(ht)
|
||||
&& ht->table_size != ht->thresh->initial_size) {
|
||||
size_t new_size = ht->table_size / ht->thresh->growth_factor;
|
||||
&& ht->table_size != ht->thresh.initial_size) {
|
||||
size_t new_size = ht->table_size / ht->thresh.growth_factor;
|
||||
return resize_to(ht, new_size);
|
||||
}
|
||||
return true;
|
||||
@ -352,7 +353,7 @@ static bool maybe_resize(HTTable *ht, size_t delta) {
|
||||
static bool bucket_matches(const HTTable *ht, Bucket *bucket, uint64_t hash,
|
||||
const void *key) {
|
||||
return bucket && bucket->hash == hash
|
||||
&& ht->fns->equal(bucket->key, key, ht->fns->user_data);
|
||||
&& ht->fns.equal(bucket->key, key, ht->fns.user_data);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -387,7 +388,7 @@ static Bucket **find_bucket(const HTTable *ht, uint64_t hash, const void *key) {
|
||||
* @return The key's hash
|
||||
*/
|
||||
static inline uint64_t hash_for_table(const HTTable *ht, const void *key) {
|
||||
return ht->fns->hash(key, ht->fns->user_data);
|
||||
return ht->fns.hash(key, ht->fns.user_data);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -436,7 +437,7 @@ bool ht_clear(HTTable *ht) {
|
||||
free_bucket(ht, bucket);
|
||||
}
|
||||
free_for_table(ht, ht->buckets);
|
||||
ht->table_size = ht->thresh->initial_size;
|
||||
ht->table_size = ht->thresh.initial_size;
|
||||
ht->buckets = zeroed_malloc(ht, sizeof(Bucket) * ht->table_size);
|
||||
ht->count = 0;
|
||||
if (!ht->buckets) {
|
||||
@ -544,7 +545,7 @@ bool ht_steal_all(HTTable *ht, void ***keys, void ***values) {
|
||||
}
|
||||
}
|
||||
Bucket **new_buckets =
|
||||
zeroed_malloc(ht, sizeof(Bucket) * ht->thresh->initial_size);
|
||||
zeroed_malloc(ht, sizeof(Bucket) * ht->thresh.initial_size);
|
||||
if (!new_buckets) {
|
||||
if (keys) {
|
||||
free_for_table(ht, *keys);
|
||||
@ -556,7 +557,7 @@ bool ht_steal_all(HTTable *ht, void ***keys, void ***values) {
|
||||
}
|
||||
size_t old_table_size = ht->table_size;
|
||||
Bucket **old_buckets = ht->buckets;
|
||||
ht->table_size = ht->thresh->initial_size;
|
||||
ht->table_size = ht->thresh.initial_size;
|
||||
ht->count = 0;
|
||||
ht->buckets = new_buckets;
|
||||
size_t out_i = 0;
|
||||
@ -731,7 +732,7 @@ bool ht_find(HTTable *ht, ht_foreach_callback_t callback, void *user_data,
|
||||
*/
|
||||
bool ht_steal_from(HTTable *ht, HTTable *src) {
|
||||
Bucket **new_buckets =
|
||||
zeroed_malloc(src, sizeof(Bucket) * src->thresh->initial_size);
|
||||
zeroed_malloc(src, sizeof(Bucket) * src->thresh.initial_size);
|
||||
if (!new_buckets) {
|
||||
return NULL;
|
||||
}
|
||||
@ -739,7 +740,7 @@ bool ht_steal_from(HTTable *ht, HTTable *src) {
|
||||
Bucket **old_buckets = src->buckets;
|
||||
src->buckets = new_buckets;
|
||||
src->count = 0;
|
||||
src->table_size = src->thresh->initial_size;
|
||||
src->table_size = src->thresh.initial_size;
|
||||
|
||||
bool no_errors = true;
|
||||
for (size_t i = 0; i < old_table_size; ++i) {
|
||||
|
Reference in New Issue
Block a user