From: Stephan Soller Date: Fri, 29 Apr 2016 18:41:30 +0000 (+0200) Subject: Fixed value reassignment with sh_put_ptr() and reformatted the macro code. X-Git-Url: https://git.owens.tech/git.owens.tech/git.owens.tech/git?a=commitdiff_plain;h=21b4805c5314a2ae49d38af5782dea5819a2735a;p=forks%2Fsingle-header-file-c-libs.git Fixed value reassignment with sh_put_ptr() and reformatted the macro code. --- diff --git a/slim_hash.h b/slim_hash.h index df49d12..452c854 100644 --- a/slim_hash.h +++ b/slim_hash.h @@ -11,9 +11,9 @@ bool sh_del(hash, key); // ture if value was found and deleted, false if not found for(sh_it_p it = sh_start(hash); it != NULL; it = sh_next(it)) { - int64_t key = sh_key(it); - int value = sh_value(it); - sh_remove(it); + int64_t key = sh_key(it); + int value = sh_value(it); + sh_remove(it); } int* sh_put_ptr(sh_hash_p hash, int64_t key); // reserve new slot, returns ptr to value @@ -28,18 +28,19 @@ void sh_remove(sh_hash_p hash, sh_it_p it); // removes current elem from has /* -prefix sh_ dict_ -key_type int64_t const char* -value_type int void* -hash_expr sh_murmur3_32(&k, sizeof(k)) sh_murmur3_32(k, strlen(k)) -key_cmp_expr (a == b) strcmp(a, b) == 0 -key_add_expr k strdup(k) -key_del_expr 0 (free(k), NULL) +prefix sh_ dict_ +key_type int64_t const char* +value_type int void* +hash_expr sh_murmur3_32(&k, sizeof(k)) sh_murmur3_32(k, strlen(k)) +key_cmp_expr (a == b) strcmp(a, b) == 0 +key_put_expr k strdup(k) +key_del_expr 0 (free(k), NULL) + +SH_GEN_DECL(dict, const char*, void*) +SH_GEN_DEF(dict, const char*, void*, sh_murmur3_32(k, strlen(k)), strcmp(a, b) == 0, strdup(k), (free(k), NULL)) +SH_GEN_DICT_DEF(dict, void*) +SH_GEN_HASH_DEF(con_list, int32_t, void*) -SH_GEN_DECL("dict_", const char*, void*) -SH_GEN_DEF("dict_", const char*, void*, sh_murmur3_32(k, strlen(k)), strcmp(a, b) == 0, strdup(k), (free(k), NULL)) -SH_GEN_DICT_DEF("dict_", void*) -SH_GEN_HASH_DEF("con_list_", int32_t, void*) */ @@ -49,40 +50,39 @@ SH_GEN_HASH_DEF("con_list_", int32_t, void*) // Data // -typedef struct { - uint32_t hash_and_flags; - int64_t key; - int value; -} sh_slot_t, *sh_slot_p, *sh_it_p; - -typedef struct { - uint32_t length, capacity, deleted; - sh_slot_p slots; -} sh_hash_t, *sh_hash_p; - #define SH_SLOT_FREE 0x00000000 #define SH_SLOT_DELETED 0x00000001 +#define SH_SLOT_FILLED 0x80000000 + +#define SH_GEN_DECL(prefix, key_t, value_t) \ + typedef struct { \ + uint32_t hash_and_flags; \ + key_t key; \ + value_t value; \ + } prefix##_slot_t, *prefix##_slot_p, *prefix##_it_p; \ + \ + typedef struct { \ + uint32_t length, capacity, deleted; \ + prefix##_slot_p slots; \ + } prefix##_t, *prefix##_p; \ + \ + void prefix##_new(prefix##_p hash); \ + void prefix##_destroy(prefix##_p hash); \ + void prefix##_optimize(prefix##_p hash); \ + \ + void prefix##_put(prefix##_p hash, key_t key, value_t value); \ + value_t prefix##_get(prefix##_p hash, key_t key, value_t default_value); \ + bool prefix##_del(prefix##_p hash, key_t key); \ + bool prefix##_contains(prefix##_p hash, key_t key); \ + \ + value_t* prefix##_put_ptr(prefix##_p hash, key_t key); \ + value_t* prefix##_get_ptr(prefix##_p hash, key_t key); \ + \ + prefix##_it_p prefix##_start(prefix##_p hash); \ + prefix##_it_p prefix##_next(prefix##_p hash, prefix##_it_p it); \ + void prefix##_remove(prefix##_p hash, prefix##_it_p it); \ -// -// API -// - -void sh_new(sh_hash_p hash); -void sh_destroy(sh_hash_p hash); -void sh_optimize(sh_hash_p hash); - -void sh_put(sh_hash_p hash, int64_t key, int value); -int sh_get(sh_hash_p hash, int64_t key, int default_value); -bool sh_del(sh_hash_p hash, int64_t key); -bool sh_contains(sh_hash_p hash, int64_t key); - -int* sh_put_ptr(sh_hash_p hash, int64_t key); -int* sh_get_ptr(sh_hash_p hash, int64_t key); - -sh_it_p sh_start(sh_hash_p hash); -sh_it_p sh_next(sh_hash_p hash, sh_it_p it); -void sh_remove(sh_hash_p hash, sh_it_p it); #endif // SLIM_HASH_HEADER @@ -90,212 +90,233 @@ void sh_remove(sh_hash_p hash, sh_it_p it); #ifdef SLIM_HASH_IMPLEMENTATION +#define SH_GEN_DEF(prefix, key_t, value_t, hash_expr, key_cmp_expr, key_put_expr, key_del_expr) \ + /** \ + * Get 32-bit Murmur3 hash of a memory block. Taken from \ + * https://github.com/wolkykim/qlibc/blob/master/src/utilities/qhash.c \ + * \ + * MurmurHash3 was created by Austin Appleby in 2008. The initial \ + * implementation was published in C++ and placed in the public: \ + * https://sites.google.com/site/murmurhash/ \ + * \ + * Seungyoung Kim has ported its implementation into C language in 2012 and \ + * published it as a part of qLibc component. \ + **/ \ + uint32_t prefix##_murmur3_32(const void *data, size_t size) { \ + if (data == NULL || size == 0) \ + return 0; \ + \ + const uint32_t c1 = 0xcc9e2d51; \ + const uint32_t c2 = 0x1b873593; \ + \ + const int nblocks = size / 4; \ + const uint32_t *blocks = (const uint32_t *) (data); \ + const uint8_t *tail = (const uint8_t *) (data + (nblocks * 4)); \ + \ + uint32_t h = 0; \ + \ + int i; \ + uint32_t k; \ + for (i = 0; i < nblocks; i++) { \ + k = blocks[i]; \ + \ + k *= c1; \ + k = (k << 15) | (k >> (32 - 15)); \ + k *= c2; \ + \ + h ^= k; \ + h = (h << 13) | (h >> (32 - 13)); \ + h = (h * 5) + 0xe6546b64; \ + } \ + \ + k = 0; \ + switch (size & 3) { \ + case 3: \ + k ^= tail[2] << 16; \ + case 2: \ + k ^= tail[1] << 8; \ + case 1: \ + k ^= tail[0]; \ + k *= c1; \ + k = (k << 15) | (k >> (32 - 15)); \ + k *= c2; \ + h ^= k; \ + }; \ + \ + h ^= size; \ + \ + h ^= h >> 16; \ + h *= 0x85ebca6b; \ + h ^= h >> 13; \ + h *= 0xc2b2ae35; \ + h ^= h >> 16; \ + \ + return h; \ + } \ + \ + bool prefix##_resize(prefix##_p hashmap, uint32_t new_capacity) { \ + /* on filling empty slot: if exceeding load factor, double capacity \ + // on deleting slot: if to empty, half capacity \ + \ + // Can't make hashmap smaller than it needs to be */ \ + if (new_capacity < hashmap->length) \ + return false; \ + \ + prefix##_t new_hashmap; \ + new_hashmap.length = 0; \ + new_hashmap.capacity = new_capacity; \ + new_hashmap.deleted = 0; \ + new_hashmap.slots = calloc(new_hashmap.capacity, sizeof(new_hashmap.slots[0])); \ + \ + /* Failed to allocate memory for new hash map, leave the original untouched */ \ + if (new_hashmap.slots == NULL) \ + return false; \ + \ + for(prefix##_it_p it = prefix##_start(hashmap); it != NULL; it = prefix##_next(hashmap, it)) { \ + prefix##_put(&new_hashmap, it->key, it->value); \ + } \ + \ + free(hashmap->slots); \ + *hashmap = new_hashmap; \ + return true; \ + } \ + \ + void prefix##_new(prefix##_p hashmap) { \ + hashmap->length = 0; \ + hashmap->capacity = 0; \ + hashmap->deleted = 0; \ + hashmap->slots = NULL; \ + prefix##_resize(hashmap, 8); \ + } \ + \ + void prefix##_destroy(prefix##_p hashmap) { \ + hashmap->length = 0; \ + hashmap->capacity = 0; \ + hashmap->deleted = 0; \ + free(hashmap->slots); \ + hashmap->slots = NULL; \ + } \ + \ + value_t* prefix##_put_ptr(prefix##_p hashmap, key_t key) { \ + /* add the +1 to the capacity doubling to avoid beeing stuck on a capacity of 0 */ \ + if (hashmap->length + hashmap->deleted + 1 > hashmap->capacity * 0.5) \ + prefix##_resize(hashmap, (hashmap->capacity + 1) * 2); \ + \ + uint32_t hash = (hash_expr) | SH_SLOT_FILLED; \ + size_t index = hash % hashmap->capacity; \ + while ( !(hashmap->slots[index].hash_and_flags == hash || hashmap->slots[index].hash_and_flags == SH_SLOT_FREE || hashmap->slots[index].hash_and_flags == SH_SLOT_DELETED) ) { \ + index = (index + 1) % hashmap->capacity; \ + } \ + \ + if (hashmap->slots[index].hash_and_flags == SH_SLOT_DELETED) \ + hashmap->deleted--; \ + hashmap->length++; \ + hashmap->slots[index].hash_and_flags = hash; \ + hashmap->slots[index].key = (key_put_expr); \ + return &hashmap->slots[index].value; \ + } \ + \ + value_t* prefix##_get_ptr(prefix##_p hashmap, key_t key) { \ + uint32_t hash = (hash_expr) | SH_SLOT_FILLED; \ + size_t index = hash % hashmap->capacity; \ + while ( !(hashmap->slots[index].hash_and_flags == SH_SLOT_FREE) ) { \ + if (hashmap->slots[index].hash_and_flags == hash) { \ + key_t a = hashmap->slots[index].key; \ + key_t b = key; \ + if (key_cmp_expr) \ + return &hashmap->slots[index].value; \ + } \ + \ + index = (index + 1) % hashmap->capacity; \ + } \ + \ + return NULL; \ + } \ + \ + bool prefix##_del(prefix##_p hashmap, key_t key) { \ + uint32_t hash = (hash_expr) | SH_SLOT_FILLED; \ + size_t index = hash % hashmap->capacity; \ + while ( !(hashmap->slots[index].hash_and_flags == SH_SLOT_FREE) ) { \ + if (hashmap->slots[index].hash_and_flags == hash) { \ + key_t a = hashmap->slots[index].key; \ + key_t b = key; \ + if (key_cmp_expr) { \ + key_t key = hashmap->slots[index].key; \ + key = key; /* avoid unused variable warning */ \ + hashmap->slots[index].key = (key_del_expr); \ + hashmap->slots[index].hash_and_flags = SH_SLOT_DELETED; \ + hashmap->length--; \ + hashmap->deleted++; \ + \ + if (hashmap->length < hashmap->capacity * 0.2) \ + prefix##_resize(hashmap, hashmap->capacity / 2); \ + \ + return true; \ + } \ + } \ + \ + index = (index + 1) % hashmap->capacity; \ + } \ + \ + return false; \ + } \ + \ + void prefix##_put(prefix##_p hashmap, key_t key, value_t value) { \ + *prefix##_put_ptr(hashmap, key) = value; \ + } \ + \ + value_t prefix##_get(prefix##_p hashmap, key_t key, value_t default_value) { \ + value_t* value_ptr = prefix##_get_ptr(hashmap, key); \ + return (value_ptr) ? *value_ptr : default_value; \ + } \ + \ + bool prefix##_contains(prefix##_p hashmap, key_t key) { \ + return (prefix##_get_ptr(hashmap, key) != NULL); \ + } \ + \ + /* Search for the first not-empty slot */ \ + prefix##_it_p prefix##_start(prefix##_p hashmap) { \ + /* We need to start at an invalid slot address since sh_next() increments it \ + // before it looks at it (so it's safe). */ \ + return prefix##_next(hashmap, hashmap->slots - 1); \ + } \ + \ + prefix##_it_p prefix##_next(prefix##_p hashmap, prefix##_it_p it) { \ + if (it == NULL) \ + return NULL; \ + \ + do { \ + it++; \ + /* Check if we're past the last slot */ \ + if (it - hashmap->slots >= hashmap->capacity) \ + return NULL; \ + } while( it->hash_and_flags == SH_SLOT_FREE || it->hash_and_flags == SH_SLOT_DELETED ); \ + \ + return it; \ + } \ + \ + void prefix##_remove(prefix##_p hashmap, prefix##_it_p it) { \ + if (it != NULL && it >= hashmap->slots && it - hashmap->slots < hashmap->capacity) { \ + key_t key = it->key; \ + key = key; /* avoid unused variable warning */ \ + key = key; /* avoid unused variable warning */ \ + it->key = (key_del_expr); \ + it->hash_and_flags = SH_SLOT_DELETED; \ + \ + hashmap->length--; \ + hashmap->deleted++; \ + } \ + } \ + \ + void prefix##_optimize(prefix##_p hashmap) { \ + prefix##_resize(hashmap, hashmap->capacity); \ + } \ + + +#define SH_GEN_HASH_DEF(prefix, key_t, value_t) \ + SH_GEN_DEF(prefix, key_t, value_t, prefix##_murmur3_32(&key, sizeof(key)), (a == b), key, 0) +#define SH_GEN_DICT_DEF(prefix, key_t, value_t) \ + SH_GEN_DEF(prefix, key_t, value_t, prefix##_murmur3_32(key, strlen(key)), (strcmp(a, b) == 0), strdup(key), (free((void*)key), NULL)) -/** - * Get 32-bit Murmur3 hash of a memory block. Taken from - * https://github.com/wolkykim/qlibc/blob/master/src/utilities/qhash.c - * - * MurmurHash3 was created by Austin Appleby in 2008. The initial implementation - * was published in C++ and placed in the public: https://sites.google.com/site/murmurhash/ - * - * Seungyoung Kim has ported its implementation into C language in 2012 and - * published it as a part of qLibc component. - */ -uint32_t sh_murmur3_32(const void *data, size_t size) { - if (data == NULL || size == 0) - return 0; - - const uint32_t c1 = 0xcc9e2d51; - const uint32_t c2 = 0x1b873593; - - const int nblocks = size / 4; - const uint32_t *blocks = (const uint32_t *) (data); - const uint8_t *tail = (const uint8_t *) (data + (nblocks * 4)); - - uint32_t h = 0; - - int i; - uint32_t k; - for (i = 0; i < nblocks; i++) { - k = blocks[i]; - - k *= c1; - k = (k << 15) | (k >> (32 - 15)); - k *= c2; - - h ^= k; - h = (h << 13) | (h >> (32 - 13)); - h = (h * 5) + 0xe6546b64; - } - - k = 0; - switch (size & 3) { - case 3: - k ^= tail[2] << 16; - case 2: - k ^= tail[1] << 8; - case 1: - k ^= tail[0]; - k *= c1; - k = (k << 15) | (k >> (32 - 15)); - k *= c2; - h ^= k; - }; - - h ^= size; - - h ^= h >> 16; - h *= 0x85ebca6b; - h ^= h >> 13; - h *= 0xc2b2ae35; - h ^= h >> 16; - - return h; -} - - -bool sh_resize(sh_hash_p hashmap, size_t new_capacity) { - // on filling empty slot: if exceeding load factor, double capacity - // on deleting slot: if to empty, half capacity - - // Can't make hashmap smaller than it needs to be - if (new_capacity < hashmap->length) - return false; - - sh_hash_t new_hashmap; - new_hashmap.length = 0; - new_hashmap.capacity = new_capacity; - new_hashmap.deleted = 0; - new_hashmap.slots = calloc(new_hashmap.capacity, sizeof(new_hashmap.slots[0])); - - // Failed to allocate memory for new hash map, leave the original untouched - if (new_hashmap.slots == NULL) - return false; - - for(sh_it_p it = sh_start(hashmap); it != NULL; it = sh_next(hashmap, it)) { - sh_put(&new_hashmap, it->key, it->value); - } - - free(hashmap->slots); - *hashmap = new_hashmap; - return true; -} - -void sh_new(sh_hash_p hashmap) { - hashmap->length = 0; - hashmap->capacity = 0; - hashmap->deleted = 0; - hashmap->slots = NULL; - sh_resize(hashmap, 8); -} - -void sh_destroy(sh_hash_p hashmap) { - hashmap->length = 0; - hashmap->capacity = 0; - hashmap->deleted = 0; - free(hashmap->slots); - hashmap->slots = NULL; -} - - -int* sh_put_ptr(sh_hash_p hashmap, int64_t key) { - // add the +1 to the capacity doubling to avoid beeing stuck on a capacity of 0 - if (hashmap->length + hashmap->deleted + 1 > hashmap->capacity * 0.5) - sh_resize(hashmap, (hashmap->capacity + 1) * 2); - - uint32_t hash = sh_murmur3_32(&key, sizeof(key)) | 0x80000000; - size_t index = hash % hashmap->capacity; - while ( !(hashmap->slots[index].hash_and_flags == SH_SLOT_FREE || hashmap->slots[index].hash_and_flags == SH_SLOT_DELETED) ) { - index = (index + 1) % hashmap->capacity; - } - - if (hashmap->slots[index].hash_and_flags == SH_SLOT_DELETED) - hashmap->deleted--; - hashmap->length++; - hashmap->slots[index].hash_and_flags = hash; - hashmap->slots[index].key = key; - return &hashmap->slots[index].value; -} - -int* sh_get_ptr(sh_hash_p hashmap, int64_t key) { - uint32_t hash = sh_murmur3_32(&key, sizeof(key)) | 0x80000000; - size_t index = hash % hashmap->capacity; - while ( !(hashmap->slots[index].hash_and_flags == SH_SLOT_FREE) ) { - if (hashmap->slots[index].hash_and_flags == hash && hashmap->slots[index].key == key) - return &hashmap->slots[index].value; - - index = (index + 1) % hashmap->capacity; - } - - return NULL; -} - -bool sh_del(sh_hash_p hashmap, int64_t key) { - uint32_t hash = sh_murmur3_32(&key, sizeof(key)) | 0x80000000; - size_t index = hash % hashmap->capacity; - while ( !(hashmap->slots[index].hash_and_flags == SH_SLOT_FREE) ) { - if (hashmap->slots[index].hash_and_flags == hash && hashmap->slots[index].key == key) { - hashmap->slots[index].hash_and_flags = SH_SLOT_DELETED; - hashmap->length--; - hashmap->deleted++; - - if (hashmap->length < hashmap->capacity * 0.2) - sh_resize(hashmap, hashmap->capacity / 2); - - return true; - } - - index = (index + 1) % hashmap->capacity; - } - - return false; -} - -void sh_put(sh_hash_p hashmap, int64_t key, int value) { - *sh_put_ptr(hashmap, key) = value; -} - -int sh_get(sh_hash_p hashmap, int64_t key, int default_value) { - int* value_ptr = sh_get_ptr(hashmap, key); - return (value_ptr) ? *value_ptr : default_value; -} - -bool sh_contains(sh_hash_p hashmap, int64_t key) { - return (sh_get_ptr(hashmap, key) != NULL); -} - -// Search for the first not-empty slot -sh_it_p sh_start(sh_hash_p hashmap) { - // We need to start at an invalid slot address since sh_next() increments it - // before it looks at it (so it's safe). - return sh_next(hashmap, hashmap->slots - 1); -} - -sh_it_p sh_next(sh_hash_p hashmap, sh_it_p it) { - if (it == NULL) - return NULL; - - do { - it++; - // Check if we're past the last slot - if (it - hashmap->slots >= hashmap->capacity) - return NULL; - } while( it->hash_and_flags == SH_SLOT_FREE || it->hash_and_flags == SH_SLOT_DELETED ); - - return it; -} - -void sh_remove(sh_hash_p hashmap, sh_it_p it) { - if (it != NULL && it >= hashmap->slots && it - hashmap->slots < hashmap->capacity) { - it->hash_and_flags = SH_SLOT_DELETED; - - hashmap->length--; - hashmap->deleted++; - } -} - -void sh_optimize(sh_hash_p hashmap) { - sh_resize(hashmap, hashmap->capacity); -} #endif // SLIM_HASH_IMPLEMENTATION \ No newline at end of file diff --git a/tests/slim_hash_test.c b/tests/slim_hash_test.c index 45b21f4..fb08c32 100644 --- a/tests/slim_hash_test.c +++ b/tests/slim_hash_test.c @@ -1,14 +1,22 @@ +// For strdup() +#define _GNU_SOURCE +#include + #define SLIM_HASH_IMPLEMENTATION #include "../slim_hash.h" #define SLIM_TEST_IMPLEMENTATION #include "../slim_test.h" +SH_GEN_DECL(sh, int64_t, int); +SH_GEN_HASH_DEF(sh, int64_t, int); +SH_GEN_DECL(dict, const char*, int); +SH_GEN_DICT_DEF(dict, const char*, int); void test_new_and_destroy() { - sh_hash_t hash; + sh_t hash; sh_new(&hash); st_check_int(hash.length, 0); @@ -20,7 +28,7 @@ void test_new_and_destroy() { } void test_put_ptr() { - sh_hash_t hash; + sh_t hash; sh_new(&hash); int* ptr = sh_put_ptr(&hash, 174); @@ -33,7 +41,7 @@ void test_put_ptr() { } void test_get_ptr() { - sh_hash_t hash; + sh_t hash; sh_new(&hash); int* put_ptr = sh_put_ptr(&hash, 174); @@ -46,7 +54,7 @@ void test_get_ptr() { } void test_get_ptr_not_found() { - sh_hash_t hash; + sh_t hash; sh_new(&hash); int* get_ptr = sh_get_ptr(&hash, 12345); @@ -64,7 +72,7 @@ void test_get_ptr_not_found() { } void test_del() { - sh_hash_t hash; + sh_t hash; sh_new(&hash); int* put_ptr = sh_put_ptr(&hash, 174); @@ -83,7 +91,7 @@ void test_del() { } void test_get_and_put() { - sh_hash_t hash; + sh_t hash; sh_new(&hash); sh_put(&hash, 1, 10); @@ -97,7 +105,7 @@ void test_get_and_put() { } void test_contains() { - sh_hash_t hash; + sh_t hash; sh_new(&hash); sh_put(&hash, 1, 10); @@ -111,7 +119,7 @@ void test_contains() { } void test_iteration() { - sh_hash_t hash; + sh_t hash; sh_new(&hash); // empty hash @@ -145,7 +153,7 @@ void test_iteration() { } void test_remove_during_iteration() { - sh_hash_t hash; + sh_t hash; sh_new(&hash); sh_put(&hash, 0, 10); @@ -165,7 +173,7 @@ void test_remove_during_iteration() { } void test_growing() { - sh_hash_t hash; + sh_t hash; sh_new(&hash); st_check_int(hash.length, 0); st_check(hash.capacity < 100); @@ -184,7 +192,7 @@ void test_growing() { } void test_shrinking() { - sh_hash_t hash; + sh_t hash; sh_new(&hash); st_check(hash.capacity < 100); @@ -201,7 +209,7 @@ void test_shrinking() { } void test_optimize() { - sh_hash_t hash; + sh_t hash; sh_new(&hash); for(size_t i = 0; i < 100; i++) @@ -220,6 +228,45 @@ void test_optimize() { sh_destroy(&hash); } +void test_dict() { + dict_t dict; + dict_new(&dict); + + dict_put(&dict, "a", 1); + st_check_int(dict_contains(&dict, "a"), 1); + st_check_int(dict_contains(&dict, "b"), 0); + st_check_int(dict_get(&dict, "a", 0), 1); + + dict_put(&dict, "b", 2); + dict_put(&dict, "c", 3); + st_check_int(dict_get(&dict, "b", 0), 2); + st_check_int(dict_get(&dict, "c", 0), 3); + + dict_del(&dict, "b"); + st_check_int(dict_get(&dict, "a", 0), 1); + st_check_int(dict_get(&dict, "b", 0), 0); + st_check_int(dict_get(&dict, "c", 0), 3); + + dict_destroy(&dict); +} + +void test_dict_update() { + dict_t dict; + dict_new(&dict); + + dict_put(&dict, "x", 7); + st_check_int(dict_get(&dict, "x", 0), 7); + dict_put(&dict, "x", 1); + st_check_int(dict_get(&dict, "x", 0), 1); + + dict_del(&dict, "x"); + st_check_int(dict_get(&dict, "x", 0), 0); + dict_put(&dict, "x", 3); + st_check_int(dict_get(&dict, "x", 0), 3); + + dict_destroy(&dict); +} + int main() { st_run(test_new_and_destroy); @@ -234,5 +281,7 @@ int main() { st_run(test_growing); st_run(test_shrinking); st_run(test_optimize); + st_run(test_dict); + st_run(test_dict_update); return st_show_report(); } \ No newline at end of file