diff --git a/libhashset/include/hash_set.h b/libhashset/include/hash_set.h index 347c4dc..a391462 100644 --- a/libhashset/include/hash_set.h +++ b/libhashset/include/hash_set.h @@ -14,15 +14,19 @@ extern "C" { #endif #define HASHSET_OPT_FAILFAST UINT16_C(0x1) +#define HASHSET_ITERATOR_INIT ((size_t)0U) struct _hash_set; typedef struct _hash_set hash_set_t; hash_set_t *hash_set_create(const size_t initial_capacity, const double load_factor, const uint16_t options); -void hash_set_destroy(hash_set_t *const instance); +void hash_set_destroy(hash_set_t *instance); errno_t hash_set_insert(hash_set_t *const instance, const uint64_t value); -errno_t hash_set_contains(hash_set_t *const instance, const uint64_t value); +errno_t hash_set_remove(hash_set_t *const instance, const uint64_t value); + +errno_t hash_set_contains(const hash_set_t *const instance, const uint64_t value); +errno_t hash_set_iterate(const hash_set_t *const instance, size_t *const offset, uint64_t *const value); size_t hash_set_size(hash_set_t *const instance); diff --git a/libhashset/src/hash_set.c b/libhashset/src/hash_set.c index e72de64..046b715 100644 --- a/libhashset/src/hash_set.c +++ b/libhashset/src/hash_set.c @@ -21,7 +21,7 @@ struct _hash_set_data { uint64_t *values; - uint8_t *used; + uint8_t *used, *deleted; size_t capacity; }; @@ -29,12 +29,16 @@ struct _hash_set { double load_factor; uint16_t options; - size_t size, limit; + size_t total, valid, limit; struct _hash_set_data data; }; +#define IS_VALID(X,Y) (get_flag((X).used, (Y)) && (!get_flag((X).deleted, (Y)))) + #define BOUND(MIN,VAL,MAX) (((VAL) < (MIN)) ? (MIN) : (((VAL) > (MAX)) ? (MAX) : (VAL))) +#define SAFE_FREE(X) do { if ((X)) { free((X)); (X) = NULL; } } while(0) + /* ========================================================================= */ /* PRIVATE FUNCTIONS */ /* ========================================================================= */ @@ -54,6 +58,12 @@ static INLINE size_t round(double d) return (d >= 0.0) ? ((size_t)(d + 0.5)) : ((size_t)(d - ((double)((size_t)(d - 1))) + 0.5)) + ((size_t)(d - 1)); } +static INLINE size_t increment(const size_t value, const size_t bound) +{ + const size_t result = value + 1U; + return (result >= bound) ? 0U : result; +} + static INLINE size_t next_pow2(const size_t minimum) { size_t result = 2U; @@ -79,8 +89,15 @@ static INLINE bool alloc_data(struct _hash_set_data *const data, const size_t ca data->used = (uint8_t*) calloc((capacity / 8U) + ((capacity % 8U != 0U) ? 1U : 0U), sizeof(uint8_t)); if (!data->used) { - free(data->values); - data->values = NULL; + SAFE_FREE(data->values); + return false; + } + + data->deleted = (uint8_t*) calloc((capacity / 8U) + ((capacity % 8U != 0U) ? 1U : 0U), sizeof(uint8_t)); + if (!data->deleted) + { + SAFE_FREE(data->used); + SAFE_FREE(data->values); return false; } @@ -92,46 +109,54 @@ static INLINE void free_data(struct _hash_set_data *const data) { if (data) { + SAFE_FREE(data->values); + SAFE_FREE(data->used); + SAFE_FREE(data->deleted); data->capacity = 0U; - if (data->values) - { - free(data->values); - data->values = NULL; - } - if (data->used) - { - free(data->used); - data->used = NULL; - } } } -static INLINE bool is_used(struct _hash_set_data *const data, const size_t index) +static INLINE bool get_flag(const uint8_t *const flags, const size_t index) { - return (data->used[index / 8U] >> (index % 8U)) & 1U; + return (flags[index / 8U] >> (index % 8U)) & 1U; } -static INLINE bool find_slot(struct _hash_set_data* const data, const uint64_t value, size_t *const index_out) +static INLINE void set_flag(uint8_t *const flags, const size_t index) { - size_t index = hash(value, data->capacity); + flags[index / 8U] |= UINT8_C(1) << (index % 8U); +} - while (is_used(data, index)) +static INLINE void clear_flag(uint8_t *const flags, const size_t index) +{ + flags[index / 8U] &= ~(UINT8_C(1) << (index % 8U)); +} + +static INLINE bool find_slot(const struct _hash_set_data *const data, const uint64_t value, size_t *const index_out) +{ + size_t index; + bool index_saved = false; + + for (index = hash(value, data->capacity); get_flag(data->used, index); index = increment(index, data->capacity)) { - if (data->values[index] == value) + if (!get_flag(data->deleted, index)) { - if (index_out) + if (data->values[index] == value) { - *index_out = index; + if (index_out) + { + *index_out = index; + } + return true; } - return true; } - if (++index >= data->capacity) + else if ((!index_saved) && index_out) { - index = 0U; + *index_out = index; + index_saved = true; } } - if (index_out) + if ((!index_saved) && index_out) { *index_out = index; } @@ -139,16 +164,17 @@ static INLINE bool find_slot(struct _hash_set_data* const data, const uint64_t v return false; } -static INLINE bool insert_value(struct _hash_set_data *const data, const size_t index, const uint64_t value) +static INLINE bool store_value(struct _hash_set_data *const data, const size_t index, const uint64_t value) { - if (is_used(data, index)) + data->values[index] = value; + + if (get_flag(data->used, index)) { + clear_flag(data->deleted, index); return false; } - data->values[index] = value; - data->used[index / 8U] |= UINT8_C(1) << (index % 8U); - + set_flag(data->used, index); return true; } @@ -164,7 +190,7 @@ static INLINE errno_t grow_set(hash_set_t *const instance, const size_t new_capa for (k = 0U; k < instance->data.capacity; ++k) { - if (is_used(&instance->data, k)) + if (IS_VALID(instance->data, k)) { const uint64_t value = instance->data.values[k]; if (find_slot(&temp, value, &index)) @@ -172,7 +198,7 @@ static INLINE errno_t grow_set(hash_set_t *const instance, const size_t new_capa free_data(&temp); return EFAULT; } - if (!insert_value(&temp, index, value)) + if (!store_value(&temp, index, value)) { free_data(&temp); return EFAULT; @@ -183,6 +209,7 @@ static INLINE errno_t grow_set(hash_set_t *const instance, const size_t new_capa free_data(&instance->data); instance->data = temp; instance->limit = round(instance->data.capacity * instance->load_factor); + instance->total = instance->valid; return 0; } @@ -193,7 +220,7 @@ static INLINE errno_t grow_set(hash_set_t *const instance, const size_t new_capa hash_set_t *hash_set_create(const size_t initial_capacity, const double load_factor, const uint16_t options) { - hash_set_t *const instance = (hash_set_t*) calloc(1U, sizeof(hash_set_t)); + hash_set_t *instance = (hash_set_t*) calloc(1U, sizeof(hash_set_t)); if (!instance) { return NULL; @@ -201,7 +228,7 @@ hash_set_t *hash_set_create(const size_t initial_capacity, const double load_fac if (!alloc_data(&instance->data, (initial_capacity > 0U) ? next_pow2(initial_capacity) : 1024U)) { - free(instance); + SAFE_FREE(instance); return NULL; } @@ -212,13 +239,13 @@ hash_set_t *hash_set_create(const size_t initial_capacity, const double load_fac return instance; } -void hash_set_destroy(hash_set_t *const instance) +void hash_set_destroy(hash_set_t *instance) { if (instance) { free_data(&instance->data); memset(instance, 0, sizeof(hash_set_t)); - free(instance); + SAFE_FREE(instance); } } @@ -236,11 +263,11 @@ errno_t hash_set_insert(hash_set_t *const instance, const uint64_t value) return EEXIST; } - while ((instance->size >= instance->limit) || (instance->size >= instance->data.capacity)) + while ((instance->total >= instance->limit) || (instance->valid >= instance->data.capacity)) { if (instance->data.capacity == SIZE_MAX) { - if ((instance->options & HASHSET_OPT_FAILFAST) || (instance->size >= instance->data.capacity)) + if ((instance->options & HASHSET_OPT_FAILFAST) || (instance->valid >= instance->data.capacity)) { return ENOMEM; /*malloc has failed!*/ } @@ -253,7 +280,7 @@ errno_t hash_set_insert(hash_set_t *const instance, const uint64_t value) instance->limit = instance->data.capacity; if (error == ENOMEM) { - if ((instance->options & HASHSET_OPT_FAILFAST) || (instance->size >= instance->data.capacity)) + if ((instance->options & HASHSET_OPT_FAILFAST) || (instance->valid >= instance->data.capacity)) { return ENOMEM; /*malloc has failed!*/ } @@ -273,16 +300,16 @@ errno_t hash_set_insert(hash_set_t *const instance, const uint64_t value) } } - if (!insert_value(&instance->data, index, value)) + if (store_value(&instance->data, index, value)) { - return EFAULT; + ++instance->total; } - - ++instance->size; + + ++instance->valid; return 0; } -errno_t hash_set_contains(hash_set_t *const instance, const uint64_t value) +errno_t hash_set_contains(const hash_set_t *const instance, const uint64_t value) { if ((!instance) || (!instance->data.values)) { @@ -292,7 +319,53 @@ errno_t hash_set_contains(hash_set_t *const instance, const uint64_t value) return find_slot(&instance->data, value, NULL) ? 0 : ENOENT; } +errno_t hash_set_remove(hash_set_t* const instance, const uint64_t value) +{ + size_t index; + + if ((!instance) || (!instance->data.values)) + { + return EINVAL; + } + + if (!find_slot(&instance->data, value, &index)) + { + return ENOENT; + } + + set_flag(instance->data.deleted, index); + --instance->valid; + + return 0; +} + +errno_t hash_set_iterate(const hash_set_t *const instance, size_t *const offset, uint64_t *const value) +{ + size_t index; + + if ((!instance) || (!offset) || (!instance->data.values)) + { + return EINVAL; + } + + for (index = *offset; index < instance->data.capacity; ++index) + { + if (IS_VALID(instance->data, index)) + { + if (value) + { + *value = instance->data.values[index]; + } + *offset = index + 1U; + return 0; + } + } + + *offset = SIZE_MAX; + return ENOENT; +} + size_t hash_set_size(hash_set_t *const instance) { - return instance ? instance->size : 0U; + return instance ? instance->valid : 0U; } diff --git a/main/src/main.c b/main/src/main.c index 2de9cab..1a7ff8d 100644 --- a/main/src/main.c +++ b/main/src/main.c @@ -24,8 +24,8 @@ static uint64_t next_rand(void) int main() { - clock_t last_update = clock(); uint8_t spinner = 0U; + clock_t last_update = clock(); hash_set_t *const hash_set = hash_set_create(0U, -1.0, HASHSET_OPT_FAILFAST); if (!hash_set)