|
|
@@ -1,469 +0,0 @@
|
|
|
-#include "toolbox/hashtable.h"
|
|
|
-
|
|
|
-#include <stddef.h>
|
|
|
-#include <stdint.h>
|
|
|
-#include <stdlib.h>
|
|
|
-#include <string.h>
|
|
|
-
|
|
|
-#include "toolbox/hash.h"
|
|
|
-#include "toolbox/cstring.h"
|
|
|
-#include "toolbox/log.h"
|
|
|
-#include "toolbox/errno.h"
|
|
|
-#include "toolbox/void_pointer.h"
|
|
|
-#include "toolbox/vptr.h"
|
|
|
-
|
|
|
-#define true ((unsigned char) 1L)
|
|
|
-#define false ((unsigned char) 0L)
|
|
|
-
|
|
|
-#define TEST_FOR_NULL(POINTER) \
|
|
|
- if ( POINTER == NULL ) { \
|
|
|
- LOG_ERROR("Null Pointer Not Expected"); \
|
|
|
- toolbox_errno = RET_EVN; \
|
|
|
- return toolbox_errno; \
|
|
|
- }
|
|
|
-
|
|
|
-#define TEST_FOR_NULL_SR(POINTER, RET) \
|
|
|
- if ( POINTER == NULL ) { \
|
|
|
- LOG_ERROR("Null Pointer Not Expected"); \
|
|
|
- toolbox_errno = RET_EVN; \
|
|
|
- return RET; \
|
|
|
- }
|
|
|
-
|
|
|
-struct hashtable_item {
|
|
|
- struct {
|
|
|
- size_t hash;
|
|
|
- uint64_t true_hash;
|
|
|
- } key;
|
|
|
-
|
|
|
- struct vptr item;
|
|
|
-
|
|
|
- struct hashtable_item *p_next;
|
|
|
-};
|
|
|
-
|
|
|
-#define HASHTABLE_SIZE \
|
|
|
- (HASHTABLE_SIZE_BYTES / sizeof(struct hashtable_item))
|
|
|
-
|
|
|
-struct hashtable {
|
|
|
- struct {
|
|
|
- size_t size;
|
|
|
- size_t data[HASHTABLE_SIZE];
|
|
|
- } used_keys;
|
|
|
-
|
|
|
- struct hashtable_item data[HASHTABLE_SIZE];
|
|
|
-};
|
|
|
-
|
|
|
-struct hashtable *
|
|
|
-hashtable_create(void) {
|
|
|
- toolbox_errno = RET_OK;
|
|
|
- struct hashtable *self = calloc(1, sizeof(struct hashtable));
|
|
|
- if ( self == NULL ) {
|
|
|
- toolbox_errno = RET_EFM;
|
|
|
- }
|
|
|
- return self;
|
|
|
-}
|
|
|
-
|
|
|
-RET_TYPE
|
|
|
-hashtable_destroy(struct hashtable **self) {
|
|
|
- toolbox_errno = RET_OK;
|
|
|
- TEST_FOR_NULL(self);
|
|
|
- TEST_FOR_NULL(*self);
|
|
|
-
|
|
|
- struct hashtable *ht = (*self);
|
|
|
-
|
|
|
- for ( size_t i = 0; i < ht->used_keys.size; ++i ) {
|
|
|
- if ( ht->data[ht->used_keys.data[i]].item.data == NULL )
|
|
|
- continue;
|
|
|
-
|
|
|
- free(ht->data[ht->used_keys.data[i]].item.data);
|
|
|
- }
|
|
|
- free(*self);
|
|
|
- *self = NULL;
|
|
|
- return RET_OK;
|
|
|
-}
|
|
|
-
|
|
|
-_Bool
|
|
|
-hashtable_contain_item(const struct hashtable *self
|
|
|
- , const void* p_item, const size_t p_item_size) {
|
|
|
- toolbox_errno = RET_OK;
|
|
|
- TEST_FOR_NULL(self);
|
|
|
- TEST_FOR_NULL(p_item);
|
|
|
- const struct hashtable_item *p_tmp = NULL;
|
|
|
- for ( size_t i = 0; i < self->used_keys.size; ++i ) {
|
|
|
- p_tmp = &self->data[self->used_keys.data[i]];
|
|
|
- if ( void_pointer_equal(p_tmp->item.data, p_tmp->item.size
|
|
|
- , p_item, p_item_size) ) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- while ( p_tmp->p_next != NULL ) {
|
|
|
- p_tmp = p_tmp->p_next;
|
|
|
- if ( void_pointer_equal(p_tmp->item.data, p_tmp->item.size
|
|
|
- , p_item, p_item_size) ) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- return false;
|
|
|
-}
|
|
|
-
|
|
|
-_Bool
|
|
|
-hashtable_contain_key(const struct hashtable *self
|
|
|
- , const char *r_p_key) {
|
|
|
- toolbox_errno = RET_OK;
|
|
|
- TEST_FOR_NULL(self);
|
|
|
- TEST_FOR_NULL(r_p_key);
|
|
|
- const uint64_t true_key_hash = hash_cstr(r_p_key);
|
|
|
- const struct hashtable_item *p_tmp
|
|
|
- = &self->data[true_key_hash % HASHTABLE_SIZE];
|
|
|
- if ( p_tmp->key.true_hash == true_key_hash ) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- return false;
|
|
|
-}
|
|
|
-
|
|
|
-_Bool
|
|
|
-hashtable_contain_key_hash(const struct hashtable *self
|
|
|
- , const size_t p_key_hash) {
|
|
|
- toolbox_errno = RET_OK;
|
|
|
- TEST_FOR_NULL(self);
|
|
|
- if ( self->data[p_key_hash].key.hash == p_key_hash ) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- return false;
|
|
|
-}
|
|
|
-
|
|
|
-const void *
|
|
|
-hashtable_get(const struct hashtable *self
|
|
|
- , const char *r_p_key) {
|
|
|
- toolbox_errno = RET_OK;
|
|
|
- TEST_FOR_NULL_SR(self, NULL);
|
|
|
- TEST_FOR_NULL_SR(r_p_key, NULL);
|
|
|
- const uint64_t true_key_hash = hash_cstr(r_p_key);
|
|
|
- const struct hashtable_item *p_tmp
|
|
|
- = &self->data[true_key_hash % HASHTABLE_SIZE];
|
|
|
- if ( p_tmp->key.true_hash == true_key_hash ) {
|
|
|
- return p_tmp->item.data;
|
|
|
- }
|
|
|
- while ( p_tmp->p_next != NULL ) {
|
|
|
- p_tmp = p_tmp->p_next;
|
|
|
- if ( p_tmp->key.true_hash == true_key_hash ) {
|
|
|
- return p_tmp->item.data;
|
|
|
- }
|
|
|
- }
|
|
|
- toolbox_errno = RET_EIDNE;
|
|
|
- return NULL;
|
|
|
-}
|
|
|
-
|
|
|
-const void *
|
|
|
-hashtable_get_or_default(const struct hashtable *self
|
|
|
- , const char *r_p_key
|
|
|
- , const void *p_default_item) {
|
|
|
- toolbox_errno = RET_OK;
|
|
|
- TEST_FOR_NULL_SR(self, NULL);
|
|
|
- TEST_FOR_NULL_SR(r_p_key, NULL);
|
|
|
- TEST_FOR_NULL_SR(p_default_item, NULL);
|
|
|
-
|
|
|
- const uint64_t true_key_hash = hash_cstr(r_p_key);
|
|
|
- const struct hashtable_item *p_tmp
|
|
|
- = &self->data[true_key_hash % HASHTABLE_SIZE];
|
|
|
-
|
|
|
- if ( p_tmp->key.true_hash == true_key_hash )
|
|
|
- return p_tmp->item.data;
|
|
|
-
|
|
|
- while ( p_tmp->p_next != NULL ) {
|
|
|
- p_tmp = p_tmp->p_next;
|
|
|
-
|
|
|
- if ( p_tmp->key.true_hash == true_key_hash )
|
|
|
- return p_tmp->item.data;
|
|
|
- }
|
|
|
-
|
|
|
- return p_default_item;
|
|
|
-}
|
|
|
-
|
|
|
-_Bool
|
|
|
-hashtable_is_empty(const struct hashtable *self) {
|
|
|
- toolbox_errno = RET_OK;
|
|
|
- TEST_FOR_NULL(self);
|
|
|
- return ( self->used_keys.size == 0 );
|
|
|
-}
|
|
|
-
|
|
|
-static RET_TYPE
|
|
|
-m_hashtable_put_helper(struct hashtable *self, struct hashtable_item *p_tmp
|
|
|
- , uint64_t true_key_hash, size_t key_hash
|
|
|
- , const void *r_p_item, size_t p_item_size) {
|
|
|
- p_tmp->key.true_hash = true_key_hash;
|
|
|
- p_tmp->key.hash = key_hash;
|
|
|
- p_tmp->p_next = NULL;
|
|
|
- p_tmp->item.size = p_item_size;
|
|
|
- p_tmp->item.data = malloc(p_tmp->item.size);
|
|
|
-
|
|
|
- if ( p_tmp->item.data == NULL ) {
|
|
|
- self->used_keys.data[--self->used_keys.size] = 0;
|
|
|
- p_tmp->key.true_hash = 0;
|
|
|
- p_tmp->key.hash = 0;
|
|
|
- p_tmp->item.size = 0;
|
|
|
- toolbox_errno = RET_EFM;
|
|
|
- return RET_EFM;
|
|
|
- }
|
|
|
-
|
|
|
- memcpy(p_tmp->item.data, r_p_item, p_tmp->item.size);
|
|
|
- return RET_OK;
|
|
|
-}
|
|
|
-
|
|
|
-RET_TYPE
|
|
|
-hashtable_put(struct hashtable *self
|
|
|
- , const char *r_p_key
|
|
|
- , const void *r_p_item, const size_t p_item_size) {
|
|
|
- toolbox_errno = RET_OK;
|
|
|
- TEST_FOR_NULL(self)
|
|
|
- TEST_FOR_NULL(r_p_key);
|
|
|
- TEST_FOR_NULL(r_p_item);
|
|
|
- const uint64_t true_key_hash = hash_cstr(r_p_key);
|
|
|
- const size_t key_hash = true_key_hash % HASHTABLE_SIZE;
|
|
|
- struct hashtable_item *p_tmp = &self->data[key_hash];
|
|
|
-
|
|
|
- if ( p_tmp->key.true_hash == true_key_hash ) {
|
|
|
- toolbox_errno = RET_EIAE;
|
|
|
- return RET_EIAE;
|
|
|
- }
|
|
|
-
|
|
|
- if ( p_tmp->key.true_hash == 0 ) {
|
|
|
- self->used_keys.data[self->used_keys.size++] = key_hash;
|
|
|
- return m_hashtable_put_helper(self, p_tmp
|
|
|
- , true_key_hash, key_hash
|
|
|
- , r_p_item, p_item_size);
|
|
|
- }
|
|
|
-
|
|
|
- do {
|
|
|
- p_tmp = p_tmp->p_next;
|
|
|
- } while ( p_tmp != NULL );
|
|
|
-
|
|
|
- p_tmp = malloc(sizeof(struct hashtable_item));
|
|
|
-
|
|
|
- return m_hashtable_put_helper(self, p_tmp
|
|
|
- , true_key_hash, key_hash
|
|
|
- , r_p_item, p_item_size);
|
|
|
-
|
|
|
- return RET_OK;
|
|
|
-}
|
|
|
-
|
|
|
-__attribute__((__nonnull__))
|
|
|
-inline static void
|
|
|
-m_remove_index_carray(size_t *restrict r_p_carray, size_t *restrict r_p_size
|
|
|
- , const size_t p_index) {
|
|
|
- --(*r_p_size);
|
|
|
- for ( size_t i = p_index; i < *r_p_size; ++i ) {
|
|
|
- *(r_p_carray + i) = *(r_p_carray + i + 1);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-__attribute__((__nonnull__))
|
|
|
-inline static void
|
|
|
-m_remove_item(struct hashtable *self
|
|
|
- , const size_t p_key_hash, const uint64_t p_true_key_hash) {
|
|
|
- struct hashtable_item *p_tmp = &self->data[p_key_hash];
|
|
|
- if ( p_tmp->key.true_hash == p_true_key_hash ) {
|
|
|
- if ( p_tmp->p_next == NULL ) {
|
|
|
- for ( size_t i = 0; i < self->used_keys.size; ++i ) {
|
|
|
- if ( self->used_keys.data[i] == p_key_hash ) {
|
|
|
- m_remove_index_carray(self->used_keys.data
|
|
|
- , &self->used_keys.size, i);
|
|
|
- }
|
|
|
- }
|
|
|
- p_tmp->key.hash = 0;
|
|
|
- p_tmp->key.true_hash = 0;
|
|
|
- free(p_tmp->item.data);
|
|
|
- p_tmp->item.data = NULL;
|
|
|
- p_tmp->item.size = 0;
|
|
|
- p_tmp->p_next = NULL;
|
|
|
- }
|
|
|
- } else {
|
|
|
- while ( p_tmp->p_next != NULL ) {
|
|
|
- if ( p_tmp->key.true_hash == p_true_key_hash ) {
|
|
|
- break;
|
|
|
- }
|
|
|
- p_tmp = p_tmp->p_next;
|
|
|
- }
|
|
|
- }
|
|
|
- free(p_tmp->item.data);
|
|
|
- p_tmp = p_tmp->p_next;
|
|
|
- //memcpy(p_tmp, p_tmp->p_next, sizeof(*p_tmp->p_next));
|
|
|
-}
|
|
|
-
|
|
|
-const void *
|
|
|
-hashtable_remove(struct hashtable *self, const char *r_p_key) {
|
|
|
- toolbox_errno = RET_OK;
|
|
|
- TEST_FOR_NULL_SR(self, NULL);
|
|
|
- TEST_FOR_NULL_SR(r_p_key, NULL);
|
|
|
-
|
|
|
- const uint64_t true_key_hash = hash_cstr(r_p_key);
|
|
|
- const size_t key_hash = true_key_hash % HASHTABLE_SIZE;
|
|
|
- const struct hashtable_item *p_tmp = &self->data[key_hash];
|
|
|
-
|
|
|
- void *ret = NULL;
|
|
|
- if ( p_tmp->key.true_hash == true_key_hash ) {
|
|
|
- ret = malloc(p_tmp->item.size);
|
|
|
- memcpy(ret, p_tmp->item.data, p_tmp->item.size);
|
|
|
- m_remove_item(self, key_hash, true_key_hash);
|
|
|
- goto exit;
|
|
|
- }
|
|
|
-
|
|
|
- while ( p_tmp->p_next != NULL ) {
|
|
|
- if ( p_tmp->key.true_hash == true_key_hash ) {
|
|
|
- ret = malloc(p_tmp->item.size);
|
|
|
- memcpy(ret, p_tmp->item.data, p_tmp->item.size);
|
|
|
- m_remove_item(self, key_hash, true_key_hash);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-exit:
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-_Bool
|
|
|
-hashtable_remove_if_equal(struct hashtable *self
|
|
|
- , const char *r_p_key
|
|
|
- , const void *r_p_item, const size_t p_item_size) {
|
|
|
- toolbox_errno = RET_OK;
|
|
|
- TEST_FOR_NULL(self);
|
|
|
- TEST_FOR_NULL(r_p_key);
|
|
|
- TEST_FOR_NULL(r_p_item);
|
|
|
-
|
|
|
- const uint64_t true_key_hash = hash_cstr(r_p_key);
|
|
|
- const size_t key_hash = true_key_hash % HASHTABLE_SIZE;
|
|
|
- const struct hashtable_item *p_tmp = &self->data[key_hash];
|
|
|
-
|
|
|
- if ( p_tmp->key.true_hash == true_key_hash
|
|
|
- && void_pointer_equal(p_tmp->item.data, p_tmp->item.size
|
|
|
- , r_p_item, p_item_size) ) {
|
|
|
- m_remove_item(self, key_hash, true_key_hash);
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- while ( p_tmp->p_next != NULL ) {
|
|
|
- if ( p_tmp->key.true_hash == true_key_hash
|
|
|
- && void_pointer_equal(p_tmp->item.data, p_tmp->item.size
|
|
|
- , r_p_item, p_item_size) ) {
|
|
|
- m_remove_item(self, key_hash, true_key_hash);
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return false;
|
|
|
-}
|
|
|
-
|
|
|
-const void *
|
|
|
-hashtable_replace(struct hashtable *self
|
|
|
- , const char *r_p_key
|
|
|
- , const void *r_p_item, const size_t p_item_size) {
|
|
|
- toolbox_errno = RET_OK;
|
|
|
-
|
|
|
- TEST_FOR_NULL_SR(self, NULL);
|
|
|
- TEST_FOR_NULL_SR(r_p_key, NULL);
|
|
|
- TEST_FOR_NULL_SR(r_p_item, NULL);
|
|
|
-
|
|
|
- const uint64_t true_key_hash = hash_cstr(r_p_key);
|
|
|
- struct hashtable_item *p_tmp
|
|
|
- = &self->data[true_key_hash % HASHTABLE_SIZE];
|
|
|
- void *ret = NULL;
|
|
|
-
|
|
|
- if ( p_tmp->key.true_hash != true_key_hash ) {
|
|
|
- while ( p_tmp->p_next != NULL ) {
|
|
|
- p_tmp = p_tmp->p_next;
|
|
|
-
|
|
|
- if ( p_tmp->key.true_hash == true_key_hash )
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- ret = malloc(p_tmp->item.size);
|
|
|
- memcpy(ret, p_tmp->item.data, p_tmp->item.size);
|
|
|
-
|
|
|
- if ( p_tmp->item.size == p_item_size ) {
|
|
|
- memcpy(p_tmp->item.data, r_p_item, p_tmp->item.size);
|
|
|
- goto exit;
|
|
|
- }
|
|
|
-
|
|
|
- free(p_tmp->item.data);
|
|
|
- p_tmp->item.size = p_item_size;
|
|
|
- p_tmp->item.data = malloc(p_tmp->item.size);
|
|
|
- memcpy(p_tmp->item.data, r_p_item, p_tmp->item.size);
|
|
|
-
|
|
|
-exit:
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-_Bool
|
|
|
-hashtable_replace_if_equal(struct hashtable *self
|
|
|
- , const char *r_p_key
|
|
|
- , const void *restrict r_p_old_item
|
|
|
- , const size_t p_old_item_size
|
|
|
- , const void *restrict r_p_new_item
|
|
|
- , const size_t p_new_item_size) {
|
|
|
- toolbox_errno = RET_OK;
|
|
|
- TEST_FOR_NULL(self);
|
|
|
- TEST_FOR_NULL(r_p_key);
|
|
|
- TEST_FOR_NULL(r_p_old_item);
|
|
|
- TEST_FOR_NULL(r_p_new_item);
|
|
|
-
|
|
|
- const uint64_t true_key_hash = hash_cstr(r_p_key);
|
|
|
- struct hashtable_item *p_tmp
|
|
|
- = &self->data[true_key_hash % HASHTABLE_SIZE];
|
|
|
-
|
|
|
- if ( p_tmp->key.true_hash != true_key_hash ) {
|
|
|
- while ( p_tmp->p_next != NULL ) {
|
|
|
- p_tmp = p_tmp->p_next;
|
|
|
-
|
|
|
- if ( p_tmp->key.true_hash == true_key_hash )
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if ( ! void_pointer_equal(p_tmp->item.data, p_tmp->item.size
|
|
|
- , r_p_old_item, p_old_item_size) ) {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- if ( p_tmp->item.size == p_new_item_size ) {
|
|
|
- memcpy(p_tmp->item.data, r_p_new_item, p_tmp->item.size);
|
|
|
- goto exit_true;
|
|
|
- }
|
|
|
-
|
|
|
- free(p_tmp->item.data);
|
|
|
- p_tmp->item.size = p_new_item_size;
|
|
|
- p_tmp->item.data = malloc(p_tmp->item.size);
|
|
|
- memcpy(p_tmp->item.data, r_p_new_item, p_tmp->item.size);
|
|
|
-
|
|
|
-exit_true:
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
-size_t
|
|
|
-hashtable_used_size(const struct hashtable *self) {
|
|
|
- toolbox_errno = RET_OK;
|
|
|
- TEST_FOR_NULL(self);
|
|
|
- return self->used_keys.size;
|
|
|
-}
|
|
|
-
|
|
|
-RET_TYPE
|
|
|
-hashtable_for_each(struct hashtable *self
|
|
|
- , void (*for_each)(void *item, size_t *item_size)) {
|
|
|
- toolbox_errno = RET_OK;
|
|
|
- for ( size_t i = 0; i < self->used_keys.size; ++i ) {
|
|
|
- struct hashtable_item *tmp_it
|
|
|
- = &self->data[self->used_keys.data[i]];
|
|
|
-
|
|
|
- for_each(tmp_it->item.data, &tmp_it->item.size);
|
|
|
-
|
|
|
- while ( tmp_it->p_next != NULL ) {
|
|
|
- tmp_it = tmp_it->p_next;
|
|
|
- for_each(tmp_it->item.data, &tmp_it->item.size);
|
|
|
- }
|
|
|
- }
|
|
|
- return RET_OK;
|
|
|
-}
|
|
|
-
|
|
|
-#undef TEST_FOR_NULL_SR
|
|
|
-#undef TEST_FOR_NULL
|
|
|
-
|
|
|
-#undef false
|
|
|
-#undef true
|