aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cheesemap.c89
-rw-r--r--cheesemap.h7
-rw-r--r--cm-demo.c84
3 files changed, 160 insertions, 20 deletions
diff --git a/cheesemap.c b/cheesemap.c
index 58d1b53..69b8212 100644
--- a/cheesemap.c
+++ b/cheesemap.c
@@ -20,6 +20,10 @@ static inline uintptr_t cm_ctz(uintptr_t val) {
#endif
}
+static inline uintptr_t cm_bitmask_to_index(bitmask_t mask) {
+ return cm_ctz(mask) / CHAR_BIT;
+}
+
static inline uintptr_t cm_clz(uintptr_t val) {
assert(val != 0);
#if defined(__GNUC__) || defined(__clang__)
@@ -96,7 +100,7 @@ static inline bitmask_t cm_group_empty_or_deleted(group_t group) {
}
static inline bitmask_t cm_group_full(group_t group) {
- return ~cm_group_empty_or_deleted(group);
+ return cm_group_empty_or_deleted(group) ^ cm_group_repeat(CM_CTRL_DELETED);
}
/* static ctrl's */
@@ -127,7 +131,7 @@ static inline uint8_t* cm_raw_elem_at(const struct cheesemap_raw* map,
assert(map != NULL);
assert(map->bucket_mask + 1 > index);
- return map->ctrl - entry_size * index;
+ return map->ctrl - entry_size * (index + 1);
}
static inline uint8_t* cm_raw_origin(const struct cheesemap_raw* map,
@@ -159,7 +163,7 @@ static bool cm_raw_find_insert_index_in_group(const struct cheesemap_raw* map,
bitmask_t mask = cm_group_empty_or_deleted(*group);
if (mask == 0) return false;
- uintptr_t bit = cm_ctz(mask);
+ uintptr_t bit = cm_bitmask_to_index(mask);
*out_index = (seq->pos + bit) & map->bucket_mask;
return true;
}
@@ -199,6 +203,35 @@ static void cm_raw_insert_at(struct cheesemap_raw* map, cm_hash_t hash,
map->count += 1;
}
+static void cm_raw_rehash(struct cheesemap_raw* old_map,
+ struct cheesemap_raw* new_map,
+ const struct cheesemap_fns* fns, uintptr_t key_size,
+ uintptr_t value_size) {
+ assert(old_map != NULL);
+ assert(new_map != NULL && fns != NULL);
+
+ uintptr_t entry_size = key_size + value_size;
+
+ struct cheesemap_raw_iter iter;
+ cm_raw_iter_init(&iter, old_map, entry_size, 0);
+
+ uintptr_t index;
+ while (cheesemap_raw_iter_next(&iter, entry_size, &index)) {
+ uint8_t* elem = cm_raw_elem_at(old_map, index, entry_size);
+ cm_hash_t hash = fns->hash(elem, fns->map_usr);
+
+ uintptr_t new_index = cm_raw_find_insert_index(new_map, hash);
+ cm_raw_ctrl_set(new_map, new_index, cm_h2(hash));
+
+ uint8_t* new_elem = cm_raw_elem_at(new_map, new_index, entry_size);
+ memcpy(new_elem, elem, entry_size);
+ }
+
+ new_map->count = old_map->count;
+ new_map->growth_left =
+ cm_buckets_to_capacity(new_map->bucket_mask) - new_map->count;
+}
+
static bool cm_raw_resize(struct cheesemap_raw* map,
const struct cheesemap_fns* fns, uintptr_t key_size,
uintptr_t value_size, uintptr_t new_capacity) {
@@ -207,9 +240,12 @@ static bool cm_raw_resize(struct cheesemap_raw* map,
struct cheesemap_raw new_map;
bool success =
cm_raw_new_with(&new_map, fns, key_size, value_size, new_capacity);
- if (!success) return success;
+ if (!success) return false;
+ cm_raw_rehash(map, &new_map, fns, key_size, value_size);
+ cm_raw_drop(map, key_size, value_size, fns);
+ *map = new_map;
return true;
}
@@ -307,12 +343,16 @@ void cm_drop(struct cheesemap* map) {
}
/* iterator */
-static inline uint8_t* cm_raw_iter_next_entry(const struct cheesemap_raw_iter* iter, uint8_t* old_entry, uintptr_t entry_size) {
+static inline uint8_t* cm_raw_iter_next_entry(
+ const struct cheesemap_raw_iter* iter, uint8_t* old_entry,
+ uintptr_t entry_size) {
assert(iter != NULL);
return old_entry - entry_size * CM_GROUP_SIZE;
}
-void cm_raw_iter_init(struct cheesemap_raw_iter* iter,const struct cheesemap_raw* map, uintptr_t entry_size, uintptr_t start_index){
+void cm_raw_iter_init(struct cheesemap_raw_iter* iter,
+ const struct cheesemap_raw* map, uintptr_t entry_size,
+ uintptr_t start_index) {
assert(map != NULL);
assert(start_index % CM_GROUP_SIZE == 0);
memset(iter, 0, sizeof(*iter));
@@ -322,7 +362,7 @@ void cm_raw_iter_init(struct cheesemap_raw_iter* iter,const struct cheesemap_raw
uint8_t* ctrl = &map->ctrl[start_index];
uint8_t* entry = cm_raw_elem_at(map, start_index, entry_size);
-
+
group_t group = cm_group_load(ctrl);
bitmask_t mask = cm_group_full(group);
@@ -333,25 +373,40 @@ void cm_raw_iter_init(struct cheesemap_raw_iter* iter,const struct cheesemap_raw
iter->end = map->ctrl + buckets;
}
-bool cheesemap_raw_iter_next(struct cheesemap_raw_iter* iter, uintptr_t entry_size, uintptr_t* out_index) {
+static inline void cm_raw_iter_next_inner_slow(
+ struct cheesemap_raw_iter* iter) {
+ assert(iter != NULL);
+
+ group_t group = cm_group_load(iter->n_ctrl);
+ iter->n_ctrl += CM_GROUP_SIZE;
+ iter->curr_mask = cm_group_full(group);
+ iter->curr_index += CM_GROUP_SIZE;
+}
+
+static inline uintptr_t cm_raw_iter_next_inner_fast(
+ struct cheesemap_raw_iter* iter) {
+ assert(iter != NULL);
+
+ uintptr_t bit = cm_bitmask_to_index(iter->curr_mask);
+ iter->curr_mask &= iter->curr_mask - 1;
+
+ return iter->curr_index + bit;
+}
+
+bool cheesemap_raw_iter_next(struct cheesemap_raw_iter* iter,
+ uintptr_t entry_size, uintptr_t* out_index) {
assert(iter != NULL);
assert(out_index != NULL);
while (true) {
if (iter->curr_mask != 0) {
- uintptr_t bit = cm_ctz(iter->curr_mask);
- *out_index = iter->curr_index + bit;
- iter->curr_mask &= iter->curr_mask - 1;
+ *out_index = cm_raw_iter_next_inner_fast(iter);
return true;
}
- if (iter->n_ctrl >= iter->end)
- return false;
+ if (iter->n_ctrl >= iter->end) return false;
- group_t group = cm_group_load(iter->n_ctrl);
- iter->curr_mask = cm_group_full(group);
- iter->curr_index += CM_GROUP_SIZE;
- iter->n_ctrl += CM_GROUP_SIZE;
+ cm_raw_iter_next_inner_slow(iter);
iter->n_entry = cm_raw_iter_next_entry(iter, iter->n_entry, entry_size);
}
}
diff --git a/cheesemap.h b/cheesemap.h
index f8991b1..17a1d42 100644
--- a/cheesemap.h
+++ b/cheesemap.h
@@ -143,7 +143,10 @@ struct cheesemap_raw_iter {
uint8_t* end;
};
-void cm_raw_iter_init(struct cheesemap_raw_iter* iter,const struct cheesemap_raw* map, uintptr_t entry_size, uintptr_t start_index);
-bool cheesemap_raw_iter_next(struct cheesemap_raw_iter* iter, uintptr_t entry_size, uintptr_t* out_index);
+void cm_raw_iter_init(struct cheesemap_raw_iter* iter,
+ const struct cheesemap_raw* map, uintptr_t entry_size,
+ uintptr_t start_index);
+bool cheesemap_raw_iter_next(struct cheesemap_raw_iter* iter,
+ uintptr_t entry_size, uintptr_t* out_index);
#endif
diff --git a/cm-demo.c b/cm-demo.c
index a349379..c784906 100644
--- a/cm-demo.c
+++ b/cm-demo.c
@@ -1,3 +1,85 @@
#include "cheesemap.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
-int main() {}
+void* cm_malloc(uintptr_t size, uint8_t* user) {
+ (void)user;
+ return malloc(size);
+}
+
+void cm_free(void* ptr, uint8_t* user) {
+ (void)user;
+ free(ptr);
+}
+
+cm_hash_t cm_hash(const uint8_t* key, uint8_t* user) {
+ (void)user;
+ uint64_t k = *(const uint64_t*)key;
+ k ^= k >> 33;
+ k *= 0xff51afd7ed558ccdULL;
+ k ^= k >> 33;
+ k *= 0xc4ceb9fe1a85ec53ULL;
+ k ^= k >> 33;
+ return k;
+}
+
+bool cm_compare(const uint8_t* key1, const uint8_t* key2, uint8_t* user) {
+ (void)user;
+ return *(const uint64_t*)key1 == *(const uint64_t*)key2;
+}
+
+int main() {
+ struct cheesemap map;
+ cm_new(&map, sizeof(uint64_t), sizeof(uint64_t),
+ NULL, cm_malloc, cm_free,
+ NULL, cm_hash, cm_compare);
+
+ const uint64_t num_entries = 1000000;
+ printf("Stress test: inserting %lu entries...\n", num_entries);
+
+ for (uint64_t i = 0; i < num_entries; i++) {
+ uint64_t key = i;
+ uint64_t value = i * 2;
+ bool success = cm_raw_insert(&map.raw, &map.fns,
+ sizeof(uint64_t), (uint8_t*)&key,
+ sizeof(uint64_t), (uint8_t*)&value);
+ if (!success) {
+ printf("Insert failed at i=%lu\n", i);
+ cm_drop(&map);
+ return 1;
+ }
+
+ if ((i + 1) % 100000 == 0) {
+ printf(" Progress: %lu entries, %lu buckets, %lu growth_left\n",
+ map.raw.count, map.raw.bucket_mask + 1, map.raw.growth_left);
+ }
+ }
+
+ printf("\nSuccessfully inserted %lu entries!\n", num_entries);
+ printf("Map count: %lu\n", map.raw.count);
+ printf("Map buckets: %lu\n", map.raw.bucket_mask + 1);
+ printf("Growth left: %lu\n", map.raw.growth_left);
+
+ // Calculate memory usage
+ uintptr_t entry_size = sizeof(uint64_t) + sizeof(uint64_t);
+ uintptr_t buckets = map.raw.bucket_mask + 1;
+ uintptr_t data_size = entry_size * buckets;
+ uintptr_t ctrl_size = buckets + 8; // buckets + mirror
+ uintptr_t total_size = data_size + ctrl_size;
+
+ printf("\nMemory usage:\n");
+ printf(" Entries: %lu x %lu bytes = %lu bytes\n",
+ buckets, entry_size, data_size);
+ printf(" Control: %lu bytes\n", ctrl_size);
+ printf(" Total: %lu bytes (%.2f MB)\n",
+ total_size, total_size / (1024.0 * 1024.0));
+ printf(" Load factor: %.2f%% (%lu / %lu)\n",
+ (map.raw.count * 100.0) / buckets, map.raw.count, buckets);
+ printf(" Overhead: %.2f%% ((total - actual) / actual)\n",
+ ((total_size - (num_entries * entry_size)) * 100.0) / (num_entries * entry_size));
+
+ cm_drop(&map);
+ printf("\nDone!\n");
+ return 0;
+}