diff --git a/src/flat_hash_map.c b/src/flat_hash_map.c index 5ebadef2..00fb57dd 100644 --- a/src/flat_hash_map.c +++ b/src/flat_hash_map.c @@ -46,7 +46,6 @@ first thought. However, improvements can still be made. */ #include "impl/impl_types.h" #include "types.h" -#include #include #include #include @@ -198,7 +197,7 @@ ccc_fhm_size(ccc_flat_hash_map const *const h) } ccc_fhmap_entry -ccc_fhm_entry(ccc_flat_hash_map *h, void const *const key) +ccc_fhm_entry(ccc_flat_hash_map *const h, void const *const key) { if (unlikely(!h || !key)) { @@ -269,7 +268,7 @@ ccc_fhm_and_modify(ccc_fhmap_entry *const e, ccc_update_fn *const fn) ccc_fhmap_entry * ccc_fhm_and_modify_aux(ccc_fhmap_entry *const e, ccc_update_fn *const fn, - void *aux) + void *const aux) { if (unlikely(!e)) { @@ -283,7 +282,7 @@ ccc_fhm_and_modify_aux(ccc_fhmap_entry *const e, ccc_update_fn *const fn, } ccc_entry -ccc_fhm_insert(ccc_flat_hash_map *h, ccc_fhmap_elem *const out_handle) +ccc_fhm_insert(ccc_flat_hash_map *const h, ccc_fhmap_elem *const out_handle) { if (unlikely(!h || !out_handle)) { @@ -332,7 +331,8 @@ ccc_fhm_try_insert(ccc_flat_hash_map *const h, } ccc_entry -ccc_fhm_insert_or_assign(ccc_flat_hash_map *h, ccc_fhmap_elem *key_val_handle) +ccc_fhm_insert_or_assign(ccc_flat_hash_map *const h, + ccc_fhmap_elem *const key_val_handle) { if (unlikely(!h || !key_val_handle)) { @@ -442,28 +442,28 @@ ccc_fhm_begin(ccc_flat_hash_map const *const h) { return NULL; } - void *iter = ccc_buf_begin(&h->buf_); + void const *iter = ccc_buf_begin(&h->buf_); for (; iter != ccc_buf_capacity_end(&h->buf_) && elem_in_slot(h, iter)->hash_ == CCC_FHM_EMPTY; iter = ccc_buf_next(&h->buf_, iter)) {} - return iter == ccc_buf_capacity_end(&h->buf_) ? NULL : iter; + return (void *)(iter == ccc_buf_capacity_end(&h->buf_) ? NULL : iter); } void * -ccc_fhm_next(ccc_flat_hash_map const *const h, ccc_fhmap_elem const *iter) +ccc_fhm_next(ccc_flat_hash_map const *const h, ccc_fhmap_elem const *const iter) { if (unlikely(!h)) { return NULL; } - void *i = struct_base(h, iter); + void const *i = struct_base(h, iter); for (i = ccc_buf_next(&h->buf_, i); i != ccc_buf_capacity_end(&h->buf_) && elem_in_slot(h, i)->hash_ == CCC_FHM_EMPTY; i = ccc_buf_next(&h->buf_, i)) {} - return i == ccc_buf_capacity_end(&h->buf_) ? NULL : i; + return (void *)(i == ccc_buf_capacity_end(&h->buf_) ? NULL : i); } void * @@ -473,7 +473,7 @@ ccc_fhm_end(ccc_flat_hash_map const *const) } size_t -ccc_fhm_next_prime(size_t n) +ccc_fhm_next_prime(size_t const n) { return next_prime(n); } @@ -607,7 +607,8 @@ ccc_fhm_validate(ccc_flat_hash_map const *const h) } static bool -valid_distance_from_home(struct ccc_fhmap_ const *h, void const *slot) +valid_distance_from_home(struct ccc_fhmap_ const *const h, + void const *const slot) { size_t const cap = ccc_buf_capacity(&h->buf_); uint64_t const hash = elem_in_slot(h, slot)->hash_; @@ -640,7 +641,7 @@ valid_distance_from_home(struct ccc_fhmap_ const *h, void const *slot) /*======================= Private Interface =============================*/ struct ccc_fhash_entry_ -ccc_impl_fhm_entry(struct ccc_fhmap_ *h, void const *key) +ccc_impl_fhm_entry(struct ccc_fhmap_ *const h, void const *const key) { return container_entry(h, key); } @@ -667,7 +668,7 @@ ccc_impl_fhm_insert(struct ccc_fhmap_ *const h, void const *const e, } ccc_result -ccc_impl_fhm_maybe_resize(struct ccc_fhmap_ *h) +ccc_impl_fhm_maybe_resize(struct ccc_fhmap_ *const h) { return maybe_resize(h); } @@ -679,7 +680,8 @@ ccc_impl_fhm_in_slot(struct ccc_fhmap_ const *const h, void const *const slot) } void * -ccc_impl_fhm_key_in_slot(struct ccc_fhmap_ const *h, void const *slot) +ccc_impl_fhm_key_in_slot(struct ccc_fhmap_ const *const h, + void const *const slot) { return key_in_slot(h, slot); } @@ -691,7 +693,7 @@ ccc_impl_fhm_distance(size_t const capacity, size_t const i, size_t const j) } size_t -ccc_impl_fhm_increment(size_t const capacity, size_t i) +ccc_impl_fhm_increment(size_t const capacity, size_t const i) { return increment(capacity, i); } @@ -717,7 +719,7 @@ ccc_impl_fhm_filter(struct ccc_fhmap_ const *const h, void const *const key) /*======================= Static Helpers =============================*/ static inline struct ccc_ent_ -entry(struct ccc_fhmap_ *const h, void const *key, uint64_t const hash) +entry(struct ccc_fhmap_ *const h, void const *const key, uint64_t const hash) { char upcoming_insertion_error = 0; if (maybe_resize(h) != CCC_OK) @@ -923,9 +925,9 @@ maybe_resize(struct ccc_fhmap_ *const h) } static inline size_t -next_prime(size_t n) +next_prime(size_t const n) { - /* Compiler should help us out here. No need to do fancy bit tricks to + /* Compiler should help us out here. No fancy binary search/tricks to tell how far along the list we are. The table is small already. */ for (size_t i = 0; i < PRIMES_SIZE; ++i) { @@ -972,16 +974,15 @@ distance(size_t const capacity, size_t const i, size_t const j) } static inline size_t -increment(size_t const capacity, size_t i) +increment(size_t const capacity, size_t const i) { return (i + 1) >= capacity ? last_swap_slot + 1 : i + 1; } static inline size_t -decrement(size_t const capacity, size_t i) +decrement(size_t const capacity, size_t const i) { - i = i ? i - 1 : capacity - 1; - return i <= last_swap_slot ? capacity - 1 : i; + return i <= num_swap_slots ? capacity - 1 : i - 1; } static inline void * @@ -992,7 +993,7 @@ struct_base(struct ccc_fhmap_ const *const h, } static inline void -swap(char tmp[], void *const a, void *const b, size_t ab_size) +swap(char tmp[const], void *const a, void *const b, size_t const ab_size) { if (a == b) { diff --git a/src/flat_ordered_map.c b/src/flat_ordered_map.c index b1c7c016..5d0f83e7 100644 --- a/src/flat_ordered_map.c +++ b/src/flat_ordered_map.c @@ -202,7 +202,7 @@ ccc_fom_insert(ccc_flat_ordered_map *const fom, { return (ccc_entry){{.stats_ = CCC_ENTRY_INPUT_ERROR}}; } - void *found = find(fom, key_from_node(fom, out_handle)); + void *const found = find(fom, key_from_node(fom, out_handle)); if (found) { assert(fom->root_); @@ -211,9 +211,9 @@ ccc_fom_insert(ccc_flat_ordered_map *const fom, void *const ret = base_at(fom, fom->root_); void *const tmp = ccc_buf_at(&fom->buf_, 0); swap(tmp, user_struct, ret, ccc_buf_elem_size(&fom->buf_)); - return (ccc_entry){{.e_ = ret, .stats_ = CCC_ENTRY_OCCUPIED}}; + return (ccc_entry){{.e_ = user_struct, .stats_ = CCC_ENTRY_OCCUPIED}}; } - void *inserted = maybe_alloc_insert(fom, out_handle); + void *const inserted = maybe_alloc_insert(fom, out_handle); if (!inserted) { return (ccc_entry){{.e_ = NULL, .stats_ = CCC_ENTRY_INSERT_ERROR}}; diff --git a/src/flat_priority_queue.c b/src/flat_priority_queue.c index 8ec3c54f..3f182b73 100644 --- a/src/flat_priority_queue.c +++ b/src/flat_priority_queue.c @@ -10,6 +10,8 @@ static size_t const swap_space = 1; +/*===================== Prototypes ================================*/ + static void *at(struct ccc_fpq_ const *, size_t); static size_t index_of(struct ccc_fpq_ const *, void const *); static bool wins(struct ccc_fpq_ const *, void const *winner, @@ -19,6 +21,8 @@ static size_t bubble_up(struct ccc_fpq_ *fpq, char tmp[], size_t i); static size_t bubble_down(struct ccc_fpq_ *, char tmp[], size_t); static size_t update_fixup(struct ccc_fpq_ *, void *e); +/*===================== Interface ================================*/ + ccc_result ccc_fpq_alloc(ccc_flat_priority_queue *const fpq, size_t const new_capacity, ccc_alloc_fn *const fn) @@ -155,7 +159,7 @@ ccc_fpq_erase(ccc_flat_priority_queue *const fpq, void *const e) void * ccc_fpq_update(ccc_flat_priority_queue *const fpq, void *const e, - ccc_update_fn *fn, void *aux) + ccc_update_fn *const fn, void *const aux) { if (!fpq || !e || !fn || ccc_buf_is_empty(&fpq->buf_)) { @@ -335,7 +339,7 @@ ccc_fpq_validate(ccc_flat_priority_queue const *const fpq) return true; } -/*=========================== Implementation ========================== */ +/*=================== Private Interface =============================*/ size_t ccc_impl_fpq_bubble_up(struct ccc_fpq_ *const fpq, char tmp[], size_t i) @@ -364,8 +368,9 @@ ccc_impl_fpq_in_place_heapify(struct ccc_fpq_ *const fpq, size_t const n) } } -/*=============================== Static Helpers =========================*/ +/*==================== Static Helpers ===============================*/ +/* Fixes the position of element e after its key value has been changed. */ static inline size_t update_fixup(struct ccc_fpq_ *const fpq, void *const e) { @@ -389,11 +394,13 @@ update_fixup(struct ccc_fpq_ *const fpq, void *const e) return i; } +/* Returns the sorted position of the element starting at position i. */ static inline size_t -bubble_up(struct ccc_fpq_ *const fpq, char tmp[], size_t i) +bubble_up(struct ccc_fpq_ *const fpq, char tmp[const], size_t i) { for (size_t parent = (i - 1) / 2; i; i = parent, parent = (parent - 1) / 2) { + /* Not winning here means we are in correct order or equal. */ if (!wins(fpq, at(fpq, i), at(fpq, parent))) { return i; @@ -403,16 +410,17 @@ bubble_up(struct ccc_fpq_ *const fpq, char tmp[], size_t i) return 0; } +/* Returns the sorted position of the element starting at position i. */ static inline size_t -bubble_down(struct ccc_fpq_ *const fpq, char tmp[], size_t i) +bubble_down(struct ccc_fpq_ *const fpq, char tmp[const], size_t i) { size_t const sz = ccc_buf_size(&fpq->buf_); for (size_t next = i, left = (i * 2) + 1, right = left + 1; left < sz; i = next, left = (i * 2) + 1, right = left + 1) { /* Avoid one comparison call if there is no right child. */ - next = right < sz && wins(fpq, at(fpq, right), at(fpq, left)) ? right - : left; + next = (right < sz && wins(fpq, at(fpq, right), at(fpq, left))) ? right + : left; /* If the child beats the parent we must swap. Equal is ok to break. */ if (!wins(fpq, at(fpq, next), at(fpq, i))) { @@ -423,8 +431,11 @@ bubble_down(struct ccc_fpq_ *const fpq, char tmp[], size_t i) return i; } +/* Swaps i and j using the underlying buffer capabilities. Not checked for + an error in release. */ static inline void -swap(struct ccc_fpq_ *const fpq, char tmp[], size_t const i, size_t const j) +swap(struct ccc_fpq_ *const fpq, char tmp[const], size_t const i, + size_t const j) { [[maybe_unused]] ccc_result const res = ccc_buf_swap(&fpq->buf_, tmp, i, j); assert(res == CCC_OK); @@ -440,7 +451,7 @@ at(struct ccc_fpq_ const *const fpq, size_t const i) return addr; } -/* Flat pqueue code that uses indices of the underlying buffer should always +/* Flat pq code that uses indices of the underlying buffer should always be within the buffer range. It should never exceed the current size and start at or after the buffer base. Only checked in debug. */ static inline size_t @@ -453,6 +464,11 @@ index_of(struct ccc_fpq_ const *const fpq, void const *const slot) return i; } +/* Returns true if the winner (the "left hand side") wins the comparison. + Winning in a three-way comparison means satisfying the total order of the + priority queue. So, there is no winner if the elements are equal and this + function would return false. If the winner is in the wrong order, thus + losing the total order comparison, the function also returns false. */ static inline bool wins(struct ccc_fpq_ const *const fpq, void const *const winner, void const *const loser) diff --git a/src/flat_realtime_ordered_map.c b/src/flat_realtime_ordered_map.c index d451b612..a72db351 100644 --- a/src/flat_realtime_ordered_map.c +++ b/src/flat_realtime_ordered_map.c @@ -115,26 +115,25 @@ static size_t *branch_r(struct ccc_fromap_ const *t, size_t node, enum frm_branch_ branch); static size_t *parent_r(struct ccc_fromap_ const *t, size_t node); /* Returning WAVL tree status. */ -static bool is_0_child(struct ccc_fromap_ const *, size_t p_of_x, size_t x); -static bool is_1_child(struct ccc_fromap_ const *, size_t p_of_x, size_t x); -static bool is_2_child(struct ccc_fromap_ const *, size_t p_of_x, size_t x); -static bool is_3_child(struct ccc_fromap_ const *, size_t p_of_x, size_t x); -static bool is_01_parent(struct ccc_fromap_ const *, size_t x, size_t p_of_xy, +static bool is_0_child(struct ccc_fromap_ const *, size_t p, size_t x); +static bool is_1_child(struct ccc_fromap_ const *, size_t p, size_t x); +static bool is_2_child(struct ccc_fromap_ const *, size_t p, size_t x); +static bool is_3_child(struct ccc_fromap_ const *, size_t p, size_t x); +static bool is_01_parent(struct ccc_fromap_ const *, size_t x, size_t p, size_t y); -static bool is_11_parent(struct ccc_fromap_ const *, size_t x, size_t p_of_xy, +static bool is_11_parent(struct ccc_fromap_ const *, size_t x, size_t p, size_t y); -static bool is_02_parent(struct ccc_fromap_ const *, size_t x, size_t p_of_xy, +static bool is_02_parent(struct ccc_fromap_ const *, size_t x, size_t p, size_t y); -static bool is_22_parent(struct ccc_fromap_ const *, size_t x, size_t p_of_xy, +static bool is_22_parent(struct ccc_fromap_ const *, size_t x, size_t p, size_t y); static bool is_leaf(struct ccc_fromap_ const *t, size_t x); static uint8_t parity(struct ccc_fromap_ const *t, size_t node); static bool validate(struct ccc_fromap_ const *frm); /* Returning void and maintaining the WAVL tree. */ static void init_node(struct ccc_fromap_elem_ *e); -static void insert_fixup(struct ccc_fromap_ *t, size_t z_p_of_xy, size_t x); -static void rebalance_3_child(struct ccc_fromap_ *t, size_t z_p_of_xy, - size_t x); +static void insert_fixup(struct ccc_fromap_ *t, size_t z, size_t x); +static void rebalance_3_child(struct ccc_fromap_ *t, size_t z, size_t x); static void transplant(struct ccc_fromap_ *t, size_t remove, size_t replacement); static void swap_and_pop(struct ccc_fromap_ *t, size_t vacant_i); @@ -143,10 +142,10 @@ static void demote(struct ccc_fromap_ const *t, size_t x); static void double_promote(struct ccc_fromap_ const *t, size_t x); static void double_demote(struct ccc_fromap_ const *t, size_t x); -static void rotate(struct ccc_fromap_ *t, size_t z_p_of_x, size_t x_p_of_y, - size_t y, enum frm_branch_ dir); -static void double_rotate(struct ccc_fromap_ *t, size_t z_p_of_x, - size_t x_p_of_y, size_t y, enum frm_branch_ dir); +static void rotate(struct ccc_fromap_ *t, size_t z, size_t x, size_t y, + enum frm_branch_ dir); +static void double_rotate(struct ccc_fromap_ *t, size_t z, size_t x, size_t y, + enum frm_branch_ dir); /* Returning void as miscellaneous helpers. */ static void swap(char tmp[], void *a, void *b, size_t elem_sz); @@ -171,7 +170,7 @@ ccc_frm_get_key_val(ccc_flat_realtime_ordered_map const *const frm, { return NULL; } - struct frm_query_ q = find(frm, key); + struct frm_query_ const q = find(frm, key); return (CCC_EQL == q.last_cmp_) ? base_at(frm, q.found_) : NULL; } @@ -183,7 +182,7 @@ ccc_frm_insert(ccc_flat_realtime_ordered_map *const frm, { return (ccc_entry){{.stats_ = CCC_ENTRY_INPUT_ERROR}}; } - struct frm_query_ q = find(frm, key_from_node(frm, out_handle)); + struct frm_query_ const q = find(frm, key_from_node(frm, out_handle)); if (CCC_EQL == q.last_cmp_) { void *const slot = ccc_buf_at(&frm->buf_, q.found_); @@ -192,11 +191,9 @@ ccc_frm_insert(ccc_flat_realtime_ordered_map *const frm, void *const tmp = ccc_buf_at(&frm->buf_, 0); swap(tmp, user_struct, slot, ccc_buf_elem_size(&frm->buf_)); elem_in_slot(frm, tmp)->parity_ = 1; - return (ccc_entry){{.e_ = slot, .stats_ = CCC_ENTRY_OCCUPIED}}; + return (ccc_entry){{.e_ = user_struct, .stats_ = CCC_ENTRY_OCCUPIED}}; } - void *const inserted - = maybe_alloc_insert(frm, q.parent_, q.last_cmp_, out_handle); - if (!inserted) + if (!maybe_alloc_insert(frm, q.parent_, q.last_cmp_, out_handle)) { return (ccc_entry){{.e_ = NULL, .stats_ = CCC_ENTRY_INSERT_ERROR}}; } @@ -211,13 +208,13 @@ ccc_frm_try_insert(ccc_flat_realtime_ordered_map *const frm, { return (ccc_entry){{.stats_ = CCC_ENTRY_INPUT_ERROR}}; } - struct frm_query_ q = find(frm, key_from_node(frm, key_val_handle)); + struct frm_query_ const q = find(frm, key_from_node(frm, key_val_handle)); if (CCC_EQL == q.last_cmp_) { return (ccc_entry){ {.e_ = base_at(frm, q.found_), .stats_ = CCC_ENTRY_OCCUPIED}}; } - void *inserted + void *const inserted = maybe_alloc_insert(frm, q.parent_, q.last_cmp_, key_val_handle); if (!inserted) { @@ -234,7 +231,7 @@ ccc_frm_insert_or_assign(ccc_flat_realtime_ordered_map *const frm, { return (ccc_entry){{.stats_ = CCC_ENTRY_INPUT_ERROR}}; } - struct frm_query_ q = find(frm, key_from_node(frm, key_val_handle)); + struct frm_query_ const q = find(frm, key_from_node(frm, key_val_handle)); if (CCC_EQL == q.last_cmp_) { void *const found = base_at(frm, q.found_); @@ -243,7 +240,7 @@ ccc_frm_insert_or_assign(ccc_flat_realtime_ordered_map *const frm, struct_base(frm, key_val_handle)); return (ccc_entry){{.e_ = found, .stats_ = CCC_ENTRY_OCCUPIED}}; } - void *inserted + void *const inserted = maybe_alloc_insert(frm, q.parent_, q.last_cmp_, key_val_handle); if (!inserted) { @@ -529,7 +526,7 @@ ccc_frm_copy(ccc_flat_realtime_ordered_map *const dst, dst->buf_.alloc_ = dst_alloc; if (dst->buf_.capacity_ < src->buf_.capacity_) { - ccc_result resize_res + ccc_result const resize_res = ccc_buf_alloc(&dst->buf_, src->buf_.capacity_, fn); if (resize_res != CCC_OK) { @@ -679,7 +676,7 @@ insert(struct ccc_fromap_ *const frm, size_t const parent_i, static inline struct ccc_frtree_entry_ entry(struct ccc_fromap_ const *const frm, void const *const key) { - struct frm_query_ q = find(frm, key); + struct frm_query_ const q = find(frm, key); if (CCC_EQL == q.last_cmp_) { return (struct ccc_frtree_entry_){ @@ -865,7 +862,7 @@ branch_r(struct ccc_fromap_ const *t, size_t const node, } static inline size_t * -parent_r(struct ccc_fromap_ const *t, size_t node) +parent_r(struct ccc_fromap_ const *t, size_t const node) { return &elem_in_slot(t, ccc_buf_at(&t->buf_, node))->parent_; @@ -907,39 +904,39 @@ key_at(struct ccc_fromap_ const *const t, size_t const i) /*======================= WAVL Tree Maintenance =========================*/ static void -insert_fixup(struct ccc_fromap_ *const t, size_t z_p_of_xy, size_t x) +insert_fixup(struct ccc_fromap_ *const t, size_t z, size_t x) { do { - promote(t, z_p_of_xy); - x = z_p_of_xy; - z_p_of_xy = parent_i(t, z_p_of_xy); - if (!z_p_of_xy) + promote(t, z); + x = z; + z = parent_i(t, z); + if (!z) { return; } - } while (is_01_parent(t, x, z_p_of_xy, sibling_of(t, x))); + } while (is_01_parent(t, x, z, sibling_of(t, x))); - if (!is_02_parent(t, x, z_p_of_xy, sibling_of(t, x))) + if (!is_02_parent(t, x, z, sibling_of(t, x))) { return; } assert(x); - assert(is_0_child(t, z_p_of_xy, x)); - enum frm_branch_ const p_to_x_dir = branch_i(t, z_p_of_xy, R) == x; + assert(is_0_child(t, z, x)); + enum frm_branch_ const p_to_x_dir = branch_i(t, z, R) == x; size_t const y = branch_i(t, x, !p_to_x_dir); - if (!y || is_2_child(t, z_p_of_xy, y)) + if (!y || is_2_child(t, z, y)) { - rotate(t, z_p_of_xy, x, y, !p_to_x_dir); - demote(t, z_p_of_xy); + rotate(t, z, x, y, !p_to_x_dir); + demote(t, z); } else { - assert(is_1_child(t, z_p_of_xy, y)); - double_rotate(t, z_p_of_xy, x, y, p_to_x_dir); + assert(is_1_child(t, z, y)); + double_rotate(t, z, x, y, p_to_x_dir); promote(t, y); demote(t, x); - demote(t, z_p_of_xy); + demote(t, z); } } @@ -948,59 +945,58 @@ remove_fixup(struct ccc_fromap_ *const t, size_t const remove) { size_t y = 0; size_t x = 0; - size_t p_of_xy = 0; + size_t p = 0; bool two_child = false; if (!branch_i(t, remove, R) || !branch_i(t, remove, L)) { y = remove; - p_of_xy = parent_i(t, y); + p = parent_i(t, y); x = branch_i(t, y, !branch_i(t, y, L)); *parent_r(t, x) = parent_i(t, y); - if (!p_of_xy) + if (!p) { t->root_ = x; } - two_child = is_2_child(t, p_of_xy, y); - *branch_r(t, p_of_xy, branch_i(t, p_of_xy, R) == y) = x; + two_child = is_2_child(t, p, y); + *branch_r(t, p, branch_i(t, p, R) == y) = x; } else { y = min_max_from(t, branch_i(t, remove, R), min); - p_of_xy = parent_i(t, y); + p = parent_i(t, y); x = branch_i(t, y, !branch_i(t, y, L)); *parent_r(t, x) = parent_i(t, y); /* Save if check and improve readability by assuming this is true. */ - assert(p_of_xy); + assert(p); - two_child = is_2_child(t, p_of_xy, y); - *branch_r(t, p_of_xy, branch_i(t, p_of_xy, R) == y) = x; + two_child = is_2_child(t, p, y); + *branch_r(t, p, branch_i(t, p, R) == y) = x; transplant(t, remove, y); - if (remove == p_of_xy) + if (remove == p) { - p_of_xy = y; + p = y; } } - if (p_of_xy) + if (p) { if (two_child) { - assert(p_of_xy); - rebalance_3_child(t, p_of_xy, x); + assert(p); + rebalance_3_child(t, p, x); } - else if (!x && branch_i(t, p_of_xy, L) == branch_i(t, p_of_xy, R)) + else if (!x && branch_i(t, p, L) == branch_i(t, p, R)) { - assert(p_of_xy); - bool const demote_makes_3_child - = is_2_child(t, parent_i(t, p_of_xy), p_of_xy); - demote(t, p_of_xy); + assert(p); + bool const demote_makes_3_child = is_2_child(t, parent_i(t, p), p); + demote(t, p); if (demote_makes_3_child) { - rebalance_3_child(t, parent_i(t, p_of_xy), p_of_xy); + rebalance_3_child(t, parent_i(t, p), p); } } - assert(!is_leaf(t, p_of_xy) || !parity(t, p_of_xy)); + assert(!is_leaf(t, p) || !parity(t, p)); } swap_and_pop(t, remove); return base_at(t, ccc_buf_size(&t->buf_)); @@ -1032,37 +1028,37 @@ transplant(struct ccc_fromap_ *const t, size_t const remove, } static inline void -rebalance_3_child(struct ccc_fromap_ *const t, size_t z_p_of_xy, size_t x) +rebalance_3_child(struct ccc_fromap_ *const t, size_t z, size_t x) { - assert(z_p_of_xy); + assert(z); bool made_3_child = false; do { - size_t const p_of_p_of_x = parent_i(t, z_p_of_xy); - size_t const y = branch_i(t, z_p_of_xy, branch_i(t, z_p_of_xy, L) == x); - made_3_child = is_2_child(t, p_of_p_of_x, z_p_of_xy); - if (is_2_child(t, z_p_of_xy, y)) + size_t const g = parent_i(t, z); + size_t const y = branch_i(t, z, branch_i(t, z, L) == x); + made_3_child = is_2_child(t, g, z); + if (is_2_child(t, z, y)) { - demote(t, z_p_of_xy); + demote(t, z); } else if (is_22_parent(t, branch_i(t, y, L), y, branch_i(t, y, R))) { - demote(t, z_p_of_xy); + demote(t, z); demote(t, y); } else /* p(x) is 1,3, y is not a 2,2 parent, and x is 3-child.*/ { - assert(is_3_child(t, z_p_of_xy, x)); - enum frm_branch_ const z_to_x_dir = branch_i(t, z_p_of_xy, R) == x; + assert(is_3_child(t, z, x)); + enum frm_branch_ const z_to_x_dir = branch_i(t, z, R) == x; size_t const w = branch_i(t, y, !z_to_x_dir); if (is_1_child(t, y, w)) { - rotate(t, z_p_of_xy, y, branch_i(t, y, z_to_x_dir), z_to_x_dir); + rotate(t, z, y, branch_i(t, y, z_to_x_dir), z_to_x_dir); promote(t, y); - demote(t, z_p_of_xy); - if (is_leaf(t, z_p_of_xy)) + demote(t, z); + if (is_leaf(t, z)) { - demote(t, z_p_of_xy); + demote(t, z); } } else /* w is a 2-child and v will be a 1-child. */ @@ -1070,10 +1066,10 @@ rebalance_3_child(struct ccc_fromap_ *const t, size_t z_p_of_xy, size_t x) size_t const v = branch_i(t, y, z_to_x_dir); assert(is_2_child(t, y, w)); assert(is_1_child(t, y, v)); - double_rotate(t, z_p_of_xy, y, v, !z_to_x_dir); + double_rotate(t, z, y, v, !z_to_x_dir); double_promote(t, v); demote(t, y); - double_demote(t, z_p_of_xy); + double_demote(t, z); /* Optional "Rebalancing with Promotion," defined as follows: if node z is a non-leaf 1,1 node, we promote it; otherwise, if y is a non-leaf 1,1 node, we promote it. @@ -1081,11 +1077,10 @@ rebalance_3_child(struct ccc_fromap_ *const t, size_t z_p_of_xy, size_t x) This reduces constants in some of theorems mentioned in the paper but may not be worth doing. Rotations stay at 2 worst case. Should revisit after more performance testing. */ - if (!is_leaf(t, z_p_of_xy) - && is_11_parent(t, branch_i(t, z_p_of_xy, L), z_p_of_xy, - branch_i(t, z_p_of_xy, R))) + if (!is_leaf(t, z) + && is_11_parent(t, branch_i(t, z, L), z, branch_i(t, z, R))) { - promote(t, z_p_of_xy); + promote(t, z); } else if (!is_leaf(t, y) && is_11_parent(t, branch_i(t, y, L), y, @@ -1096,9 +1091,9 @@ rebalance_3_child(struct ccc_fromap_ *const t, size_t z_p_of_xy, size_t x) } return; } - x = z_p_of_xy; - z_p_of_xy = p_of_p_of_x; - } while (z_p_of_xy && made_3_child); + x = z; + z = g; + } while (z && made_3_child); } /** Swaps in the back buffer element into vacated slot*/ @@ -1144,27 +1139,27 @@ and uppercase are arbitrary subtrees. │ │ B B */ static inline void -rotate(struct ccc_fromap_ *const t, size_t const z_p_of_x, - size_t const x_p_of_y, size_t const y, enum frm_branch_ const dir) +rotate(struct ccc_fromap_ *const t, size_t const z, size_t const x, + size_t const y, enum frm_branch_ const dir) { - assert(z_p_of_x); - struct ccc_fromap_elem_ *const z_r = at(t, z_p_of_x); - struct ccc_fromap_elem_ *const x_r = at(t, x_p_of_y); - size_t const p_of_p_of_x = parent_i(t, z_p_of_x); - x_r->parent_ = p_of_p_of_x; - if (!p_of_p_of_x) + assert(z); + struct ccc_fromap_elem_ *const z_r = at(t, z); + struct ccc_fromap_elem_ *const x_r = at(t, x); + size_t const g = parent_i(t, z); + x_r->parent_ = g; + if (!g) { - t->root_ = x_p_of_y; + t->root_ = x; } else { - struct ccc_fromap_elem_ *const g = at(t, p_of_p_of_x); - g->branch_[g->branch_[R] == z_p_of_x] = x_p_of_y; + struct ccc_fromap_elem_ *const g_r = at(t, g); + g_r->branch_[g_r->branch_[R] == z] = x; } - x_r->branch_[dir] = z_p_of_x; - z_r->parent_ = x_p_of_y; + x_r->branch_[dir] = z; + z_r->parent_ = x; z_r->branch_[!dir] = y; - *parent_r(t, y) = z_p_of_x; + *parent_r(t, y) = z; } /** A double rotation shouldn't actually be two calls to rotate because that @@ -1179,32 +1174,32 @@ Lowercase are nodes and uppercase are arbitrary subtrees. ╭─┴─╮ B C */ static inline void -double_rotate(struct ccc_fromap_ *const t, size_t const z_p_of_x, - size_t const x_p_of_y, size_t const y, enum frm_branch_ const dir) +double_rotate(struct ccc_fromap_ *const t, size_t const z, size_t const x, + size_t const y, enum frm_branch_ const dir) { - assert(z_p_of_x && x_p_of_y && y); - struct ccc_fromap_elem_ *const z_r = at(t, z_p_of_x); - struct ccc_fromap_elem_ *const x_r = at(t, x_p_of_y); + assert(z && x && y); + struct ccc_fromap_elem_ *const z_r = at(t, z); + struct ccc_fromap_elem_ *const x_r = at(t, x); struct ccc_fromap_elem_ *const y_r = at(t, y); - size_t const p_of_p_of_x = z_r->parent_; - y_r->parent_ = p_of_p_of_x; - if (!p_of_p_of_x) + size_t const g = z_r->parent_; + y_r->parent_ = g; + if (!g) { t->root_ = y; } else { - struct ccc_fromap_elem_ *const g = at(t, p_of_p_of_x); - g->branch_[g->branch_[R] == z_p_of_x] = y; + struct ccc_fromap_elem_ *const g_r = at(t, g); + g_r->branch_[g_r->branch_[R] == z] = y; } x_r->branch_[!dir] = y_r->branch_[dir]; - *parent_r(t, y_r->branch_[dir]) = x_p_of_y; - y_r->branch_[dir] = x_p_of_y; + *parent_r(t, y_r->branch_[dir]) = x; + y_r->branch_[dir] = x; x_r->parent_ = y; z_r->branch_[dir] = y_r->branch_[!dir]; - *parent_r(t, y_r->branch_[!dir]) = z_p_of_x; - y_r->branch_[!dir] = z_p_of_x; + *parent_r(t, y_r->branch_[!dir]) = z; + y_r->branch_[!dir] = z; z_r->parent_ = y; } @@ -1213,10 +1208,9 @@ double_rotate(struct ccc_fromap_ *const t, size_t const z_p_of_x, 0╭─╯ x */ [[maybe_unused]] static inline bool -is_0_child(struct ccc_fromap_ const *const t, size_t const p_of_x, - size_t const x) +is_0_child(struct ccc_fromap_ const *const t, size_t const p, size_t const x) { - return p_of_x && parity(t, p_of_x) == parity(t, x); + return p && parity(t, p) == parity(t, x); } /* Returns true for rank difference 1 between the parent and node. @@ -1224,10 +1218,9 @@ is_0_child(struct ccc_fromap_ const *const t, size_t const p_of_x, 1╭─╯ x */ static inline bool -is_1_child(struct ccc_fromap_ const *const t, size_t const p_of_x, - size_t const x) +is_1_child(struct ccc_fromap_ const *const t, size_t const p, size_t const x) { - return p_of_x && parity(t, p_of_x) != parity(t, x); + return p && parity(t, p) != parity(t, x); } /* Returns true for rank difference 2 between the parent and node. @@ -1235,10 +1228,9 @@ is_1_child(struct ccc_fromap_ const *const t, size_t const p_of_x, 2╭─╯ x */ static inline bool -is_2_child(struct ccc_fromap_ const *const t, size_t const p_of_x, - size_t const x) +is_2_child(struct ccc_fromap_ const *const t, size_t const p, size_t const x) { - return p_of_x && parity(t, p_of_x) == parity(t, x); + return p && parity(t, p) == parity(t, x); } /* Returns true for rank difference 3 between the parent and node. @@ -1246,10 +1238,9 @@ is_2_child(struct ccc_fromap_ const *const t, size_t const p_of_x, 3╭─╯ x */ [[maybe_unused]] static inline bool -is_3_child(struct ccc_fromap_ const *const t, size_t const p_of_x, - size_t const x) +is_3_child(struct ccc_fromap_ const *const t, size_t const p, size_t const x) { - return p_of_x && parity(t, p_of_x) != parity(t, x); + return p && parity(t, p) != parity(t, x); } /* Returns true if a parent is a 0,1 or 1,0 node, which is not allowed. Either @@ -1258,12 +1249,12 @@ is_3_child(struct ccc_fromap_ const *const t, size_t const p_of_x, 0╭─┴─╮1 x y */ static inline bool -is_01_parent(struct ccc_fromap_ const *const t, size_t const x, - size_t const p_of_xy, size_t const y) +is_01_parent(struct ccc_fromap_ const *const t, size_t const x, size_t const p, + size_t const y) { - assert(p_of_xy); - return (!parity(t, x) && !parity(t, p_of_xy) && parity(t, y)) - || (parity(t, x) && parity(t, p_of_xy) && !parity(t, y)); + assert(p); + return (!parity(t, x) && !parity(t, p) && parity(t, y)) + || (parity(t, x) && parity(t, p) && !parity(t, y)); } /* Returns true if a parent is a 1,1 node. Either child may be the sentinel @@ -1272,12 +1263,12 @@ is_01_parent(struct ccc_fromap_ const *const t, size_t const x, 1╭─┴─╮1 x y */ static inline bool -is_11_parent(struct ccc_fromap_ const *const t, size_t const x, - size_t const p_of_xy, size_t const y) +is_11_parent(struct ccc_fromap_ const *const t, size_t const x, size_t const p, + size_t const y) { - assert(p_of_xy); - return (!parity(t, x) && parity(t, p_of_xy) && !parity(t, y)) - || (parity(t, x) && !parity(t, p_of_xy) && parity(t, y)); + assert(p); + return (!parity(t, x) && parity(t, p) && !parity(t, y)) + || (parity(t, x) && !parity(t, p) && parity(t, y)); } /* Returns true if a parent is a 0,2 or 2,0 node, which is not allowed. Either @@ -1286,12 +1277,11 @@ is_11_parent(struct ccc_fromap_ const *const t, size_t const x, 0╭─┴─╮2 x y */ static inline bool -is_02_parent(struct ccc_fromap_ const *const t, size_t const x, - size_t const p_of_xy, size_t const y) +is_02_parent(struct ccc_fromap_ const *const t, size_t const x, size_t const p, + size_t const y) { - assert(p_of_xy); - return (parity(t, x) == parity(t, p_of_xy)) - && (parity(t, p_of_xy) == parity(t, y)); + assert(p); + return (parity(t, x) == parity(t, p)) && (parity(t, p) == parity(t, y)); } /* Returns true if a parent is a 2,2 or 2,2 node, which is allowed. 2,2 nodes @@ -1303,12 +1293,11 @@ is_02_parent(struct ccc_fromap_ const *const t, size_t const x, 2╭─┴─╮2 x y */ static inline bool -is_22_parent(struct ccc_fromap_ const *const t, size_t const x, - size_t const p_of_xy, size_t const y) +is_22_parent(struct ccc_fromap_ const *const t, size_t const x, size_t const p, + size_t const y) { - assert(p_of_xy); - return (parity(t, x) == parity(t, p_of_xy)) - && (parity(t, p_of_xy) == parity(t, y)); + assert(p); + return (parity(t, x) == parity(t, p)) && (parity(t, p) == parity(t, y)); } static inline void @@ -1326,10 +1315,14 @@ demote(struct ccc_fromap_ const *const t, size_t const x) promote(t, x); } +/* Parity based ranks mean this is no-op but leave in case implementation ever + changes. Also, makes clear what sections of code are trying to do. */ static inline void double_promote(struct ccc_fromap_ const *const, size_t const) {} +/* Parity based ranks mean this is no-op but leave in case implementation ever + changes. Also, makes clear what sections of code are trying to do. */ static inline void double_demote(struct ccc_fromap_ const *const, size_t const) {} diff --git a/src/ordered_map.c b/src/ordered_map.c index ef3cda8a..d32f6f81 100644 --- a/src/ordered_map.c +++ b/src/ordered_map.c @@ -570,7 +570,7 @@ next(struct ccc_tree_ const *const t, struct ccc_node_ const *n, return n; } /* This is how to return internal nodes on the way back up from a leaf. */ - struct ccc_node_ *p = n->parent_; + struct ccc_node_ const *p = n->parent_; for (; p != &t->end_ && p->branch_[!traversal] != n; n = p, p = n->parent_) {} return p; diff --git a/src/ordered_multimap.c b/src/ordered_multimap.c index 972cfe1d..7a75af20 100644 --- a/src/ordered_multimap.c +++ b/src/ordered_multimap.c @@ -1,7 +1,7 @@ /** This file implements a splay tree that supports duplicates. -The code to support a splay tree that allows duplicates. It requires -significant modification from a traditional splay tree. This implementation is -based on the following source. +The code to support a splay tree that allows duplicates is more complicated than +a traditional splay tree. It requires significant modification. This +implementation is based on the following source. 1. Daniel Sleator, Carnegie Mellon University. Sleator's implementation of a topdown splay tree was instrumental in starting things off, but required @@ -22,18 +22,17 @@ based on the following source. #define LR 2 -/** @private - Instead of thinking about left and right consider only links - in the abstract sense. Put them in an array and then flip - this enum and left and right code paths can be united into one */ +/** @private Instead of thinking about left and right consider only links in +the abstract sense. Put them in an array and then flip this enum and left and +right code paths can be united into one */ enum tree_link_ { L = 0, R = 1 }; -/* @private Trees are just a different interpretation of the same links used - for doubly linked lists. We take advantage of this for duplicates. */ +/** @private Trees are just a different interpretation of the same links used +for doubly linked lists. We take advantage of this for duplicates. */ enum list_link_ { P = 0, @@ -160,7 +159,8 @@ ccc_omm_insert_entry(ccc_ommap_entry const *const e, } void * -ccc_omm_or_insert(ccc_ommap_entry const *e, ccc_ommap_elem *key_val_handle) +ccc_omm_or_insert(ccc_ommap_entry const *const e, + ccc_ommap_elem *const key_val_handle) { if (!e || !key_val_handle) { @@ -526,25 +526,25 @@ ccc_omm_size(ccc_ordered_multimap const *const mm) } void * -ccc_omm_unwrap(ccc_ommap_entry const *e) +ccc_omm_unwrap(ccc_ommap_entry const *const e) { return ccc_entry_unwrap(&(ccc_entry){e->impl_.entry_}); } bool -ccc_omm_insert_error(ccc_ommap_entry const *e) +ccc_omm_insert_error(ccc_ommap_entry const *const e) { return e ? e->impl_.entry_.stats_ & CCC_ENTRY_INSERT_ERROR : false; } bool -ccc_omm_input_error(ccc_ommap_entry const *e) +ccc_omm_input_error(ccc_ommap_entry const *const e) { return e ? e->impl_.entry_.stats_ & CCC_ENTRY_INPUT_ERROR : false; } bool -ccc_omm_occupied(ccc_ommap_entry const *e) +ccc_omm_occupied(ccc_ommap_entry const *const e) { return e ? e->impl_.entry_.stats_ & CCC_ENTRY_OCCUPIED : false; } @@ -598,7 +598,8 @@ ccc_impl_omm_entry(struct ccc_tree_ *const t, void const *const key) } void * -ccc_impl_omm_multimap_insert(struct ccc_tree_ *const t, struct ccc_node_ *n) +ccc_impl_omm_multimap_insert(struct ccc_tree_ *const t, + struct ccc_node_ *const n) { return multimap_insert(t, n).e_; } @@ -786,7 +787,7 @@ next(struct ccc_tree_ const *const t, struct ccc_node_ const *n, return n; } /* This is how to return internal nodes on the way back up from a leaf. */ - struct ccc_node_ *p = parent_r(t, n); + struct ccc_node_ const *p = parent_r(t, n); for (; p != &t->end_ && p->branch_[!traversal] != n; n = p, p = parent_r(t, n)) {} diff --git a/src/priority_queue.c b/src/priority_queue.c index ba354758..812fef44 100644 --- a/src/priority_queue.c +++ b/src/priority_queue.c @@ -376,7 +376,7 @@ delete_min(struct ccc_pq_ *const pq, struct ccc_pq_elem_ *root) cur = next_cur; } /* This covers the odd or even case for number of pairings. */ - root = cur != eldest ? merge(pq, accumulator, cur) : accumulator; + root = cur == eldest ? accumulator : merge(pq, accumulator, cur); /* The root is always alone in its circular list at the end of merges. */ root->next_sibling_ = root->prev_sibling_ = root; root->parent_ = NULL; diff --git a/src/realtime_ordered_map.c b/src/realtime_ordered_map.c index c46f4e5d..4de4824c 100644 --- a/src/realtime_ordered_map.c +++ b/src/realtime_ordered_map.c @@ -78,39 +78,39 @@ static void *maybe_alloc_insert(struct ccc_romap_ *, ccc_threeway_cmp last_cmp, struct ccc_romap_elem_ *); static void *remove_fixup(struct ccc_romap_ *, struct ccc_romap_elem_ *); -static void insert_fixup(struct ccc_romap_ *, struct ccc_romap_elem_ *z_p_of_x, +static void insert_fixup(struct ccc_romap_ *, struct ccc_romap_elem_ *z, struct ccc_romap_elem_ *x); static void transplant(struct ccc_romap_ *, struct ccc_romap_elem_ *remove, struct ccc_romap_elem_ *replacement); static void rebalance_3_child(struct ccc_romap_ *rom, struct ccc_romap_elem_ *z, struct ccc_romap_elem_ *x); static bool is_0_child(struct ccc_romap_ const *, - struct ccc_romap_elem_ const *p_of_x, + struct ccc_romap_elem_ const *p, struct ccc_romap_elem_ const *x); static bool is_1_child(struct ccc_romap_ const *, - struct ccc_romap_elem_ const *p_of_x, + struct ccc_romap_elem_ const *p, struct ccc_romap_elem_ const *x); static bool is_2_child(struct ccc_romap_ const *, - struct ccc_romap_elem_ const *p_of_x, + struct ccc_romap_elem_ const *p, struct ccc_romap_elem_ const *x); static bool is_3_child(struct ccc_romap_ const *, - struct ccc_romap_elem_ const *p_of_x, + struct ccc_romap_elem_ const *p, struct ccc_romap_elem_ const *x); static bool is_01_parent(struct ccc_romap_ const *, struct ccc_romap_elem_ const *x, - struct ccc_romap_elem_ const *p_of_xy, + struct ccc_romap_elem_ const *p, struct ccc_romap_elem_ const *y); static bool is_11_parent(struct ccc_romap_ const *, struct ccc_romap_elem_ const *x, - struct ccc_romap_elem_ const *p_of_xy, + struct ccc_romap_elem_ const *p, struct ccc_romap_elem_ const *y); static bool is_02_parent(struct ccc_romap_ const *, struct ccc_romap_elem_ const *x, - struct ccc_romap_elem_ const *p_of_xy, + struct ccc_romap_elem_ const *p, struct ccc_romap_elem_ const *y); static bool is_22_parent(struct ccc_romap_ const *, struct ccc_romap_elem_ const *x, - struct ccc_romap_elem_ const *p_of_xy, + struct ccc_romap_elem_ const *p, struct ccc_romap_elem_ const *y); static bool is_leaf(struct ccc_romap_ const *rom, struct ccc_romap_elem_ const *x); @@ -123,13 +123,12 @@ static void double_promote(struct ccc_romap_ const *rom, static void double_demote(struct ccc_romap_ const *rom, struct ccc_romap_elem_ *x); -static void rotate(struct ccc_romap_ *rom, struct ccc_romap_elem_ *z_p_of_x, - struct ccc_romap_elem_ *x_p_of_y, struct ccc_romap_elem_ *y, +static void rotate(struct ccc_romap_ *rom, struct ccc_romap_elem_ *z, + struct ccc_romap_elem_ *x, struct ccc_romap_elem_ *y, enum romap_link_ dir); -static void double_rotate(struct ccc_romap_ *rom, - struct ccc_romap_elem_ *z_p_of_x, - struct ccc_romap_elem_ *x_p_of_y, - struct ccc_romap_elem_ *y, enum romap_link_ dir); +static void double_rotate(struct ccc_romap_ *rom, struct ccc_romap_elem_ *z, + struct ccc_romap_elem_ *x, struct ccc_romap_elem_ *y, + enum romap_link_ dir); static bool validate(struct ccc_romap_ const *rom); static struct ccc_romap_elem_ *next(struct ccc_romap_ const *, struct ccc_romap_elem_ const *, @@ -171,7 +170,7 @@ ccc_rom_get_key_val(ccc_realtime_ordered_map const *const rom, { return NULL; } - struct romap_query_ q = find(rom, key); + struct romap_query_ const q = find(rom, key); return (CCC_EQL == q.last_cmp_) ? struct_base(rom, q.found_) : NULL; } @@ -183,7 +182,7 @@ ccc_rom_insert(ccc_realtime_ordered_map *const rom, { return (ccc_entry){{.stats_ = CCC_ENTRY_INPUT_ERROR}}; } - struct romap_query_ q = find(rom, key_from_node(rom, key_val_handle)); + struct romap_query_ const q = find(rom, key_from_node(rom, key_val_handle)); if (CCC_EQL == q.last_cmp_) { *key_val_handle = *q.found_; @@ -196,9 +195,7 @@ ccc_rom_insert(ccc_realtime_ordered_map *const rom, tmp->branch_[L] = tmp->branch_[R] = tmp->parent_ = NULL; return (ccc_entry){{.e_ = old_val, .stats_ = CCC_ENTRY_OCCUPIED}}; } - void *inserted - = maybe_alloc_insert(rom, q.parent_, q.last_cmp_, key_val_handle); - if (!inserted) + if (!maybe_alloc_insert(rom, q.parent_, q.last_cmp_, key_val_handle)) { return (ccc_entry){{.e_ = NULL, .stats_ = CCC_ENTRY_INSERT_ERROR}}; } @@ -213,13 +210,13 @@ ccc_rom_try_insert(ccc_realtime_ordered_map *const rom, { return (ccc_entry){{.stats_ = CCC_ENTRY_INPUT_ERROR}}; } - struct romap_query_ q = find(rom, key_from_node(rom, key_val_handle)); + struct romap_query_ const q = find(rom, key_from_node(rom, key_val_handle)); if (CCC_EQL == q.last_cmp_) { return (ccc_entry){ {.e_ = struct_base(rom, q.found_), .stats_ = CCC_ENTRY_OCCUPIED}}; } - void *inserted + void *const inserted = maybe_alloc_insert(rom, q.parent_, q.last_cmp_, key_val_handle); if (!inserted) { @@ -236,7 +233,7 @@ ccc_rom_insert_or_assign(ccc_realtime_ordered_map *const rom, { return (ccc_entry){{.stats_ = CCC_ENTRY_INPUT_ERROR}}; } - struct romap_query_ q = find(rom, key_from_node(rom, key_val_handle)); + struct romap_query_ const q = find(rom, key_from_node(rom, key_val_handle)); if (CCC_EQL == q.last_cmp_) { void *const found = struct_base(rom, q.found_); @@ -244,7 +241,7 @@ ccc_rom_insert_or_assign(ccc_realtime_ordered_map *const rom, memcpy(found, struct_base(rom, key_val_handle), rom->elem_sz_); return (ccc_entry){{.e_ = found, .stats_ = CCC_ENTRY_OCCUPIED}}; } - void *inserted + void *const inserted = maybe_alloc_insert(rom, q.parent_, q.last_cmp_, key_val_handle); if (!inserted) { @@ -407,7 +404,7 @@ ccc_rom_begin(ccc_realtime_ordered_map const *rom) { return NULL; } - struct ccc_romap_elem_ *m = min_max_from(rom, rom->root_, min); + struct ccc_romap_elem_ *const m = min_max_from(rom, rom->root_, min); return m == &rom->end_ ? NULL : struct_base(rom, m); } @@ -434,7 +431,7 @@ ccc_rom_rbegin(ccc_realtime_ordered_map const *const rom) { return NULL; } - struct ccc_romap_elem_ *m = min_max_from(rom, rom->root_, max); + struct ccc_romap_elem_ *const m = min_max_from(rom, rom->root_, max); return m == &rom->end_ ? NULL : struct_base(rom, m); } @@ -587,7 +584,7 @@ min_max_from(struct ccc_romap_ const *const rom, struct ccc_romap_elem_ *start, static inline struct ccc_rtree_entry_ entry(struct ccc_romap_ const *const rom, void const *const key) { - struct romap_query_ q = find(rom, key); + struct romap_query_ const q = find(rom, key); if (CCC_EQL == q.last_cmp_) { return (struct ccc_rtree_entry_){ @@ -637,12 +634,12 @@ insert(struct ccc_romap_ *const rom, struct ccc_romap_elem_ *const parent, static inline void * maybe_alloc_insert(struct ccc_romap_ *const rom, struct ccc_romap_elem_ *const parent, - ccc_threeway_cmp last_cmp, + ccc_threeway_cmp const last_cmp, struct ccc_romap_elem_ *out_handle) { if (rom->alloc_) { - void *new = rom->alloc_(NULL, rom->elem_sz_, rom->aux_); + void *const new = rom->alloc_(NULL, rom->elem_sz_, rom->aux_); if (!new) { return NULL; @@ -783,40 +780,40 @@ elem_in_slot(struct ccc_romap_ const *const rom, void const *const slot) /*======================= WAVL Tree Maintenance =========================*/ static void -insert_fixup(struct ccc_romap_ *const rom, struct ccc_romap_elem_ *z_p_of_xy, +insert_fixup(struct ccc_romap_ *const rom, struct ccc_romap_elem_ *z, struct ccc_romap_elem_ *x) { do { - promote(rom, z_p_of_xy); - x = z_p_of_xy; - z_p_of_xy = z_p_of_xy->parent_; - if (z_p_of_xy == &rom->end_) + promote(rom, z); + x = z; + z = z->parent_; + if (z == &rom->end_) { return; } - } while (is_01_parent(rom, x, z_p_of_xy, sibling_of(rom, x))); + } while (is_01_parent(rom, x, z, sibling_of(rom, x))); - if (!is_02_parent(rom, x, z_p_of_xy, sibling_of(rom, x))) + if (!is_02_parent(rom, x, z, sibling_of(rom, x))) { return; } assert(x != &rom->end_); - assert(is_0_child(rom, z_p_of_xy, x)); - enum romap_link_ const p_to_x_dir = z_p_of_xy->branch_[R] == x; - struct ccc_romap_elem_ *y = x->branch_[!p_to_x_dir]; - if (y == &rom->end_ || is_2_child(rom, z_p_of_xy, y)) + assert(is_0_child(rom, z, x)); + enum romap_link_ const p_to_x_dir = z->branch_[R] == x; + struct ccc_romap_elem_ *const y = x->branch_[!p_to_x_dir]; + if (y == &rom->end_ || is_2_child(rom, z, y)) { - rotate(rom, z_p_of_xy, x, y, !p_to_x_dir); - demote(rom, z_p_of_xy); + rotate(rom, z, x, y, !p_to_x_dir); + demote(rom, z); } else { - assert(is_1_child(rom, z_p_of_xy, y)); - double_rotate(rom, z_p_of_xy, x, y, p_to_x_dir); + assert(is_1_child(rom, z, y)); + double_rotate(rom, z, x, y, p_to_x_dir); promote(rom, y); demote(rom, x); - demote(rom, z_p_of_xy); + demote(rom, z); } } @@ -887,39 +884,38 @@ remove_fixup(struct ccc_romap_ *const rom, struct ccc_romap_elem_ *const remove) } static inline void -rebalance_3_child(struct ccc_romap_ *const rom, - struct ccc_romap_elem_ *z_p_of_xy, struct ccc_romap_elem_ *x) +rebalance_3_child(struct ccc_romap_ *const rom, struct ccc_romap_elem_ *z, + struct ccc_romap_elem_ *x) { - assert(z_p_of_xy != &rom->end_); + assert(z != &rom->end_); bool made_3_child = false; do { - struct ccc_romap_elem_ *const p_of_p_of_xy = z_p_of_xy->parent_; - struct ccc_romap_elem_ *y - = z_p_of_xy->branch_[z_p_of_xy->branch_[L] == x]; - made_3_child = is_2_child(rom, p_of_p_of_xy, z_p_of_xy); - if (is_2_child(rom, z_p_of_xy, y)) + struct ccc_romap_elem_ *const g = z->parent_; + struct ccc_romap_elem_ *const y = z->branch_[z->branch_[L] == x]; + made_3_child = is_2_child(rom, g, z); + if (is_2_child(rom, z, y)) { - demote(rom, z_p_of_xy); + demote(rom, z); } else if (is_22_parent(rom, y->branch_[L], y, y->branch_[R])) { - demote(rom, z_p_of_xy); + demote(rom, z); demote(rom, y); } else /* p(x) is 1,3, y is not a 2,2 parent, and x is 3-child.*/ { - assert(is_3_child(rom, z_p_of_xy, x)); - enum romap_link_ const z_to_x_dir = z_p_of_xy->branch_[R] == x; + assert(is_3_child(rom, z, x)); + enum romap_link_ const z_to_x_dir = z->branch_[R] == x; struct ccc_romap_elem_ *const w = y->branch_[!z_to_x_dir]; if (is_1_child(rom, y, w)) { - rotate(rom, z_p_of_xy, y, y->branch_[z_to_x_dir], z_to_x_dir); + rotate(rom, z, y, y->branch_[z_to_x_dir], z_to_x_dir); promote(rom, y); - demote(rom, z_p_of_xy); - if (is_leaf(rom, z_p_of_xy)) + demote(rom, z); + if (is_leaf(rom, z)) { - demote(rom, z_p_of_xy); + demote(rom, z); } } else /* w is a 2-child and v will be a 1-child. */ @@ -927,10 +923,10 @@ rebalance_3_child(struct ccc_romap_ *const rom, struct ccc_romap_elem_ *const v = y->branch_[z_to_x_dir]; assert(is_2_child(rom, y, w)); assert(is_1_child(rom, y, v)); - double_rotate(rom, z_p_of_xy, y, v, !z_to_x_dir); + double_rotate(rom, z, y, v, !z_to_x_dir); double_promote(rom, v); demote(rom, y); - double_demote(rom, z_p_of_xy); + double_demote(rom, z); /* Optional "Rebalancing with Promotion," defined as follows: if node z is a non-leaf 1,1 node, we promote it; otherwise, if y is a non-leaf 1,1 node, we promote it. @@ -938,11 +934,10 @@ rebalance_3_child(struct ccc_romap_ *const rom, This reduces constants in some of theorems mentioned in the paper but may not be worth doing. Rotations stay at 2 worst case. Should revisit after more performance testing. */ - if (!is_leaf(rom, z_p_of_xy) - && is_11_parent(rom, z_p_of_xy->branch_[L], z_p_of_xy, - z_p_of_xy->branch_[R])) + if (!is_leaf(rom, z) + && is_11_parent(rom, z->branch_[L], z, z->branch_[R])) { - promote(rom, z_p_of_xy); + promote(rom, z); } else if (!is_leaf(rom, y) && is_11_parent(rom, y->branch_[L], y, y->branch_[R])) @@ -952,9 +947,9 @@ rebalance_3_child(struct ccc_romap_ *const rom, } return; } - x = z_p_of_xy; - z_p_of_xy = p_of_p_of_xy; - } while (z_p_of_xy != &rom->end_ && made_3_child); + x = z; + z = g; + } while (z != &rom->end_ && made_3_child); } static inline void @@ -990,25 +985,25 @@ and uppercase are arbitrary subtrees. │ │ B B*/ static inline void -rotate(struct ccc_romap_ *const rom, struct ccc_romap_elem_ *const z_p_of_x, - struct ccc_romap_elem_ *const x_p_of_y, struct ccc_romap_elem_ *const y, +rotate(struct ccc_romap_ *const rom, struct ccc_romap_elem_ *const z, + struct ccc_romap_elem_ *const x, struct ccc_romap_elem_ *const y, enum romap_link_ dir) { - assert(z_p_of_x != &rom->end_); - struct ccc_romap_elem_ *const p_of_p_of_x = z_p_of_x->parent_; - x_p_of_y->parent_ = p_of_p_of_x; - if (p_of_p_of_x == &rom->end_) + assert(z != &rom->end_); + struct ccc_romap_elem_ *const g = z->parent_; + x->parent_ = g; + if (g == &rom->end_) { - rom->root_ = x_p_of_y; + rom->root_ = x; } else { - p_of_p_of_x->branch_[p_of_p_of_x->branch_[R] == z_p_of_x] = x_p_of_y; + g->branch_[g->branch_[R] == z] = x; } - x_p_of_y->branch_[dir] = z_p_of_x; - z_p_of_x->parent_ = x_p_of_y; - z_p_of_x->branch_[!dir] = y; - y->parent_ = z_p_of_x; + x->branch_[dir] = z; + z->parent_ = x; + z->branch_[!dir] = y; + y->parent_ = z; } /** A double rotation shouldn't actually be two calls to rotate because that @@ -1023,33 +1018,32 @@ Lowercase are nodes and uppercase are arbitrary subtrees. ╭─┴─╮ B C */ static inline void -double_rotate(struct ccc_romap_ *const rom, - struct ccc_romap_elem_ *const z_p_of_x, - struct ccc_romap_elem_ *const x_p_of_y, - struct ccc_romap_elem_ *const y, enum romap_link_ dir) +double_rotate(struct ccc_romap_ *const rom, struct ccc_romap_elem_ *const z, + struct ccc_romap_elem_ *const x, struct ccc_romap_elem_ *const y, + enum romap_link_ dir) { - assert(z_p_of_x != &rom->end_); - assert(x_p_of_y != &rom->end_); + assert(z != &rom->end_); + assert(x != &rom->end_); assert(y != &rom->end_); - struct ccc_romap_elem_ *const p_of_p_of_x = z_p_of_x->parent_; - y->parent_ = p_of_p_of_x; - if (p_of_p_of_x == &rom->end_) + struct ccc_romap_elem_ *const g = z->parent_; + y->parent_ = g; + if (g == &rom->end_) { rom->root_ = y; } else { - p_of_p_of_x->branch_[p_of_p_of_x->branch_[R] == z_p_of_x] = y; + g->branch_[g->branch_[R] == z] = y; } - x_p_of_y->branch_[!dir] = y->branch_[dir]; - y->branch_[dir]->parent_ = x_p_of_y; - y->branch_[dir] = x_p_of_y; - x_p_of_y->parent_ = y; + x->branch_[!dir] = y->branch_[dir]; + y->branch_[dir]->parent_ = x; + y->branch_[dir] = x; + x->parent_ = y; - z_p_of_x->branch_[dir] = y->branch_[!dir]; - y->branch_[!dir]->parent_ = z_p_of_x; - y->branch_[!dir] = z_p_of_x; - z_p_of_x->parent_ = y; + z->branch_[dir] = y->branch_[!dir]; + y->branch_[!dir]->parent_ = z; + y->branch_[!dir] = z; + z->parent_ = y; } /* Returns true for rank difference 0 (rule break) between the parent and node. @@ -1058,10 +1052,10 @@ double_rotate(struct ccc_romap_ *const rom, x */ [[maybe_unused]] static inline bool is_0_child(struct ccc_romap_ const *const rom, - struct ccc_romap_elem_ const *p_of_x, - struct ccc_romap_elem_ const *x) + struct ccc_romap_elem_ const *const p, + struct ccc_romap_elem_ const *const x) { - return p_of_x != &rom->end_ && p_of_x->parity_ == x->parity_; + return p != &rom->end_ && p->parity_ == x->parity_; } /* Returns true for rank difference 1 between the parent and node. @@ -1070,10 +1064,10 @@ is_0_child(struct ccc_romap_ const *const rom, x*/ static inline bool is_1_child(struct ccc_romap_ const *const rom, - struct ccc_romap_elem_ const *p_of_x, - struct ccc_romap_elem_ const *x) + struct ccc_romap_elem_ const *const p, + struct ccc_romap_elem_ const *const x) { - return p_of_x != &rom->end_ && p_of_x->parity_ != x->parity_; + return p != &rom->end_ && p->parity_ != x->parity_; } /* Returns true for rank difference 2 between the parent and node. @@ -1082,10 +1076,10 @@ is_1_child(struct ccc_romap_ const *const rom, x */ static inline bool is_2_child(struct ccc_romap_ const *const rom, - struct ccc_romap_elem_ const *const p_of_x, + struct ccc_romap_elem_ const *const p, struct ccc_romap_elem_ const *const x) { - return p_of_x != &rom->end_ && p_of_x->parity_ == x->parity_; + return p != &rom->end_ && p->parity_ == x->parity_; } /* Returns true for rank difference 3 between the parent and node. @@ -1094,10 +1088,10 @@ is_2_child(struct ccc_romap_ const *const rom, x */ [[maybe_unused]] static inline bool is_3_child(struct ccc_romap_ const *const rom, - struct ccc_romap_elem_ const *const p_of_x, + struct ccc_romap_elem_ const *const p, struct ccc_romap_elem_ const *const x) { - return p_of_x != &rom->end_ && p_of_x->parity_ != x->parity_; + return p != &rom->end_ && p->parity_ != x->parity_; } /* Returns true if a parent is a 0,1 or 1,0 node, which is not allowed. Either @@ -1108,12 +1102,12 @@ is_3_child(struct ccc_romap_ const *const rom, static inline bool is_01_parent([[maybe_unused]] struct ccc_romap_ const *const rom, struct ccc_romap_elem_ const *const x, - struct ccc_romap_elem_ const *const p_of_xy, + struct ccc_romap_elem_ const *const p, struct ccc_romap_elem_ const *const y) { - assert(p_of_xy != &rom->end_); - return (!x->parity_ && !p_of_xy->parity_ && y->parity_) - || (x->parity_ && p_of_xy->parity_ && !y->parity_); + assert(p != &rom->end_); + return (!x->parity_ && !p->parity_ && y->parity_) + || (x->parity_ && p->parity_ && !y->parity_); } /* Returns true if a parent is a 1,1 node. Either child may be the sentinel @@ -1124,12 +1118,12 @@ is_01_parent([[maybe_unused]] struct ccc_romap_ const *const rom, static inline bool is_11_parent([[maybe_unused]] struct ccc_romap_ const *const rom, struct ccc_romap_elem_ const *const x, - struct ccc_romap_elem_ const *const p_of_xy, + struct ccc_romap_elem_ const *const p, struct ccc_romap_elem_ const *const y) { - assert(p_of_xy != &rom->end_); - return (!x->parity_ && p_of_xy->parity_ && !y->parity_) - || (x->parity_ && !p_of_xy->parity_ && y->parity_); + assert(p != &rom->end_); + return (!x->parity_ && p->parity_ && !y->parity_) + || (x->parity_ && !p->parity_ && y->parity_); } /* Returns true if a parent is a 0,2 or 2,0 node, which is not allowed. Either @@ -1140,11 +1134,11 @@ is_11_parent([[maybe_unused]] struct ccc_romap_ const *const rom, static inline bool is_02_parent([[maybe_unused]] struct ccc_romap_ const *const rom, struct ccc_romap_elem_ const *const x, - struct ccc_romap_elem_ const *const p_of_xy, + struct ccc_romap_elem_ const *const p, struct ccc_romap_elem_ const *const y) { - assert(p_of_xy != &rom->end_); - return (x->parity_ == p_of_xy->parity_) && (p_of_xy->parity_ == y->parity_); + assert(p != &rom->end_); + return (x->parity_ == p->parity_) && (p->parity_ == y->parity_); } /* Returns true if a parent is a 2,2 or 2,2 node, which is allowed. 2,2 nodes @@ -1158,11 +1152,11 @@ is_02_parent([[maybe_unused]] struct ccc_romap_ const *const rom, static inline bool is_22_parent([[maybe_unused]] struct ccc_romap_ const *const rom, struct ccc_romap_elem_ const *const x, - struct ccc_romap_elem_ const *const p_of_xy, + struct ccc_romap_elem_ const *const p, struct ccc_romap_elem_ const *const y) { - assert(p_of_xy != &rom->end_); - return (x->parity_ == p_of_xy->parity_) && (p_of_xy->parity_ == y->parity_); + assert(p != &rom->end_); + return (x->parity_ == p->parity_) && (p->parity_ == y->parity_); } static inline void @@ -1180,10 +1174,16 @@ demote(struct ccc_romap_ const *const rom, struct ccc_romap_elem_ *const x) promote(rom, x); } +/* One could imagine non-parity based rank tracking making this function + meaningful, but two parity changes are the same as a no-op. Leave for + clarity of what the code is meant to do through certain sections. */ static inline void double_promote(struct ccc_romap_ const *const, struct ccc_romap_elem_ *const) {} +/* One could imagine non-parity based rank tracking making this function + meaningful, but two parity changes are the same as a no-op. Leave for + clarity of what the code is meant to do through certain sections. */ static inline void double_demote(struct ccc_romap_ const *const, struct ccc_romap_elem_ *const) {} diff --git a/src/singly_linked_list.c b/src/singly_linked_list.c index 9fca48df..3729af3b 100644 --- a/src/singly_linked_list.c +++ b/src/singly_linked_list.c @@ -291,7 +291,8 @@ ccc_sll_is_empty(ccc_singly_linked_list const *const sll) /*========================= Private Interface ==========================*/ void -ccc_impl_sll_push_front(struct ccc_sll_ *const sll, struct ccc_sll_elem_ *elem) +ccc_impl_sll_push_front(struct ccc_sll_ *const sll, + struct ccc_sll_elem_ *const elem) { push_front(sll, elem); } diff --git a/tests/fhmap/test_fhmap_insert.c b/tests/fhmap/test_fhmap_insert.c index c6784e14..9d0f095d 100644 --- a/tests/fhmap/test_fhmap_insert.c +++ b/tests/fhmap/test_fhmap_insert.c @@ -556,15 +556,50 @@ CHECK_BEGIN_STATIC_FN(fhmap_test_insert_limit) CHECK_END_FN(); } +CHECK_BEGIN_STATIC_FN(fhmap_test_insert_and_find) +{ + int const size = 101; + ccc_flat_hash_map fh = fhm_init((struct val[101]){}, 101, key, e, NULL, + fhmap_int_to_u64, fhmap_id_eq, NULL); + + for (int i = 0; i < size; i += 2) + { + ccc_entry e = try_insert(&fh, &(struct val){.key = i, .val = i}.e); + CHECK(occupied(&e), false); + CHECK(validate(&fh), true); + e = try_insert(&fh, &(struct val){.key = i, .val = i}.e); + CHECK(occupied(&e), true); + CHECK(validate(&fh), true); + struct val const *const v = unwrap(&e); + CHECK(v == NULL, false); + CHECK(v->key, i); + CHECK(v->val, i); + } + for (int i = 0; i < size; i += 2) + { + CHECK(contains(&fh, &i), true); + CHECK(occupied(entry_r(&fh, &i)), true); + CHECK(validate(&fh), true); + } + for (int i = 1; i < size; i += 2) + { + CHECK(contains(&fh, &i), false); + CHECK(occupied(entry_r(&fh, &i)), false); + CHECK(validate(&fh), true); + } + CHECK_END_FN(); +} + int main() { return CHECK_RUN( fhmap_test_insert(), fhmap_test_insert_macros(), - fhmap_test_insert_overwrite(), fhmap_test_insert_then_bad_ideas(), - fhmap_test_insert_via_entry(), fhmap_test_insert_via_entry_macros(), - fhmap_test_entry_api_functional(), fhmap_test_entry_api_macros(), - fhmap_test_two_sum(), fhmap_test_resize(), fhmap_test_resize_macros(), + fhmap_test_insert_and_find(), fhmap_test_insert_overwrite(), + fhmap_test_insert_then_bad_ideas(), fhmap_test_insert_via_entry(), + fhmap_test_insert_via_entry_macros(), fhmap_test_entry_api_functional(), + fhmap_test_entry_api_macros(), fhmap_test_two_sum(), + fhmap_test_resize(), fhmap_test_resize_macros(), fhmap_test_resize_from_null(), fhmap_test_resize_from_null_macros(), fhmap_test_insert_limit()); } diff --git a/tests/fomap/test_fomap_insert.c b/tests/fomap/test_fomap_insert.c index f1608220..a8309bd0 100644 --- a/tests/fomap/test_fomap_insert.c +++ b/tests/fomap/test_fomap_insert.c @@ -14,53 +14,601 @@ #include #include -CHECK_BEGIN_STATIC_FN(fomap_test_insert_one) +static inline struct val +fomap_create(int const id, int const val) { - flat_ordered_map s - = fom_init((struct val[2]){}, 2, elem, id, NULL, id_cmp, NULL); - CHECK(occupied(insert_r(&s, &(struct val){}.elem)), false); - CHECK(is_empty(&s), false); + return (struct val){.id = id, .val = val}; +} + +static inline void +fomap_modplus(ccc_user_type const t) +{ + ((struct val *)t.user_type)->val++; +} + +CHECK_BEGIN_STATIC_FN(fomap_test_insert) +{ + ccc_flat_ordered_map fom + = fom_init((struct val[10]){}, 10, elem, id, NULL, id_cmp, NULL); + + /* Nothing was there before so nothing is in the entry. */ + ccc_entry ent = insert(&fom, &(struct val){.id = 137, .val = 99}.elem); + CHECK(occupied(&ent), false); + CHECK(unwrap(&ent), NULL); + CHECK(size(&fom), 1); CHECK_END_FN(); } CHECK_BEGIN_STATIC_FN(fomap_test_insert_macros) { - /* This is also a good test to see if the buffer can manage its own memory - when provided with a std_alloc function starting from NULL. */ - flat_ordered_map s - = fom_init((struct val *)NULL, 0, elem, id, std_alloc, id_cmp, NULL); - struct val *v = fom_or_insert_w(entry_r(&s, &(int){0}), (struct val){}); + ccc_flat_ordered_map fom + = fom_init((struct val[10]){}, 10, elem, id, NULL, id_cmp, NULL); + + struct val const *ins = ccc_fom_or_insert_w( + entry_r(&fom, &(int){2}), (struct val){.id = 2, .val = 0}); + CHECK(ins != NULL, true); + CHECK(validate(&fom), true); + CHECK(size(&fom), 1); + ins = fom_insert_entry_w(entry_r(&fom, &(int){2}), + (struct val){.id = 2, .val = 0}); + CHECK(validate(&fom), true); + CHECK(ins != NULL, true); + ins = fom_insert_entry_w(entry_r(&fom, &(int){9}), + (struct val){.id = 9, .val = 1}); + CHECK(validate(&fom), true); + CHECK(ins != NULL, true); + ins = ccc_entry_unwrap( + fom_insert_or_assign_w(&fom, 3, (struct val){.val = 99})); + CHECK(validate(&fom), true); + CHECK(ins == NULL, false); + CHECK(validate(&fom), true); + CHECK(ins->val, 99); + CHECK(size(&fom), 3); + ins = ccc_entry_unwrap( + fom_insert_or_assign_w(&fom, 3, (struct val){.val = 98})); + CHECK(validate(&fom), true); + CHECK(ins == NULL, false); + CHECK(ins->val, 98); + CHECK(size(&fom), 3); + ins = ccc_entry_unwrap(fom_try_insert_w(&fom, 3, (struct val){.val = 100})); + CHECK(ins == NULL, false); + CHECK(validate(&fom), true); + CHECK(ins->val, 98); + CHECK(size(&fom), 3); + ins = ccc_entry_unwrap(fom_try_insert_w(&fom, 4, (struct val){.val = 100})); + CHECK(ins == NULL, false); + CHECK(validate(&fom), true); + CHECK(ins->val, 100); + CHECK(size(&fom), 4); + CHECK_END_FN(ccc_fom_clear_and_free(&fom, NULL);); +} + +CHECK_BEGIN_STATIC_FN(fomap_test_insert_overwrite) +{ + ccc_flat_ordered_map fom + = fom_init((struct val[10]){}, 10, elem, id, NULL, id_cmp, NULL); + + struct val q = {.id = 137, .val = 99}; + ccc_entry ent = insert(&fom, &q.elem); + CHECK(occupied(&ent), false); + CHECK(unwrap(&ent), NULL); + + struct val const *v = unwrap(entry_r(&fom, &q.id)); CHECK(v != NULL, true); - v = fom_insert_entry_w(entry_r(&s, &(int){0}), - (struct val){.id = 0, .val = 99}); - CHECK(validate(&s), true); - CHECK(v == NULL, false); CHECK(v->val, 99); - v = fom_insert_entry_w(entry_r(&s, &(int){9}), - (struct val){.id = 9, .val = 100}); + + /* Now the second insertion will take place and the old occupying value + will be written into our struct we used to make the query. */ + q = (struct val){.id = 137, .val = 100}; + + /* The contents of q are now in the table. */ + ccc_entry old_ent = insert(&fom, &q.elem); + CHECK(occupied(&old_ent), true); + + /* The old contents are now in q and the entry is in the table. */ + v = unwrap(&old_ent); CHECK(v != NULL, true); - CHECK(v->val, 100); - v = unwrap(fom_insert_or_assign_w(&s, 1, (struct val){.val = 100})); - CHECK(validate(&s), true); + CHECK(v->val, 99); + CHECK(q.val, 99); + v = unwrap(entry_r(&fom, &q.id)); CHECK(v != NULL, true); CHECK(v->val, 100); - CHECK(size(&s), 3); - v = unwrap(fom_insert_or_assign_w(&s, 1, (struct val){.val = 99})); - CHECK(validate(&s), true); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(fomap_test_insert_then_bad_ideas) +{ + ccc_flat_ordered_map fom + = fom_init((struct val[10]){}, 10, elem, id, NULL, id_cmp, NULL); + struct val q = {.id = 137, .val = 99}; + ccc_entry ent = insert(&fom, &q.elem); + CHECK(occupied(&ent), false); + CHECK(unwrap(&ent), NULL); + struct val const *v = unwrap(entry_r(&fom, &q.id)); CHECK(v != NULL, true); CHECK(v->val, 99); - CHECK(size(&s), 3); - v = unwrap(fom_try_insert_w(&s, 1, (struct val){.val = 2})); - CHECK(validate(&s), true); + + q = (struct val){.id = 137, .val = 100}; + + ent = insert(&fom, &q.elem); + CHECK(occupied(&ent), true); + v = unwrap(&ent); CHECK(v != NULL, true); CHECK(v->val, 99); - CHECK(size(&s), 3); - v = unwrap(fom_try_insert_w(&s, 2, (struct val){.val = 2})); - CHECK(validate(&s), true); + CHECK(q.val, 99); + q.val -= 9; + + v = get_key_val(&fom, &q.id); CHECK(v != NULL, true); - CHECK(v->val, 2); - CHECK(size(&s), 4); - CHECK_END_FN(fom_clear_and_free(&s, NULL);); + CHECK(v->val, 100); + CHECK(q.val, 90); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(fomap_test_entry_api_functional) +{ + /* Over allocate size now because we don't want to worry about resizing. */ + ccc_flat_ordered_map fom + = fom_init((struct val[200]){}, 200, elem, id, NULL, id_cmp, NULL); + size_t const size = 200; + + /* Test entry or insert with for all even values. Default should be + inserted. All entries are hashed to last digit so many spread out + collisions. */ + struct val def = {0}; + for (size_t i = 0; i < size / 2; i += 2) + { + def.id = (int)i; + def.val = (int)i; + struct val const *const d + = or_insert(entry_r(&fom, &def.id), &def.elem); + CHECK((d != NULL), true); + CHECK(d->id, i); + CHECK(d->val, i); + } + CHECK(size(&fom), (size / 2) / 2); + /* The default insertion should not occur every other element. */ + for (size_t i = 0; i < size / 2; ++i) + { + def.id = (int)i; + def.val = (int)i; + struct val const *const d + = or_insert(fom_and_modify_w(entry_r(&fom, &def.id), struct val, + { + T->val++; + }), + &def.elem); + /* All values in the array should be odd now */ + CHECK((d != NULL), true); + CHECK(d->id, i); + if (i % 2) + { + CHECK(d->val, i); + } + else + { + CHECK(d->val, i + 1); + } + CHECK(d->val % 2, true); + } + CHECK(size(&fom), (size / 2)); + /* More simply modifications don't require the and modify function. All + should be switched back to even now. */ + for (size_t i = 0; i < size / 2; ++i) + { + def.id = (int)i; + def.val = (int)i; + struct val *const in = or_insert(entry_r(&fom, &def.id), &def.elem); + in->val++; + /* All values in the array should be odd now */ + CHECK((in->val % 2 == 0), true); + } + CHECK(size(&fom), (size / 2)); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(fomap_test_insert_via_entry) +{ + /* Over allocate size now because we don't want to worry about resizing. */ + size_t const size = 200; + ccc_flat_ordered_map fom + = fom_init((struct val[200]){}, 200, elem, id, NULL, id_cmp, NULL); + + /* Test entry or insert with for all even values. Default should be + inserted. All entries are hashed to last digit so many spread out + collisions. */ + struct val def = {0}; + for (size_t i = 0; i < size / 2; i += 2) + { + def.id = (int)i; + def.val = (int)i; + struct val const *const d + = insert_entry(entry_r(&fom, &def.id), &def.elem); + CHECK((d != NULL), true); + CHECK(d->id, i); + CHECK(d->val, i); + } + CHECK(size(&fom), (size / 2) / 2); + /* The default insertion should not occur every other element. */ + for (size_t i = 0; i < size / 2; ++i) + { + def.id = (int)i; + def.val = (int)i + 1; + struct val const *const d + = insert_entry(entry_r(&fom, &def.id), &def.elem); + /* All values in the array should be odd now */ + CHECK((d != NULL), true); + CHECK(d->val, i + 1); + if (i % 2) + { + CHECK(d->val % 2 == 0, true); + } + else + { + CHECK(d->val % 2, true); + } + } + CHECK(size(&fom), (size / 2)); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(fomap_test_insert_via_entry_macros) +{ + /* Over allocate size now because we don't want to worry about resizing. */ + size_t const size = 200; + ccc_flat_ordered_map fom + = fom_init((struct val[200]){}, 200, elem, id, NULL, id_cmp, NULL); + + /* Test entry or insert with for all even values. Default should be + inserted. All entries are hashed to last digit so many spread out + collisions. */ + for (size_t i = 0; i < size / 2; i += 2) + { + struct val const *const d + = insert_entry(entry_r(&fom, &i), &(struct val){i, i, {}}.elem); + CHECK((d != NULL), true); + CHECK(d->id, i); + CHECK(d->val, i); + } + CHECK(size(&fom), (size / 2) / 2); + /* The default insertion should not occur every other element. */ + for (size_t i = 0; i < size / 2; ++i) + { + struct val const *const d + = insert_entry(entry_r(&fom, &i), &(struct val){i, i + 1, {}}.elem); + /* All values in the array should be odd now */ + CHECK((d != NULL), true); + CHECK(d->val, i + 1); + if (i % 2) + { + CHECK(d->val % 2 == 0, true); + } + else + { + CHECK(d->val % 2, true); + } + } + CHECK(size(&fom), (size / 2)); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(fomap_test_entry_api_macros) +{ + /* Over allocate size now because we don't want to worry about resizing. */ + int const size = 200; + ccc_flat_ordered_map fom + = fom_init((struct val[200]){}, 200, elem, id, NULL, id_cmp, NULL); + + /* Test entry or insert with for all even values. Default should be + inserted. All entries are hashed to last digit so many spread out + collisions. */ + for (int i = 0; i < size / 2; i += 2) + { + /* The macros support functions that will only execute if the or + insert branch executes. */ + struct val const *const d + = fom_or_insert_w(entry_r(&fom, &i), fomap_create(i, i)); + CHECK((d != NULL), true); + CHECK(d->id, i); + CHECK(d->val, i); + } + CHECK(size(&fom), (size / 2) / 2); + /* The default insertion should not occur every other element. */ + for (int i = 0; i < size / 2; ++i) + { + struct val const *const d = fom_or_insert_w( + and_modify(entry_r(&fom, &i), fomap_modplus), fomap_create(i, i)); + /* All values in the array should be odd now */ + CHECK((d != NULL), true); + CHECK(d->id, i); + if (i % 2) + { + CHECK(d->val, i); + } + else + { + CHECK(d->val, i + 1); + } + CHECK(d->val % 2, true); + } + CHECK(size(&fom), (size / 2)); + /* More simply modifications don't require the and modify function. All + should be switched back to even now. */ + for (int i = 0; i < size / 2; ++i) + { + struct val *v = fom_or_insert_w(entry_r(&fom, &i), (struct val){}); + CHECK(v != NULL, true); + v->val++; + /* All values in the array should be odd now */ + CHECK(v->val % 2 == 0, true); + } + CHECK(size(&fom), (size / 2)); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(fomap_test_two_sum) +{ + ccc_flat_ordered_map fom + = fom_init((struct val[20]){}, 20, elem, id, NULL, id_cmp, NULL); + int const addends[10] = {1, 3, -980, 6, 7, 13, 44, 32, 995, -1}; + int const target = 15; + int solution_indices[2] = {-1, -1}; + for (size_t i = 0; i < (sizeof(addends) / sizeof(addends[0])); ++i) + { + struct val const *const other_addend + = get_key_val(&fom, &(int){target - addends[i]}); + if (other_addend) + { + solution_indices[0] = (int)i; + solution_indices[1] = other_addend->val; + break; + } + ccc_entry const e = insert_or_assign( + &fom, &(struct val){.id = addends[i], .val = i}.elem); + CHECK(insert_error(&e), false); + } + CHECK(solution_indices[0], 8); + CHECK(solution_indices[1], 2); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(fomap_test_resize) +{ + size_t const prime_start = 11; + ccc_flat_ordered_map fom + = fom_init((struct val *)malloc(sizeof(struct val) * prime_start), + prime_start, elem, id, std_alloc, id_cmp, NULL); + CHECK(fom_data(&fom) != NULL, true); + + int const to_insert = 1000; + int const larger_prime = 1009; + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val elem = {.id = shuffled_index, .val = i}; + struct val *v = insert_entry(entry_r(&fom, &elem.id), &elem.elem); + CHECK(v != NULL, true); + CHECK(v->id, shuffled_index); + CHECK(v->val, i); + CHECK(validate(&fom), true); + } + CHECK(size(&fom), to_insert); + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val swap_slot = {shuffled_index, shuffled_index, {}}; + struct val const *const in_table + = insert_entry(entry_r(&fom, &swap_slot.id), &swap_slot.elem); + CHECK(in_table != NULL, true); + CHECK(in_table->val, shuffled_index); + } + CHECK(fom_clear_and_free(&fom, NULL), CCC_OK); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(fomap_test_resize_macros) +{ + size_t const prime_start = 11; + ccc_flat_ordered_map fom + = fom_init((struct val *)malloc(sizeof(struct val) * prime_start), + prime_start, elem, id, std_alloc, id_cmp, NULL); + CHECK(fom_data(&fom) != NULL, true); + int const to_insert = 1000; + int const larger_prime = 1009; + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val *v = insert_entry(entry_r(&fom, &shuffled_index), + &(struct val){shuffled_index, i, {}}.elem); + CHECK(v != NULL, true); + CHECK(v->id, shuffled_index); + CHECK(v->val, i); + } + CHECK(size(&fom), to_insert); + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val const *const in_table = fom_or_insert_w( + fom_and_modify_w(entry_r(&fom, &shuffled_index), struct val, + { + T->val = shuffled_index; + }), + (struct val){}); + CHECK(in_table != NULL, true); + CHECK(in_table->val, shuffled_index); + struct val *v + = fom_or_insert_w(entry_r(&fom, &shuffled_index), (struct val){}); + CHECK(v == NULL, false); + v->val = i; + v = get_key_val(&fom, &shuffled_index); + CHECK(v != NULL, true); + CHECK(v->val, i); + } + CHECK(fom_clear_and_free(&fom, NULL), CCC_OK); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(fomap_test_resize_from_null) +{ + ccc_flat_ordered_map fom + = fom_init((struct val *)NULL, 0, elem, id, std_alloc, id_cmp, NULL); + int const to_insert = 1000; + int const larger_prime = 1009; + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val elem = {.id = shuffled_index, .val = i}; + struct val *v = insert_entry(entry_r(&fom, &elem.id), &elem.elem); + CHECK(v != NULL, true); + CHECK(v->id, shuffled_index); + CHECK(v->val, i); + } + CHECK(size(&fom), to_insert); + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val swap_slot = {shuffled_index, shuffled_index, {}}; + struct val const *const in_table + = insert_entry(entry_r(&fom, &swap_slot.id), &swap_slot.elem); + CHECK(in_table != NULL, true); + CHECK(in_table->val, shuffled_index); + } + CHECK(fom_clear_and_free(&fom, NULL), CCC_OK); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(fomap_test_resize_from_null_macros) +{ + ccc_flat_ordered_map fom + = fom_init((struct val *)NULL, 0, elem, id, std_alloc, id_cmp, NULL); + int const to_insert = 1000; + int const larger_prime = 1009; + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val *v = insert_entry(entry_r(&fom, &shuffled_index), + &(struct val){shuffled_index, i, {}}.elem); + CHECK(v != NULL, true); + CHECK(v->id, shuffled_index); + CHECK(v->val, i); + } + CHECK(size(&fom), to_insert); + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val const *const in_table = fom_or_insert_w( + fom_and_modify_w(entry_r(&fom, &shuffled_index), struct val, + { + T->val = shuffled_index; + }), + (struct val){}); + CHECK(in_table != NULL, true); + CHECK(in_table->val, shuffled_index); + struct val *v + = fom_or_insert_w(entry_r(&fom, &shuffled_index), (struct val){}); + CHECK(v == NULL, false); + v->val = i; + v = get_key_val(&fom, &shuffled_index); + CHECK(v == NULL, false); + CHECK(v->val, i); + } + CHECK(fom_clear_and_free(&fom, NULL), CCC_OK); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(fomap_test_insert_limit) +{ + int const size = 101; + ccc_flat_ordered_map fom + = fom_init((struct val[101]){}, 101, elem, id, NULL, id_cmp, NULL); + + int const larger_prime = 103; + int last_index = 0; + int shuffled_index = larger_prime % size; + for (int i = 0; i < size; + ++i, shuffled_index = (shuffled_index + larger_prime) % size) + { + struct val *v = insert_entry(entry_r(&fom, &shuffled_index), + &(struct val){shuffled_index, i, {}}.elem); + if (!v) + { + break; + } + CHECK(v->id, shuffled_index); + CHECK(v->val, i); + last_index = shuffled_index; + } + size_t const final_size = size(&fom); + /* The last successful entry is still in the table and is overwritten. */ + struct val v = {.id = last_index, .val = -1}; + ccc_entry ent = insert(&fom, &v.elem); + CHECK(unwrap(&ent) != NULL, true); + CHECK(insert_error(&ent), false); + CHECK(size(&fom), final_size); + + v = (struct val){.id = last_index, .val = -2}; + struct val *in_table = insert_entry(entry_r(&fom, &v.id), &v.elem); + CHECK(in_table != NULL, true); + CHECK(in_table->val, -2); + CHECK(size(&fom), final_size); + + in_table = insert_entry(entry_r(&fom, &last_index), + &(struct val){.id = last_index, .val = -3}.elem); + CHECK(in_table != NULL, true); + CHECK(in_table->val, -3); + CHECK(size(&fom), final_size); + + /* The shuffled index key that failed insertion should fail again. */ + v = (struct val){.id = shuffled_index, .val = -4}; + in_table = insert_entry(entry_r(&fom, &v.id), &v.elem); + CHECK(in_table == NULL, true); + CHECK(size(&fom), final_size); + + in_table + = insert_entry(entry_r(&fom, &shuffled_index), + &(struct val){.id = shuffled_index, .val = -4}.elem); + CHECK(in_table == NULL, true); + CHECK(size(&fom), final_size); + + ent = insert(&fom, &v.elem); + CHECK(unwrap(&ent) == NULL, true); + CHECK(insert_error(&ent), true); + CHECK(size(&fom), final_size); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(fomap_test_insert_and_find) +{ + int const size = 101; + ccc_flat_ordered_map fom + = fom_init((struct val[101]){}, 101, elem, id, NULL, id_cmp, NULL); + + for (int i = 0; i < size; i += 2) + { + ccc_entry e = try_insert(&fom, &(struct val){.id = i, .val = i}.elem); + CHECK(occupied(&e), false); + CHECK(validate(&fom), true); + e = try_insert(&fom, &(struct val){.id = i, .val = i}.elem); + CHECK(occupied(&e), true); + CHECK(validate(&fom), true); + struct val const *const v = unwrap(&e); + CHECK(v == NULL, false); + CHECK(v->id, i); + CHECK(v->val, i); + } + for (int i = 0; i < size; i += 2) + { + CHECK(contains(&fom, &i), true); + CHECK(occupied(entry_r(&fom, &i)), true); + CHECK(validate(&fom), true); + } + for (int i = 1; i < size; i += 2) + { + CHECK(contains(&fom, &i), false); + CHECK(occupied(entry_r(&fom, &i)), false); + CHECK(validate(&fom), true); + } + CHECK_END_FN(); } CHECK_BEGIN_STATIC_FN(fomap_test_insert_shuffle) @@ -100,7 +648,14 @@ CHECK_BEGIN_STATIC_FN(fomap_test_insert_weak_srand) int main() { - return CHECK_RUN(fomap_test_insert_one(), fomap_test_insert_macros(), - fomap_test_insert_shuffle(), - fomap_test_insert_weak_srand()); + return CHECK_RUN( + fomap_test_insert(), fomap_test_insert_macros(), + fomap_test_insert_and_find(), fomap_test_insert_overwrite(), + fomap_test_insert_then_bad_ideas(), fomap_test_insert_via_entry(), + fomap_test_insert_via_entry_macros(), fomap_test_entry_api_functional(), + fomap_test_entry_api_macros(), fomap_test_two_sum(), + fomap_test_resize(), fomap_test_resize_macros(), + fomap_test_resize_from_null(), fomap_test_resize_from_null_macros(), + fomap_test_insert_limit(), fomap_test_insert_weak_srand(), + fomap_test_insert_shuffle()); } diff --git a/tests/fromap/test_fromap_insert.c b/tests/fromap/test_fromap_insert.c index 47f98f19..c4e860f3 100644 --- a/tests/fromap/test_fromap_insert.c +++ b/tests/fromap/test_fromap_insert.c @@ -14,66 +14,613 @@ #include #include -CHECK_BEGIN_STATIC_FN(fromap_test_insert_one) +static inline struct val +fromap_create(int const id, int const val) { - flat_realtime_ordered_map s - = frm_init((struct val[2]){}, 2, elem, id, NULL, id_cmp, NULL); - CHECK(occupied(insert_r(&s, &(struct val){}.elem)), false); - CHECK(is_empty(&s), false); + return (struct val){.id = id, .val = val}; +} + +static inline void +fromap_modplus(ccc_user_type const t) +{ + ((struct val *)t.user_type)->val++; +} + +CHECK_BEGIN_STATIC_FN(fromap_test_insert) +{ + ccc_flat_realtime_ordered_map frm + = frm_init((struct val[10]){}, 10, elem, id, NULL, id_cmp, NULL); + + /* Nothing was there before so nothing is in the entry. */ + ccc_entry ent = insert(&frm, &(struct val){.id = 137, .val = 99}.elem); + CHECK(occupied(&ent), false); + CHECK(unwrap(&ent), NULL); + CHECK(size(&frm), 1); CHECK_END_FN(); } CHECK_BEGIN_STATIC_FN(fromap_test_insert_macros) { - /* This is also a good test to see if the buffer can manage its own memory - when provided with a std_alloc function starting from NULL. */ - flat_realtime_ordered_map s - = frm_init((struct val *)NULL, 0, elem, id, std_alloc, id_cmp, NULL); - struct val *v = frm_or_insert_w(entry_r(&s, &(int){0}), (struct val){}); + ccc_flat_realtime_ordered_map frm + = frm_init((struct val[10]){}, 10, elem, id, NULL, id_cmp, NULL); + + struct val const *ins = ccc_frm_or_insert_w( + entry_r(&frm, &(int){2}), (struct val){.id = 2, .val = 0}); + CHECK(ins != NULL, true); + CHECK(validate(&frm), true); + CHECK(size(&frm), 1); + ins = frm_insert_entry_w(entry_r(&frm, &(int){2}), + (struct val){.id = 2, .val = 0}); + CHECK(validate(&frm), true); + CHECK(ins != NULL, true); + ins = frm_insert_entry_w(entry_r(&frm, &(int){9}), + (struct val){.id = 9, .val = 1}); + CHECK(validate(&frm), true); + CHECK(ins != NULL, true); + ins = ccc_entry_unwrap( + frm_insert_or_assign_w(&frm, 3, (struct val){.val = 99})); + CHECK(validate(&frm), true); + CHECK(ins == NULL, false); + CHECK(validate(&frm), true); + CHECK(ins->val, 99); + CHECK(size(&frm), 3); + ins = ccc_entry_unwrap( + frm_insert_or_assign_w(&frm, 3, (struct val){.val = 98})); + CHECK(validate(&frm), true); + CHECK(ins == NULL, false); + CHECK(ins->val, 98); + CHECK(size(&frm), 3); + ins = ccc_entry_unwrap(frm_try_insert_w(&frm, 3, (struct val){.val = 100})); + CHECK(ins == NULL, false); + CHECK(validate(&frm), true); + CHECK(ins->val, 98); + CHECK(size(&frm), 3); + ins = ccc_entry_unwrap(frm_try_insert_w(&frm, 4, (struct val){.val = 100})); + CHECK(ins == NULL, false); + CHECK(validate(&frm), true); + CHECK(ins->val, 100); + CHECK(size(&frm), 4); + CHECK_END_FN(ccc_frm_clear_and_free(&frm, NULL);); +} + +CHECK_BEGIN_STATIC_FN(fromap_test_insert_overwrite) +{ + ccc_flat_realtime_ordered_map frm + = frm_init((struct val[10]){}, 10, elem, id, NULL, id_cmp, NULL); + + struct val q = {.id = 137, .val = 99}; + ccc_entry ent = insert(&frm, &q.elem); + CHECK(occupied(&ent), false); + CHECK(unwrap(&ent), NULL); + + struct val const *v = unwrap(entry_r(&frm, &q.id)); CHECK(v != NULL, true); - v = frm_insert_entry_w(entry_r(&s, &(int){0}), - (struct val){.id = 0, .val = 99}); - CHECK(validate(&s), true); - CHECK(v == NULL, false); CHECK(v->val, 99); - v = frm_insert_entry_w(entry_r(&s, &(int){9}), - (struct val){.id = 9, .val = 100}); + + /* Now the second insertion will take place and the old occupying value + will be written into our struct we used to make the query. */ + q = (struct val){.id = 137, .val = 100}; + + /* The contents of q are now in the table. */ + ccc_entry old_ent = insert(&frm, &q.elem); + CHECK(occupied(&old_ent), true); + + /* The old contents are now in q and the entry is in the table. */ + v = unwrap(&old_ent); CHECK(v != NULL, true); - CHECK(v->val, 100); - v = unwrap(frm_insert_or_assign_w(&s, 1, (struct val){.val = 100})); - CHECK(validate(&s), true); + CHECK(v->val, 99); + CHECK(q.val, 99); + v = unwrap(entry_r(&frm, &q.id)); CHECK(v != NULL, true); CHECK(v->val, 100); - CHECK(size(&s), 3); - v = unwrap(frm_insert_or_assign_w(&s, 1, (struct val){.val = 99})); - CHECK(validate(&s), true); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(fromap_test_insert_then_bad_ideas) +{ + ccc_flat_realtime_ordered_map frm + = frm_init((struct val[10]){}, 10, elem, id, NULL, id_cmp, NULL); + struct val q = {.id = 137, .val = 99}; + ccc_entry ent = insert(&frm, &q.elem); + CHECK(occupied(&ent), false); + CHECK(unwrap(&ent), NULL); + struct val const *v = unwrap(entry_r(&frm, &q.id)); CHECK(v != NULL, true); CHECK(v->val, 99); - CHECK(size(&s), 3); - v = unwrap(frm_try_insert_w(&s, 1, (struct val){.val = 2})); - CHECK(validate(&s), true); + + q = (struct val){.id = 137, .val = 100}; + + ent = insert(&frm, &q.elem); + CHECK(occupied(&ent), true); + v = unwrap(&ent); CHECK(v != NULL, true); CHECK(v->val, 99); - CHECK(size(&s), 3); - v = unwrap(frm_try_insert_w(&s, 2, (struct val){.val = 2})); - CHECK(validate(&s), true); + CHECK(q.val, 99); + q.val -= 9; + + v = get_key_val(&frm, &q.id); CHECK(v != NULL, true); - CHECK(v->val, 2); - CHECK(size(&s), 4); - CHECK_END_FN(frm_clear_and_free(&s, NULL);); + CHECK(v->val, 100); + CHECK(q.val, 90); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(fromap_test_entry_api_functional) +{ + /* Over allocate size now because we don't want to worry about resizing. */ + ccc_flat_realtime_ordered_map frm + = frm_init((struct val[200]){}, 200, elem, id, NULL, id_cmp, NULL); + size_t const size = 200; + + /* Test entry or insert with for all even values. Default should be + inserted. All entries are hashed to last digit so many spread out + collisions. */ + struct val def = {0}; + for (size_t i = 0; i < size / 2; i += 2) + { + def.id = (int)i; + def.val = (int)i; + struct val const *const d + = or_insert(entry_r(&frm, &def.id), &def.elem); + CHECK((d != NULL), true); + CHECK(d->id, i); + CHECK(d->val, i); + } + CHECK(size(&frm), (size / 2) / 2); + /* The default insertion should not occur every other element. */ + for (size_t i = 0; i < size / 2; ++i) + { + def.id = (int)i; + def.val = (int)i; + struct val const *const d + = or_insert(frm_and_modify_w(entry_r(&frm, &def.id), struct val, + { + T->val++; + }), + &def.elem); + /* All values in the array should be odd now */ + CHECK((d != NULL), true); + CHECK(d->id, i); + if (i % 2) + { + CHECK(d->val, i); + } + else + { + CHECK(d->val, i + 1); + } + CHECK(d->val % 2, true); + } + CHECK(size(&frm), (size / 2)); + /* More simply modifications don't require the and modify function. All + should be switched back to even now. */ + for (size_t i = 0; i < size / 2; ++i) + { + def.id = (int)i; + def.val = (int)i; + struct val *const in = or_insert(entry_r(&frm, &def.id), &def.elem); + in->val++; + /* All values in the array should be odd now */ + CHECK((in->val % 2 == 0), true); + } + CHECK(size(&frm), (size / 2)); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(fromap_test_insert_via_entry) +{ + /* Over allocate size now because we don't want to worry about resizing. */ + size_t const size = 200; + ccc_flat_realtime_ordered_map frm + = frm_init((struct val[200]){}, 200, elem, id, NULL, id_cmp, NULL); + + /* Test entry or insert with for all even values. Default should be + inserted. All entries are hashed to last digit so many spread out + collisions. */ + struct val def = {0}; + for (size_t i = 0; i < size / 2; i += 2) + { + def.id = (int)i; + def.val = (int)i; + struct val const *const d + = insert_entry(entry_r(&frm, &def.id), &def.elem); + CHECK((d != NULL), true); + CHECK(d->id, i); + CHECK(d->val, i); + } + CHECK(size(&frm), (size / 2) / 2); + /* The default insertion should not occur every other element. */ + for (size_t i = 0; i < size / 2; ++i) + { + def.id = (int)i; + def.val = (int)i + 1; + struct val const *const d + = insert_entry(entry_r(&frm, &def.id), &def.elem); + /* All values in the array should be odd now */ + CHECK((d != NULL), true); + CHECK(d->val, i + 1); + if (i % 2) + { + CHECK(d->val % 2 == 0, true); + } + else + { + CHECK(d->val % 2, true); + } + } + CHECK(size(&frm), (size / 2)); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(fromap_test_insert_via_entry_macros) +{ + /* Over allocate size now because we don't want to worry about resizing. */ + size_t const size = 200; + ccc_flat_realtime_ordered_map frm + = frm_init((struct val[200]){}, 200, elem, id, NULL, id_cmp, NULL); + + /* Test entry or insert with for all even values. Default should be + inserted. All entries are hashed to last digit so many spread out + collisions. */ + for (size_t i = 0; i < size / 2; i += 2) + { + struct val const *const d + = insert_entry(entry_r(&frm, &i), &(struct val){i, i, {}}.elem); + CHECK((d != NULL), true); + CHECK(d->id, i); + CHECK(d->val, i); + } + CHECK(size(&frm), (size / 2) / 2); + /* The default insertion should not occur every other element. */ + for (size_t i = 0; i < size / 2; ++i) + { + struct val const *const d + = insert_entry(entry_r(&frm, &i), &(struct val){i, i + 1, {}}.elem); + /* All values in the array should be odd now */ + CHECK((d != NULL), true); + CHECK(d->val, i + 1); + if (i % 2) + { + CHECK(d->val % 2 == 0, true); + } + else + { + CHECK(d->val % 2, true); + } + } + CHECK(size(&frm), (size / 2)); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(fromap_test_entry_api_macros) +{ + /* Over allocate size now because we don't want to worry about resizing. */ + int const size = 200; + ccc_flat_realtime_ordered_map frm + = frm_init((struct val[200]){}, 200, elem, id, NULL, id_cmp, NULL); + + /* Test entry or insert with for all even values. Default should be + inserted. All entries are hashed to last digit so many spread out + collisions. */ + for (int i = 0; i < size / 2; i += 2) + { + /* The macros support functions that will only execute if the or + insert branch executes. */ + struct val const *const d + = frm_or_insert_w(entry_r(&frm, &i), fromap_create(i, i)); + CHECK((d != NULL), true); + CHECK(d->id, i); + CHECK(d->val, i); + } + CHECK(size(&frm), (size / 2) / 2); + /* The default insertion should not occur every other element. */ + for (int i = 0; i < size / 2; ++i) + { + struct val const *const d = frm_or_insert_w( + and_modify(entry_r(&frm, &i), fromap_modplus), fromap_create(i, i)); + /* All values in the array should be odd now */ + CHECK((d != NULL), true); + CHECK(d->id, i); + if (i % 2) + { + CHECK(d->val, i); + } + else + { + CHECK(d->val, i + 1); + } + CHECK(d->val % 2, true); + } + CHECK(size(&frm), (size / 2)); + /* More simply modifications don't require the and modify function. All + should be switched back to even now. */ + for (int i = 0; i < size / 2; ++i) + { + struct val *v = frm_or_insert_w(entry_r(&frm, &i), (struct val){}); + CHECK(v != NULL, true); + v->val++; + /* All values in the array should be odd now */ + CHECK(v->val % 2 == 0, true); + } + CHECK(size(&frm), (size / 2)); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(fromap_test_two_sum) +{ + ccc_flat_realtime_ordered_map frm + = frm_init((struct val[20]){}, 20, elem, id, NULL, id_cmp, NULL); + int const addends[10] = {1, 3, -980, 6, 7, 13, 44, 32, 995, -1}; + int const target = 15; + int solution_indices[2] = {-1, -1}; + for (size_t i = 0; i < (sizeof(addends) / sizeof(addends[0])); ++i) + { + struct val const *const other_addend + = get_key_val(&frm, &(int){target - addends[i]}); + if (other_addend) + { + solution_indices[0] = (int)i; + solution_indices[1] = other_addend->val; + break; + } + ccc_entry const e = insert_or_assign( + &frm, &(struct val){.id = addends[i], .val = i}.elem); + CHECK(insert_error(&e), false); + } + CHECK(solution_indices[0], 8); + CHECK(solution_indices[1], 2); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(fromap_test_resize) +{ + size_t const prime_start = 11; + ccc_flat_realtime_ordered_map frm + = frm_init((struct val *)malloc(sizeof(struct val) * prime_start), + prime_start, elem, id, std_alloc, id_cmp, NULL); + CHECK(frm_data(&frm) != NULL, true); + + int const to_insert = 1000; + int const larger_prime = 1009; + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val elem = {.id = shuffled_index, .val = i}; + struct val *v = insert_entry(entry_r(&frm, &elem.id), &elem.elem); + CHECK(v != NULL, true); + CHECK(v->id, shuffled_index); + CHECK(v->val, i); + CHECK(validate(&frm), true); + } + CHECK(size(&frm), to_insert); + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val swap_slot = {shuffled_index, shuffled_index, {}}; + struct val const *const in_table + = insert_entry(entry_r(&frm, &swap_slot.id), &swap_slot.elem); + CHECK(in_table != NULL, true); + CHECK(in_table->val, shuffled_index); + } + CHECK(frm_clear_and_free(&frm, NULL), CCC_OK); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(fromap_test_resize_macros) +{ + size_t const prime_start = 11; + ccc_flat_realtime_ordered_map frm + = frm_init((struct val *)malloc(sizeof(struct val) * prime_start), + prime_start, elem, id, std_alloc, id_cmp, NULL); + CHECK(frm_data(&frm) != NULL, true); + int const to_insert = 1000; + int const larger_prime = 1009; + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val *v = insert_entry(entry_r(&frm, &shuffled_index), + &(struct val){shuffled_index, i, {}}.elem); + CHECK(v != NULL, true); + CHECK(v->id, shuffled_index); + CHECK(v->val, i); + } + CHECK(size(&frm), to_insert); + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val const *const in_table = frm_or_insert_w( + frm_and_modify_w(entry_r(&frm, &shuffled_index), struct val, + { + T->val = shuffled_index; + }), + (struct val){}); + CHECK(in_table != NULL, true); + CHECK(in_table->val, shuffled_index); + struct val *v + = frm_or_insert_w(entry_r(&frm, &shuffled_index), (struct val){}); + CHECK(v == NULL, false); + v->val = i; + v = get_key_val(&frm, &shuffled_index); + CHECK(v != NULL, true); + CHECK(v->val, i); + } + CHECK(frm_clear_and_free(&frm, NULL), CCC_OK); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(fromap_test_resize_from_null) +{ + ccc_flat_realtime_ordered_map frm + = frm_init((struct val *)NULL, 0, elem, id, std_alloc, id_cmp, NULL); + int const to_insert = 1000; + int const larger_prime = 1009; + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val elem = {.id = shuffled_index, .val = i}; + struct val *v = insert_entry(entry_r(&frm, &elem.id), &elem.elem); + CHECK(v != NULL, true); + CHECK(v->id, shuffled_index); + CHECK(v->val, i); + } + CHECK(size(&frm), to_insert); + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val swap_slot = {shuffled_index, shuffled_index, {}}; + struct val const *const in_table + = insert_entry(entry_r(&frm, &swap_slot.id), &swap_slot.elem); + CHECK(in_table != NULL, true); + CHECK(in_table->val, shuffled_index); + } + CHECK(frm_clear_and_free(&frm, NULL), CCC_OK); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(fromap_test_resize_from_null_macros) +{ + ccc_flat_realtime_ordered_map frm + = frm_init((struct val *)NULL, 0, elem, id, std_alloc, id_cmp, NULL); + int const to_insert = 1000; + int const larger_prime = 1009; + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val *v = insert_entry(entry_r(&frm, &shuffled_index), + &(struct val){shuffled_index, i, {}}.elem); + CHECK(v != NULL, true); + CHECK(v->id, shuffled_index); + CHECK(v->val, i); + } + CHECK(size(&frm), to_insert); + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val const *const in_table = frm_or_insert_w( + frm_and_modify_w(entry_r(&frm, &shuffled_index), struct val, + { + T->val = shuffled_index; + }), + (struct val){}); + CHECK(in_table != NULL, true); + CHECK(in_table->val, shuffled_index); + struct val *v + = frm_or_insert_w(entry_r(&frm, &shuffled_index), (struct val){}); + CHECK(v == NULL, false); + v->val = i; + v = get_key_val(&frm, &shuffled_index); + CHECK(v == NULL, false); + CHECK(v->val, i); + } + CHECK(frm_clear_and_free(&frm, NULL), CCC_OK); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(fromap_test_insert_limit) +{ + int const size = 101; + ccc_flat_realtime_ordered_map frm + = frm_init((struct val[101]){}, 101, elem, id, NULL, id_cmp, NULL); + + int const larger_prime = 103; + int last_index = 0; + int shuffled_index = larger_prime % size; + for (int i = 0; i < size; + ++i, shuffled_index = (shuffled_index + larger_prime) % size) + { + struct val *v = insert_entry(entry_r(&frm, &shuffled_index), + &(struct val){shuffled_index, i, {}}.elem); + if (!v) + { + break; + } + CHECK(v->id, shuffled_index); + CHECK(v->val, i); + last_index = shuffled_index; + } + size_t const final_size = size(&frm); + /* The last successful entry is still in the table and is overwritten. */ + struct val v = {.id = last_index, .val = -1}; + ccc_entry ent = insert(&frm, &v.elem); + CHECK(unwrap(&ent) != NULL, true); + CHECK(insert_error(&ent), false); + CHECK(size(&frm), final_size); + + v = (struct val){.id = last_index, .val = -2}; + struct val *in_table = insert_entry(entry_r(&frm, &v.id), &v.elem); + CHECK(in_table != NULL, true); + CHECK(in_table->val, -2); + CHECK(size(&frm), final_size); + + in_table = insert_entry(entry_r(&frm, &last_index), + &(struct val){.id = last_index, .val = -3}.elem); + CHECK(in_table != NULL, true); + CHECK(in_table->val, -3); + CHECK(size(&frm), final_size); + + /* The shuffled index key that failed insertion should fail again. */ + v = (struct val){.id = shuffled_index, .val = -4}; + in_table = insert_entry(entry_r(&frm, &v.id), &v.elem); + CHECK(in_table == NULL, true); + CHECK(size(&frm), final_size); + + in_table + = insert_entry(entry_r(&frm, &shuffled_index), + &(struct val){.id = shuffled_index, .val = -4}.elem); + CHECK(in_table == NULL, true); + CHECK(size(&frm), final_size); + + ent = insert(&frm, &v.elem); + CHECK(unwrap(&ent) == NULL, true); + CHECK(insert_error(&ent), true); + CHECK(size(&frm), final_size); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(fromap_test_insert_and_find) +{ + int const size = 101; + ccc_flat_realtime_ordered_map frm + = frm_init((struct val[101]){}, 101, elem, id, NULL, id_cmp, NULL); + + for (int i = 0; i < size; i += 2) + { + ccc_entry e = try_insert(&frm, &(struct val){.id = i, .val = i}.elem); + CHECK(occupied(&e), false); + CHECK(validate(&frm), true); + e = try_insert(&frm, &(struct val){.id = i, .val = i}.elem); + CHECK(occupied(&e), true); + CHECK(validate(&frm), true); + struct val const *const v = unwrap(&e); + CHECK(v == NULL, false); + CHECK(v->id, i); + CHECK(v->val, i); + } + for (int i = 0; i < size; i += 2) + { + CHECK(contains(&frm, &i), true); + CHECK(occupied(entry_r(&frm, &i)), true); + CHECK(validate(&frm), true); + } + for (int i = 1; i < size; i += 2) + { + CHECK(contains(&frm, &i), false); + CHECK(occupied(entry_r(&frm, &i)), false); + CHECK(validate(&frm), true); + } + CHECK_END_FN(); } CHECK_BEGIN_STATIC_FN(fromap_test_insert_shuffle) { size_t const size = 50; - ccc_flat_realtime_ordered_map s + ccc_flat_realtime_ordered_map frm = frm_init((struct val[51]){}, 51, elem, id, NULL, id_cmp, NULL); CHECK(size > 1, true); int const prime = 53; - CHECK(insert_shuffled(&s, size, prime), PASS); - + CHECK(insert_shuffled(&frm, size, prime), PASS); int sorted_check[50]; - CHECK(inorder_fill(sorted_check, size, &s), size); + CHECK(inorder_fill(sorted_check, size, &frm), size); for (size_t i = 1; i < size; ++i) { CHECK(sorted_check[i - 1] <= sorted_check[i], true); @@ -84,24 +631,31 @@ CHECK_BEGIN_STATIC_FN(fromap_test_insert_shuffle) CHECK_BEGIN_STATIC_FN(fromap_test_insert_weak_srand) { int const num_nodes = 1000; - ccc_flat_realtime_ordered_map s + ccc_flat_realtime_ordered_map frm = frm_init((struct val[1001]){}, 1001, elem, id, NULL, id_cmp, NULL); srand(time(NULL)); /* NOLINT */ for (int i = 0; i < num_nodes; ++i) { - ccc_entry const e - = insert(&s, &(struct val){.id = rand() /*NOLINT*/, .val = i}.elem); + ccc_entry const e = insert( + &frm, &(struct val){.id = rand() /* NOLINT */, .val = i}.elem); CHECK(insert_error(&e), false); - CHECK(validate(&s), true); + CHECK(validate(&frm), true); } - CHECK(size(&s), (size_t)num_nodes); + CHECK(size(&frm), (size_t)num_nodes); CHECK_END_FN(); } int main() { - return CHECK_RUN(fromap_test_insert_one(), fromap_test_insert_macros(), - fromap_test_insert_shuffle(), - fromap_test_insert_weak_srand()); + return CHECK_RUN( + fromap_test_insert(), fromap_test_insert_macros(), + fromap_test_insert_and_find(), fromap_test_insert_overwrite(), + fromap_test_insert_then_bad_ideas(), fromap_test_insert_via_entry(), + fromap_test_insert_via_entry_macros(), + fromap_test_entry_api_functional(), fromap_test_entry_api_macros(), + fromap_test_two_sum(), fromap_test_resize(), + fromap_test_resize_macros(), fromap_test_resize_from_null(), + fromap_test_resize_from_null_macros(), fromap_test_insert_limit(), + fromap_test_insert_weak_srand(), fromap_test_insert_shuffle()); } diff --git a/tests/omap/test_omap_insert.c b/tests/omap/test_omap_insert.c index d0499f82..3ed74f5f 100644 --- a/tests/omap/test_omap_insert.c +++ b/tests/omap/test_omap_insert.c @@ -1,3 +1,9 @@ +#include +#include +#include +#include +#include + #define TRAITS_USING_NAMESPACE_CCC #define ORDERED_MAP_USING_NAMESPACE_CCC @@ -8,159 +14,579 @@ #include "traits.h" #include "types.h" -#include -#include -#include -#include +static inline struct val +omap_create(int const id, int const val) +{ + return (struct val){.key = id, .val = val}; +} -CHECK_BEGIN_STATIC_FN(omap_test_insert_one) +static inline void +omap_modplus(ccc_user_type const t) { - ccc_ordered_map s - = ccc_om_init(s, struct val, elem, key, NULL, id_cmp, NULL); - struct val single; - single.key = 0; - CHECK(insert_entry(entry_r(&s, &single.key), &single.elem) != NULL, true); - CHECK(is_empty(&s), false); - CHECK_END_FN(); + ((struct val *)t.user_type)->val++; } -CHECK_BEGIN_STATIC_FN(omap_test_insert_three) -{ - ccc_ordered_map s - = ccc_om_init(s, struct val, elem, key, std_alloc, id_cmp, NULL); - struct val swap_slot = {.val = 99, .key = 1}; - CHECK(occupied(insert_r(&s, &swap_slot.elem, &(struct val){}.elem)), false); - CHECK(validate(&s), true); - CHECK(size(&s), (size_t)1); - swap_slot = (struct val){.val = 137, .key = 1}; - ccc_entry *const e = insert_r(&s, &swap_slot.elem, &(struct val){}.elem); - struct val const *ins = unwrap(e); - CHECK(occupied(e), true); - CHECK(validate(&s), true); - CHECK(size(&s), (size_t)1); - CHECK(ins != NULL, true); - CHECK(ins->val, 99); - CHECK(ins->key, 1); - CHECK(swap_slot.key, 1); - CHECK(swap_slot.val, 99); - ins = ccc_om_or_insert_w(entry_r(&s, &(int){2}), - (struct val){.val = 0, .key = 2}); - CHECK(ins != NULL, true); - CHECK(ins->val, 0); - CHECK(validate(&s), true); - CHECK(size(&s), (size_t)2); - ins = insert_entry(entry_r(&s, &(int){2}), - &(struct val){.val = 1, .key = 2}.elem); - CHECK(ins != NULL, true); - CHECK(ins->val, 1); - CHECK(validate(&s), true); - CHECK(size(&s), (size_t)2); - ins = ccc_entry_unwrap( - om_insert_or_assign_w(&s, 3, (struct val){.val = 99})); - CHECK(ins == NULL, false); - CHECK(validate(&s), true); - CHECK(ins->val, 99); - CHECK(ins->key, 3); - CHECK(size(&s), 3); - CHECK_END_FN((void)ccc_om_clear(&s, NULL);); +CHECK_BEGIN_STATIC_FN(omap_test_insert) +{ + ccc_ordered_map om = om_init(om, struct val, elem, key, NULL, id_cmp, NULL); + + /* Nothing was there before so nothing is in the entry. */ + ccc_entry ent = insert(&om, &(struct val){.key = 137, .val = 99}.elem, + &(struct val){}.elem); + CHECK(occupied(&ent), false); + CHECK(unwrap(&ent), NULL); + CHECK(size(&om), 1); + CHECK_END_FN(); } CHECK_BEGIN_STATIC_FN(omap_test_insert_macros) { - ccc_ordered_map s - = ccc_om_init(s, struct val, elem, key, std_alloc, id_cmp, NULL); + ccc_ordered_map om + = om_init(om, struct val, elem, key, std_alloc, id_cmp, NULL); + struct val const *ins = ccc_om_or_insert_w( - entry_r(&s, &(int){2}), (struct val){.val = 0, .key = 2}); + entry_r(&om, &(int){2}), (struct val){.key = 2, .val = 0}); CHECK(ins != NULL, true); - CHECK(validate(&s), true); - CHECK(size(&s), 1); - ins = om_insert_entry_w(entry_r(&s, &(int){2}), - (struct val){.val = 0, .key = 2}); + CHECK(validate(&om), true); + CHECK(size(&om), 1); + ins = om_insert_entry_w(entry_r(&om, &(int){2}), + (struct val){.key = 2, .val = 0}); + CHECK(validate(&om), true); CHECK(ins != NULL, true); - CHECK(validate(&s), true); - CHECK(size(&s), 1); - ins = om_insert_entry_w(entry_r(&s, &(int){9}), - (struct val){.val = 1, .key = 9}); + ins = om_insert_entry_w(entry_r(&om, &(int){9}), + (struct val){.key = 9, .val = 1}); + CHECK(validate(&om), true); CHECK(ins != NULL, true); - CHECK(validate(&s), true); - CHECK(size(&s), 2); ins = ccc_entry_unwrap( - om_insert_or_assign_w(&s, 3, (struct val){.val = 99})); - CHECK(validate(&s), true); + om_insert_or_assign_w(&om, 3, (struct val){.val = 99})); + CHECK(validate(&om), true); CHECK(ins == NULL, false); - CHECK(validate(&s), true); + CHECK(validate(&om), true); CHECK(ins->val, 99); - CHECK(size(&s), 3); + CHECK(size(&om), 3); ins = ccc_entry_unwrap( - om_insert_or_assign_w(&s, 3, (struct val){.val = 98})); - CHECK(validate(&s), true); + om_insert_or_assign_w(&om, 3, (struct val){.val = 98})); + CHECK(validate(&om), true); CHECK(ins == NULL, false); CHECK(ins->val, 98); - CHECK(size(&s), 3); - ins = ccc_entry_unwrap(om_try_insert_w(&s, 3, (struct val){.val = 100})); + CHECK(size(&om), 3); + ins = ccc_entry_unwrap(om_try_insert_w(&om, 3, (struct val){.val = 100})); CHECK(ins == NULL, false); - CHECK(validate(&s), true); + CHECK(validate(&om), true); CHECK(ins->val, 98); - CHECK(size(&s), 3); - ins = ccc_entry_unwrap(om_try_insert_w(&s, 4, (struct val){.val = 100})); + CHECK(size(&om), 3); + ins = ccc_entry_unwrap(om_try_insert_w(&om, 4, (struct val){.val = 100})); CHECK(ins == NULL, false); - CHECK(validate(&s), true); + CHECK(validate(&om), true); CHECK(ins->val, 100); - CHECK(size(&s), 4); - CHECK_END_FN((void)ccc_om_clear(&s, NULL);); -} - -CHECK_BEGIN_STATIC_FN(omap_test_struct_getter) -{ - ccc_ordered_map s - = ccc_om_init(s, struct val, elem, key, NULL, id_cmp, NULL); - ccc_ordered_map map_tester_clone = ccc_om_init( - map_tester_clone, struct val, elem, key, NULL, id_cmp, NULL); - struct val vals[10]; - struct val tester_clone[10]; - for (int i = 0; i < 10; ++i) - { - vals[i].key = i; - tester_clone[i].key = i; - CHECK(insert_entry(entry_r(&s, &vals[i].key), &vals[i].elem) != NULL, - true); - CHECK(insert_entry(entry_r(&map_tester_clone, &tester_clone[i].key), - &tester_clone[i].elem) - != NULL, - true); - CHECK(validate(&s), true); - /* Because the getter returns a pointer, if the casting returned - misaligned data and we overwrote something we need to compare our - get to uncorrupted data. */ - struct val const *get = &tester_clone[i]; - CHECK(get->key, vals[i].key); - } - CHECK(size(&s), (size_t)10); + CHECK(size(&om), 4); + CHECK_END_FN(ccc_om_clear(&om, NULL);); +} + +CHECK_BEGIN_STATIC_FN(omap_test_insert_overwrite) +{ + ccc_ordered_map om = om_init(om, struct val, elem, key, NULL, id_cmp, NULL); + + struct val q = {.key = 137, .val = 99}; + ccc_entry ent = insert(&om, &q.elem, &(struct val){}.elem); + CHECK(occupied(&ent), false); + CHECK(unwrap(&ent), NULL); + + struct val const *v = unwrap(entry_r(&om, &q.key)); + CHECK(v != NULL, true); + CHECK(v->val, 99); + + /* Now the second insertion will take place and the old occupying value + will be written into our struct we used to make the query. */ + struct val r = (struct val){.key = 137, .val = 100}; + + /* The contents of q are now in the table. */ + ccc_entry old_ent = insert(&om, &r.elem, &(struct val){}.elem); + CHECK(occupied(&old_ent), true); + + /* The old contents are now in q and the entry is in the table. */ + v = unwrap(&old_ent); + CHECK(v != NULL, true); + CHECK(v->val, 99); + CHECK(r.val, 99); + v = unwrap(entry_r(&om, &r.key)); + CHECK(v != NULL, true); + CHECK(v->val, 100); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(omap_test_insert_then_bad_ideas) +{ + ccc_ordered_map om = om_init(om, struct val, elem, key, NULL, id_cmp, NULL); + struct val q = {.key = 137, .val = 99}; + ccc_entry ent = insert(&om, &q.elem, &(struct val){}.elem); + CHECK(occupied(&ent), false); + CHECK(unwrap(&ent), NULL); + struct val const *v = unwrap(entry_r(&om, &q.key)); + CHECK(v != NULL, true); + CHECK(v->val, 99); + + struct val r = (struct val){.key = 137, .val = 100}; + + ent = insert(&om, &r.elem, &(struct val){}.elem); + CHECK(occupied(&ent), true); + v = unwrap(&ent); + CHECK(v != NULL, true); + CHECK(v->val, 99); + CHECK(r.val, 99); + r.val -= 9; + + v = get_key_val(&om, &q.key); + CHECK(v != NULL, true); + CHECK(v->val, 100); + CHECK(r.val, 90); CHECK_END_FN(); } +CHECK_BEGIN_STATIC_FN(omap_test_entry_api_functional) +{ + /* Over allocate size now because we don't want to worry about resizing. */ + ccc_ordered_map om + = om_init(om, struct val, elem, key, std_alloc, id_cmp, NULL); + size_t const size = 200; + + /* Test entry or insert with for all even values. Default should be + inserted. All entries are hashed to last digit so many spread out + collisions. */ + struct val def = {0}; + for (size_t i = 0; i < size / 2; i += 2) + { + def.key = (int)i; + def.val = (int)i; + struct val const *const d + = or_insert(entry_r(&om, &def.key), &def.elem); + CHECK((d != NULL), true); + CHECK(d->key, i); + CHECK(d->val, i); + } + CHECK(size(&om), (size / 2) / 2); + /* The default insertion should not occur every other element. */ + for (size_t i = 0; i < size / 2; ++i) + { + def.key = (int)i; + def.val = (int)i; + struct val const *const d + = or_insert(om_and_modify_w(entry_r(&om, &def.key), struct val, + { + T->val++; + }), + &def.elem); + /* All values in the array should be odd now */ + CHECK((d != NULL), true); + CHECK(d->key, i); + if (i % 2) + { + CHECK(d->val, i); + } + else + { + CHECK(d->val, i + 1); + } + CHECK(d->val % 2, true); + } + CHECK(size(&om), (size / 2)); + /* More simply modifications don't require the and modify function. All + should be switched back to even now. */ + for (size_t i = 0; i < size / 2; ++i) + { + def.key = (int)i; + def.val = (int)i; + struct val *const in = or_insert(entry_r(&om, &def.key), &def.elem); + in->val++; + /* All values in the array should be odd now */ + CHECK((in->val % 2 == 0), true); + } + CHECK(size(&om), (size / 2)); + CHECK_END_FN(om_clear(&om, NULL);); +} + +CHECK_BEGIN_STATIC_FN(omap_test_insert_via_entry) +{ + /* Over allocate size now because we don't want to worry about resizing. */ + size_t const size = 200; + ccc_ordered_map om + = om_init(om, struct val, elem, key, std_alloc, id_cmp, NULL); + + /* Test entry or insert with for all even values. Default should be + inserted. All entries are hashed to last digit so many spread out + collisions. */ + struct val def = {}; + for (size_t i = 0; i < size / 2; i += 2) + { + def.key = (int)i; + def.val = (int)i; + struct val const *const d + = insert_entry(entry_r(&om, &def.key), &def.elem); + CHECK((d != NULL), true); + CHECK(d->key, i); + CHECK(d->val, i); + } + CHECK(size(&om), (size / 2) / 2); + /* The default insertion should not occur every other element. */ + for (size_t i = 0; i < size / 2; ++i) + { + def.key = (int)i; + def.val = (int)i + 1; + struct val const *const d + = insert_entry(entry_r(&om, &def.key), &def.elem); + /* All values in the array should be odd now */ + CHECK((d != NULL), true); + CHECK(d->val, i + 1); + if (i % 2) + { + CHECK(d->val % 2 == 0, true); + } + else + { + CHECK(d->val % 2, true); + } + } + CHECK(size(&om), (size / 2)); + CHECK_END_FN(om_clear(&om, NULL);); +} + +CHECK_BEGIN_STATIC_FN(omap_test_insert_via_entry_macros) +{ + /* Over allocate size now because we don't want to worry about resizing. */ + size_t const size = 200; + ccc_ordered_map om + = om_init(om, struct val, elem, key, std_alloc, id_cmp, NULL); + + /* Test entry or insert with for all even values. Default should be + inserted. All entries are hashed to last digit so many spread out + collisions. */ + for (size_t i = 0; i < size / 2; i += 2) + { + struct val const *const d + = insert_entry(entry_r(&om, &i), &(struct val){i, i, {}}.elem); + CHECK((d != NULL), true); + CHECK(d->key, i); + CHECK(d->val, i); + } + CHECK(size(&om), (size / 2) / 2); + /* The default insertion should not occur every other element. */ + for (size_t i = 0; i < size / 2; ++i) + { + struct val const *const d + = insert_entry(entry_r(&om, &i), &(struct val){i, i + 1, {}}.elem); + /* All values in the array should be odd now */ + CHECK((d != NULL), true); + CHECK(d->val, i + 1); + if (i % 2) + { + CHECK(d->val % 2 == 0, true); + } + else + { + CHECK(d->val % 2, true); + } + } + CHECK(size(&om), (size / 2)); + CHECK_END_FN(om_clear(&om, NULL);); +} + +CHECK_BEGIN_STATIC_FN(omap_test_entry_api_macros) +{ + /* Over allocate size now because we don't want to worry about resizing. */ + int const size = 200; + ccc_ordered_map om + = om_init(om, struct val, elem, key, std_alloc, id_cmp, NULL); + + /* Test entry or insert with for all even values. Default should be + inserted. All entries are hashed to last digit so many spread out + collisions. */ + for (int i = 0; i < size / 2; i += 2) + { + /* The macros support functions that will only execute if the or + insert branch executes. */ + struct val const *const d + = om_or_insert_w(entry_r(&om, &i), omap_create(i, i)); + CHECK((d != NULL), true); + CHECK(d->key, i); + CHECK(d->val, i); + } + CHECK(size(&om), (size / 2) / 2); + /* The default insertion should not occur every other element. */ + for (int i = 0; i < size / 2; ++i) + { + struct val const *const d = om_or_insert_w( + and_modify(entry_r(&om, &i), omap_modplus), omap_create(i, i)); + /* All values in the array should be odd now */ + CHECK((d != NULL), true); + CHECK(d->key, i); + if (i % 2) + { + CHECK(d->val, i); + } + else + { + CHECK(d->val, i + 1); + } + CHECK(d->val % 2, true); + } + CHECK(size(&om), (size / 2)); + /* More simply modifications don't require the and modify function. All + should be switched back to even now. */ + for (int i = 0; i < size / 2; ++i) + { + struct val *v = om_or_insert_w(entry_r(&om, &i), (struct val){}); + CHECK(v != NULL, true); + v->val++; + /* All values in the array should be odd now */ + CHECK(v->val % 2 == 0, true); + } + CHECK(size(&om), (size / 2)); + CHECK_END_FN(om_clear(&om, NULL);); +} + +CHECK_BEGIN_STATIC_FN(omap_test_two_sum) +{ + ccc_ordered_map om + = om_init(om, struct val, elem, key, std_alloc, id_cmp, NULL); + int const addends[10] = {1, 3, -980, 6, 7, 13, 44, 32, 995, -1}; + int const target = 15; + int solution_indices[2] = {-1, -1}; + for (size_t i = 0; i < (sizeof(addends) / sizeof(addends[0])); ++i) + { + struct val const *const other_addend + = get_key_val(&om, &(int){target - addends[i]}); + if (other_addend) + { + solution_indices[0] = (int)i; + solution_indices[1] = other_addend->val; + break; + } + ccc_entry const e = insert_or_assign( + &om, &(struct val){.key = addends[i], .val = i}.elem); + CHECK(insert_error(&e), false); + } + CHECK(solution_indices[0], 8); + CHECK(solution_indices[1], 2); + CHECK_END_FN(om_clear(&om, NULL);); +} + +CHECK_BEGIN_STATIC_FN(omap_test_resize) +{ + ccc_ordered_map om + = om_init(om, struct val, elem, key, std_alloc, id_cmp, NULL); + + int const to_insert = 1000; + int const larger_prime = 1009; + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val elem = {.key = shuffled_index, .val = i}; + struct val *v = insert_entry(entry_r(&om, &elem.key), &elem.elem); + CHECK(v != NULL, true); + CHECK(v->key, shuffled_index); + CHECK(v->val, i); + CHECK(validate(&om), true); + } + CHECK(size(&om), to_insert); + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val swap_slot = {shuffled_index, shuffled_index, {}}; + struct val const *const in_table + = insert_entry(entry_r(&om, &swap_slot.key), &swap_slot.elem); + CHECK(in_table != NULL, true); + CHECK(in_table->val, shuffled_index); + } + CHECK(om_clear(&om, NULL), CCC_OK); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(omap_test_resize_macros) +{ + ccc_ordered_map om + = om_init(om, struct val, elem, key, std_alloc, id_cmp, NULL); + int const to_insert = 1000; + int const larger_prime = 1009; + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val *v = insert_entry(entry_r(&om, &shuffled_index), + &(struct val){shuffled_index, i, {}}.elem); + CHECK(v != NULL, true); + CHECK(v->key, shuffled_index); + CHECK(v->val, i); + } + CHECK(size(&om), to_insert); + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val const *const in_table = om_or_insert_w( + om_and_modify_w(entry_r(&om, &shuffled_index), struct val, + { + T->val = shuffled_index; + }), + (struct val){}); + CHECK(in_table != NULL, true); + CHECK(in_table->val, shuffled_index); + struct val *v + = om_or_insert_w(entry_r(&om, &shuffled_index), (struct val){}); + CHECK(v == NULL, false); + v->val = i; + v = get_key_val(&om, &shuffled_index); + CHECK(v != NULL, true); + CHECK(v->val, i); + } + CHECK(om_clear(&om, NULL), CCC_OK); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(omap_test_resize_fom_null) +{ + ccc_ordered_map om + = om_init(om, struct val, elem, key, std_alloc, id_cmp, NULL); + int const to_insert = 1000; + int const larger_prime = 1009; + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val elem = {.key = shuffled_index, .val = i}; + struct val *v = insert_entry(entry_r(&om, &elem.key), &elem.elem); + CHECK(v != NULL, true); + CHECK(v->key, shuffled_index); + CHECK(v->val, i); + } + CHECK(size(&om), to_insert); + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val swap_slot = {shuffled_index, shuffled_index, {}}; + struct val const *const in_table + = insert_entry(entry_r(&om, &swap_slot.key), &swap_slot.elem); + CHECK(in_table != NULL, true); + CHECK(in_table->val, shuffled_index); + } + CHECK(om_clear(&om, NULL), CCC_OK); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(omap_test_resize_fom_null_macros) +{ + ccc_ordered_map om + = om_init(om, struct val, elem, key, std_alloc, id_cmp, NULL); + int const to_insert = 1000; + int const larger_prime = 1009; + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val *v = insert_entry(entry_r(&om, &shuffled_index), + &(struct val){shuffled_index, i, {}}.elem); + CHECK(v != NULL, true); + CHECK(v->key, shuffled_index); + CHECK(v->val, i); + } + CHECK(size(&om), to_insert); + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val const *const in_table = om_or_insert_w( + om_and_modify_w(entry_r(&om, &shuffled_index), struct val, + { + T->val = shuffled_index; + }), + (struct val){}); + CHECK(in_table != NULL, true); + CHECK(in_table->val, shuffled_index); + struct val *v + = om_or_insert_w(entry_r(&om, &shuffled_index), (struct val){}); + CHECK(v == NULL, false); + v->val = i; + v = get_key_val(&om, &shuffled_index); + CHECK(v == NULL, false); + CHECK(v->val, i); + } + CHECK(om_clear(&om, NULL), CCC_OK); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(omap_test_insert_and_find) +{ + int const size = 101; + ccc_ordered_map om + = om_init(om, struct val, elem, key, std_alloc, id_cmp, NULL); + + for (int i = 0; i < size; i += 2) + { + ccc_entry e = try_insert(&om, &(struct val){.key = i, .val = i}.elem); + CHECK(occupied(&e), false); + CHECK(validate(&om), true); + e = try_insert(&om, &(struct val){.key = i, .val = i}.elem); + CHECK(occupied(&e), true); + CHECK(validate(&om), true); + struct val const *const v = unwrap(&e); + CHECK(v == NULL, false); + CHECK(v->key, i); + CHECK(v->val, i); + } + for (int i = 0; i < size; i += 2) + { + CHECK(contains(&om, &i), true); + CHECK(occupied(entry_r(&om, &i)), true); + CHECK(validate(&om), true); + } + for (int i = 1; i < size; i += 2) + { + CHECK(contains(&om, &i), false); + CHECK(occupied(entry_r(&om, &i)), false); + CHECK(validate(&om), true); + } + CHECK_END_FN(om_clear(&om, NULL);); +} + CHECK_BEGIN_STATIC_FN(omap_test_insert_shuffle) { - ccc_ordered_map s - = ccc_om_init(s, struct val, elem, key, NULL, id_cmp, NULL); - /* Math magic ahead... */ size_t const size = 50; + ccc_ordered_map om = om_init(om, struct val, elem, key, NULL, id_cmp, NULL); + struct val vals[50] = {}; + CHECK(size > 1, true); int const prime = 53; - struct val vals[50]; - CHECK(insert_shuffled(&s, vals, size, prime), PASS); + CHECK(insert_shuffled(&om, vals, size, prime), PASS); int sorted_check[50]; - CHECK(inorder_fill(sorted_check, size, &s), size); - for (size_t i = 0; i < size; ++i) + CHECK(inorder_fill(sorted_check, size, &om), size); + for (size_t i = 1; i < size; ++i) { - CHECK(vals[i].key, sorted_check[i]); + CHECK(sorted_check[i - 1] <= sorted_check[i], true); } CHECK_END_FN(); } +CHECK_BEGIN_STATIC_FN(omap_test_insert_weak_srand) +{ + int const num_nodes = 1000; + ccc_ordered_map om + = om_init(om, struct val, elem, key, std_alloc, id_cmp, NULL); + srand(time(NULL)); /* NOLINT */ + for (int i = 0; i < num_nodes; ++i) + { + ccc_entry const e = insert( + &om, &(struct val){.key = rand() /* NOLINT */, .val = i}.elem, + &(struct val){}.elem); + CHECK(insert_error(&e), false); + CHECK(validate(&om), true); + } + CHECK(size(&om), (size_t)num_nodes); + CHECK_END_FN(om_clear(&om, NULL);); +} + int main() { - return CHECK_RUN(omap_test_insert_one(), omap_test_insert_three(), - omap_test_insert_macros(), omap_test_struct_getter(), - omap_test_insert_shuffle()); + return CHECK_RUN( + omap_test_insert(), omap_test_insert_macros(), + omap_test_insert_and_find(), omap_test_insert_overwrite(), + omap_test_insert_then_bad_ideas(), omap_test_insert_via_entry(), + omap_test_insert_via_entry_macros(), omap_test_entry_api_functional(), + omap_test_entry_api_macros(), omap_test_two_sum(), omap_test_resize(), + omap_test_resize_macros(), omap_test_resize_fom_null(), + omap_test_resize_fom_null_macros(), omap_test_insert_weak_srand(), + omap_test_insert_shuffle()); } diff --git a/tests/ommap/test_ommap_insert.c b/tests/ommap/test_ommap_insert.c index 13f25f0d..79c2c333 100644 --- a/tests/ommap/test_ommap_insert.c +++ b/tests/ommap/test_ommap_insert.c @@ -170,11 +170,45 @@ CHECK_BEGIN_STATIC_FN(ommap_test_read_max_min) CHECK_END_FN(); } +CHECK_BEGIN_STATIC_FN(ommap_test_insert_and_find) +{ + int const size = 100; + ordered_multimap s = omm_init(s, struct val, elem, key, NULL, id_cmp, NULL); + struct val vals[101]; + for (int i = 0, curval = 0; i < size; i += 2, ++curval) + { + vals[curval] = (struct val){.key = i, .val = i}; + ccc_entry e = try_insert(&s, &vals[curval].elem); + CHECK(occupied(&e), false); + CHECK(validate(&s), true); + e = try_insert(&s, &vals[curval].elem); + CHECK(occupied(&e), true); + CHECK(validate(&s), true); + struct val const *const v = unwrap(&e); + CHECK(v == NULL, false); + CHECK(v->key, i); + CHECK(v->val, i); + } + for (int i = 0; i < size; i += 2) + { + CHECK(contains(&s, &i), true); + CHECK(occupied(entry_r(&s, &i)), true); + CHECK(validate(&s), true); + } + for (int i = 1; i < size; i += 2) + { + CHECK(contains(&s, &i), false); + CHECK(occupied(entry_r(&s, &i)), false); + CHECK(validate(&s), true); + } + CHECK_END_FN(); +} + int main() { return CHECK_RUN(ommap_test_insert_one(), ommap_test_insert_three(), - ommap_test_insert_macros(), ommap_test_struct_getter(), - ommap_test_insert_three_dups(), + ommap_test_insert_and_find(), ommap_test_insert_macros(), + ommap_test_struct_getter(), ommap_test_insert_three_dups(), ommap_test_insert_shuffle(), ommap_test_read_max_min()); } diff --git a/tests/romap/test_romap_insert.c b/tests/romap/test_romap_insert.c index a9b308b4..bdd35732 100644 --- a/tests/romap/test_romap_insert.c +++ b/tests/romap/test_romap_insert.c @@ -14,98 +14,583 @@ #include #include -CHECK_BEGIN_STATIC_FN(romap_test_insert_one) +static inline struct val +romap_create(int const id, int const val) { - struct val one = {}; - ccc_realtime_ordered_map s - = rom_init(s, struct val, elem, key, NULL, id_cmp, NULL); - CHECK(occupied(insert_r(&s, &one.elem, &one.elem)), false); - CHECK(is_empty(&s), false); + return (struct val){.key = id, .val = val}; +} + +static inline void +romap_modplus(ccc_user_type const t) +{ + ((struct val *)t.user_type)->val++; +} + +CHECK_BEGIN_STATIC_FN(romap_test_insert) +{ + ccc_realtime_ordered_map rom + = rom_init(rom, struct val, elem, key, NULL, id_cmp, NULL); + + /* Nothing was there before so nothing is in the entry. */ + ccc_entry ent = insert(&rom, &(struct val){.key = 137, .val = 99}.elem, + &(struct val){}.elem); + CHECK(occupied(&ent), false); + CHECK(unwrap(&ent), NULL); + CHECK(size(&rom), 1); CHECK_END_FN(); } CHECK_BEGIN_STATIC_FN(romap_test_insert_macros) { - ccc_realtime_ordered_map s - = rom_init(s, struct val, elem, key, std_alloc, id_cmp, NULL); - struct val *v = rom_or_insert_w(entry_r(&s, &(int){0}), (struct val){}); + ccc_realtime_ordered_map rom + = rom_init(rom, struct val, elem, key, std_alloc, id_cmp, NULL); + + struct val const *ins = ccc_rom_or_insert_w( + entry_r(&rom, &(int){2}), (struct val){.key = 2, .val = 0}); + CHECK(ins != NULL, true); + CHECK(validate(&rom), true); + CHECK(size(&rom), 1); + ins = rom_insert_entry_w(entry_r(&rom, &(int){2}), + (struct val){.key = 2, .val = 0}); + CHECK(validate(&rom), true); + CHECK(ins != NULL, true); + ins = rom_insert_entry_w(entry_r(&rom, &(int){9}), + (struct val){.key = 9, .val = 1}); + CHECK(validate(&rom), true); + CHECK(ins != NULL, true); + ins = ccc_entry_unwrap( + rom_insert_or_assign_w(&rom, 3, (struct val){.val = 99})); + CHECK(validate(&rom), true); + CHECK(ins == NULL, false); + CHECK(validate(&rom), true); + CHECK(ins->val, 99); + CHECK(size(&rom), 3); + ins = ccc_entry_unwrap( + rom_insert_or_assign_w(&rom, 3, (struct val){.val = 98})); + CHECK(validate(&rom), true); + CHECK(ins == NULL, false); + CHECK(ins->val, 98); + CHECK(size(&rom), 3); + ins = ccc_entry_unwrap(rom_try_insert_w(&rom, 3, (struct val){.val = 100})); + CHECK(ins == NULL, false); + CHECK(validate(&rom), true); + CHECK(ins->val, 98); + CHECK(size(&rom), 3); + ins = ccc_entry_unwrap(rom_try_insert_w(&rom, 4, (struct val){.val = 100})); + CHECK(ins == NULL, false); + CHECK(validate(&rom), true); + CHECK(ins->val, 100); + CHECK(size(&rom), 4); + CHECK_END_FN(ccc_rom_clear(&rom, NULL);); +} + +CHECK_BEGIN_STATIC_FN(romap_test_insert_overwrite) +{ + ccc_realtime_ordered_map rom + = rom_init(rom, struct val, elem, key, NULL, id_cmp, NULL); + + struct val q = {.key = 137, .val = 99}; + ccc_entry ent = insert(&rom, &q.elem, &(struct val){}.elem); + CHECK(occupied(&ent), false); + CHECK(unwrap(&ent), NULL); + + struct val const *v = unwrap(entry_r(&rom, &q.key)); CHECK(v != NULL, true); - v = rom_insert_entry_w(entry_r(&s, &(int){0}), - (struct val){.val = 99, .key = 0}); - CHECK(validate(&s), true); - CHECK(v == NULL, false); CHECK(v->val, 99); - v = rom_insert_entry_w(entry_r(&s, &(int){9}), - (struct val){.val = 100, .key = 9}); + + /* Now the second insertion will take place and the old occupying value + will be written into our struct we used to make the query. */ + struct val r = (struct val){.key = 137, .val = 100}; + + /* The contents of q are now in the table. */ + ccc_entry old_ent = insert(&rom, &r.elem, &(struct val){}.elem); + CHECK(occupied(&old_ent), true); + + /* The old contents are now in q and the entry is in the table. */ + v = unwrap(&old_ent); CHECK(v != NULL, true); - CHECK(v->val, 100); - v = unwrap(rom_insert_or_assign_w(&s, 1, (struct val){.val = 100})); - CHECK(validate(&s), true); + CHECK(v->val, 99); + CHECK(r.val, 99); + v = unwrap(entry_r(&rom, &r.key)); CHECK(v != NULL, true); CHECK(v->val, 100); - CHECK(size(&s), 3); - v = unwrap(rom_insert_or_assign_w(&s, 1, (struct val){.val = 99})); - CHECK(validate(&s), true); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(romap_test_insert_then_bad_ideas) +{ + ccc_realtime_ordered_map rom + = rom_init(rom, struct val, elem, key, NULL, id_cmp, NULL); + struct val q = {.key = 137, .val = 99}; + ccc_entry ent = insert(&rom, &q.elem, &(struct val){}.elem); + CHECK(occupied(&ent), false); + CHECK(unwrap(&ent), NULL); + struct val const *v = unwrap(entry_r(&rom, &q.key)); CHECK(v != NULL, true); CHECK(v->val, 99); - CHECK(size(&s), 3); - v = unwrap(rom_try_insert_w(&s, 1, (struct val){.val = 2})); - CHECK(validate(&s), true); + + struct val r = (struct val){.key = 137, .val = 100}; + + ent = insert(&rom, &r.elem, &(struct val){}.elem); + CHECK(occupied(&ent), true); + v = unwrap(&ent); CHECK(v != NULL, true); CHECK(v->val, 99); - CHECK(size(&s), 3); - v = unwrap(rom_try_insert_w(&s, 2, (struct val){.val = 2})); - CHECK(validate(&s), true); + CHECK(r.val, 99); + r.val -= 9; + + v = get_key_val(&rom, &q.key); CHECK(v != NULL, true); - CHECK(v->val, 2); - CHECK(size(&s), 4); - CHECK_END_FN(rom_clear(&s, NULL);); + CHECK(v->val, 100); + CHECK(r.val, 90); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(romap_test_entry_api_functional) +{ + /* Over allocate size now because we don't want to worry about resizing. */ + ccc_realtime_ordered_map rom + = rom_init(rom, struct val, elem, key, std_alloc, id_cmp, NULL); + size_t const size = 200; + + /* Test entry or insert with for all even values. Default should be + inserted. All entries are hashed to last digit so many spread out + collisions. */ + struct val def = {0}; + for (size_t i = 0; i < size / 2; i += 2) + { + def.key = (int)i; + def.val = (int)i; + struct val const *const d + = or_insert(entry_r(&rom, &def.key), &def.elem); + CHECK((d != NULL), true); + CHECK(d->key, i); + CHECK(d->val, i); + } + CHECK(size(&rom), (size / 2) / 2); + /* The default insertion should not occur every other element. */ + for (size_t i = 0; i < size / 2; ++i) + { + def.key = (int)i; + def.val = (int)i; + struct val const *const d + = or_insert(rom_and_modify_w(entry_r(&rom, &def.key), struct val, + { + T->val++; + }), + &def.elem); + /* All values in the array should be odd now */ + CHECK((d != NULL), true); + CHECK(d->key, i); + if (i % 2) + { + CHECK(d->val, i); + } + else + { + CHECK(d->val, i + 1); + } + CHECK(d->val % 2, true); + } + CHECK(size(&rom), (size / 2)); + /* More simply modifications don't require the and modify function. All + should be switched back to even now. */ + for (size_t i = 0; i < size / 2; ++i) + { + def.key = (int)i; + def.val = (int)i; + struct val *const in = or_insert(entry_r(&rom, &def.key), &def.elem); + in->val++; + /* All values in the array should be odd now */ + CHECK((in->val % 2 == 0), true); + } + CHECK(size(&rom), (size / 2)); + CHECK_END_FN(rom_clear(&rom, NULL);); +} + +CHECK_BEGIN_STATIC_FN(romap_test_insert_via_entry) +{ + /* Over allocate size now because we don't want to worry about resizing. */ + size_t const size = 200; + ccc_realtime_ordered_map rom + = rom_init(rom, struct val, elem, key, std_alloc, id_cmp, NULL); + + /* Test entry or insert with for all even values. Default should be + inserted. All entries are hashed to last digit so many spread out + collisions. */ + struct val def = {}; + for (size_t i = 0; i < size / 2; i += 2) + { + def.key = (int)i; + def.val = (int)i; + struct val const *const d + = insert_entry(entry_r(&rom, &def.key), &def.elem); + CHECK((d != NULL), true); + CHECK(d->key, i); + CHECK(d->val, i); + } + CHECK(size(&rom), (size / 2) / 2); + /* The default insertion should not occur every other element. */ + for (size_t i = 0; i < size / 2; ++i) + { + def.key = (int)i; + def.val = (int)i + 1; + struct val const *const d + = insert_entry(entry_r(&rom, &def.key), &def.elem); + /* All values in the array should be odd now */ + CHECK((d != NULL), true); + CHECK(d->val, i + 1); + if (i % 2) + { + CHECK(d->val % 2 == 0, true); + } + else + { + CHECK(d->val % 2, true); + } + } + CHECK(size(&rom), (size / 2)); + CHECK_END_FN(rom_clear(&rom, NULL);); +} + +CHECK_BEGIN_STATIC_FN(romap_test_insert_via_entry_macros) +{ + /* Over allocate size now because we don't want to worry about resizing. */ + size_t const size = 200; + ccc_realtime_ordered_map rom + = rom_init(rom, struct val, elem, key, std_alloc, id_cmp, NULL); + + /* Test entry or insert with for all even values. Default should be + inserted. All entries are hashed to last digit so many spread out + collisions. */ + for (size_t i = 0; i < size / 2; i += 2) + { + struct val const *const d + = insert_entry(entry_r(&rom, &i), &(struct val){i, i, {}}.elem); + CHECK((d != NULL), true); + CHECK(d->key, i); + CHECK(d->val, i); + } + CHECK(size(&rom), (size / 2) / 2); + /* The default insertion should not occur every other element. */ + for (size_t i = 0; i < size / 2; ++i) + { + struct val const *const d + = insert_entry(entry_r(&rom, &i), &(struct val){i, i + 1, {}}.elem); + /* All values in the array should be odd now */ + CHECK((d != NULL), true); + CHECK(d->val, i + 1); + if (i % 2) + { + CHECK(d->val % 2 == 0, true); + } + else + { + CHECK(d->val % 2, true); + } + } + CHECK(size(&rom), (size / 2)); + CHECK_END_FN(rom_clear(&rom, NULL);); +} + +CHECK_BEGIN_STATIC_FN(romap_test_entry_api_macros) +{ + /* Over allocate size now because we don't want to worry about resizing. */ + int const size = 200; + ccc_realtime_ordered_map rom + = rom_init(rom, struct val, elem, key, std_alloc, id_cmp, NULL); + + /* Test entry or insert with for all even values. Default should be + inserted. All entries are hashed to last digit so many spread out + collisions. */ + for (int i = 0; i < size / 2; i += 2) + { + /* The macros support functions that will only execute if the or + insert branch executes. */ + struct val const *const d + = rom_or_insert_w(entry_r(&rom, &i), romap_create(i, i)); + CHECK((d != NULL), true); + CHECK(d->key, i); + CHECK(d->val, i); + } + CHECK(size(&rom), (size / 2) / 2); + /* The default insertion should not occur every other element. */ + for (int i = 0; i < size / 2; ++i) + { + struct val const *const d = rom_or_insert_w( + and_modify(entry_r(&rom, &i), romap_modplus), romap_create(i, i)); + /* All values in the array should be odd now */ + CHECK((d != NULL), true); + CHECK(d->key, i); + if (i % 2) + { + CHECK(d->val, i); + } + else + { + CHECK(d->val, i + 1); + } + CHECK(d->val % 2, true); + } + CHECK(size(&rom), (size / 2)); + /* More simply modifications don't require the and modify function. All + should be switched back to even now. */ + for (int i = 0; i < size / 2; ++i) + { + struct val *v = rom_or_insert_w(entry_r(&rom, &i), (struct val){}); + CHECK(v != NULL, true); + v->val++; + /* All values in the array should be odd now */ + CHECK(v->val % 2 == 0, true); + } + CHECK(size(&rom), (size / 2)); + CHECK_END_FN(rom_clear(&rom, NULL);); +} + +CHECK_BEGIN_STATIC_FN(romap_test_two_sum) +{ + ccc_realtime_ordered_map rom + = rom_init(rom, struct val, elem, key, std_alloc, id_cmp, NULL); + int const addends[10] = {1, 3, -980, 6, 7, 13, 44, 32, 995, -1}; + int const target = 15; + int solution_indices[2] = {-1, -1}; + for (size_t i = 0; i < (sizeof(addends) / sizeof(addends[0])); ++i) + { + struct val const *const other_addend + = get_key_val(&rom, &(int){target - addends[i]}); + if (other_addend) + { + solution_indices[0] = (int)i; + solution_indices[1] = other_addend->val; + break; + } + ccc_entry const e = insert_or_assign( + &rom, &(struct val){.key = addends[i], .val = i}.elem); + CHECK(insert_error(&e), false); + } + CHECK(solution_indices[0], 8); + CHECK(solution_indices[1], 2); + CHECK_END_FN(rom_clear(&rom, NULL);); +} + +CHECK_BEGIN_STATIC_FN(romap_test_resize) +{ + ccc_realtime_ordered_map rom + = rom_init(rom, struct val, elem, key, std_alloc, id_cmp, NULL); + + int const to_insert = 1000; + int const larger_prime = 1009; + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val elem = {.key = shuffled_index, .val = i}; + struct val *v = insert_entry(entry_r(&rom, &elem.key), &elem.elem); + CHECK(v != NULL, true); + CHECK(v->key, shuffled_index); + CHECK(v->val, i); + CHECK(validate(&rom), true); + } + CHECK(size(&rom), to_insert); + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val swap_slot = {shuffled_index, shuffled_index, {}}; + struct val const *const in_table + = insert_entry(entry_r(&rom, &swap_slot.key), &swap_slot.elem); + CHECK(in_table != NULL, true); + CHECK(in_table->val, shuffled_index); + } + CHECK(rom_clear(&rom, NULL), CCC_OK); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(romap_test_resize_macros) +{ + ccc_realtime_ordered_map rom + = rom_init(rom, struct val, elem, key, std_alloc, id_cmp, NULL); + int const to_insert = 1000; + int const larger_prime = 1009; + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val *v = insert_entry(entry_r(&rom, &shuffled_index), + &(struct val){shuffled_index, i, {}}.elem); + CHECK(v != NULL, true); + CHECK(v->key, shuffled_index); + CHECK(v->val, i); + } + CHECK(size(&rom), to_insert); + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val const *const in_table = rom_or_insert_w( + rom_and_modify_w(entry_r(&rom, &shuffled_index), struct val, + { + T->val = shuffled_index; + }), + (struct val){}); + CHECK(in_table != NULL, true); + CHECK(in_table->val, shuffled_index); + struct val *v + = rom_or_insert_w(entry_r(&rom, &shuffled_index), (struct val){}); + CHECK(v == NULL, false); + v->val = i; + v = get_key_val(&rom, &shuffled_index); + CHECK(v != NULL, true); + CHECK(v->val, i); + } + CHECK(rom_clear(&rom, NULL), CCC_OK); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(romap_test_resize_from_null) +{ + ccc_realtime_ordered_map rom + = rom_init(rom, struct val, elem, key, std_alloc, id_cmp, NULL); + int const to_insert = 1000; + int const larger_prime = 1009; + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val elem = {.key = shuffled_index, .val = i}; + struct val *v = insert_entry(entry_r(&rom, &elem.key), &elem.elem); + CHECK(v != NULL, true); + CHECK(v->key, shuffled_index); + CHECK(v->val, i); + } + CHECK(size(&rom), to_insert); + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val swap_slot = {shuffled_index, shuffled_index, {}}; + struct val const *const in_table + = insert_entry(entry_r(&rom, &swap_slot.key), &swap_slot.elem); + CHECK(in_table != NULL, true); + CHECK(in_table->val, shuffled_index); + } + CHECK(rom_clear(&rom, NULL), CCC_OK); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(romap_test_resize_from_null_macros) +{ + ccc_realtime_ordered_map rom + = rom_init(rom, struct val, elem, key, std_alloc, id_cmp, NULL); + int const to_insert = 1000; + int const larger_prime = 1009; + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val *v = insert_entry(entry_r(&rom, &shuffled_index), + &(struct val){shuffled_index, i, {}}.elem); + CHECK(v != NULL, true); + CHECK(v->key, shuffled_index); + CHECK(v->val, i); + } + CHECK(size(&rom), to_insert); + for (int i = 0, shuffled_index = larger_prime % to_insert; i < to_insert; + ++i, shuffled_index = (shuffled_index + larger_prime) % to_insert) + { + struct val const *const in_table = rom_or_insert_w( + rom_and_modify_w(entry_r(&rom, &shuffled_index), struct val, + { + T->val = shuffled_index; + }), + (struct val){}); + CHECK(in_table != NULL, true); + CHECK(in_table->val, shuffled_index); + struct val *v + = rom_or_insert_w(entry_r(&rom, &shuffled_index), (struct val){}); + CHECK(v == NULL, false); + v->val = i; + v = get_key_val(&rom, &shuffled_index); + CHECK(v == NULL, false); + CHECK(v->val, i); + } + CHECK(rom_clear(&rom, NULL), CCC_OK); + CHECK_END_FN(); +} + +CHECK_BEGIN_STATIC_FN(romap_test_insert_and_find) +{ + int const size = 101; + ccc_realtime_ordered_map rom + = rom_init(rom, struct val, elem, key, std_alloc, id_cmp, NULL); + + for (int i = 0; i < size; i += 2) + { + ccc_entry e = try_insert(&rom, &(struct val){.key = i, .val = i}.elem); + CHECK(occupied(&e), false); + CHECK(validate(&rom), true); + e = try_insert(&rom, &(struct val){.key = i, .val = i}.elem); + CHECK(occupied(&e), true); + CHECK(validate(&rom), true); + struct val const *const v = unwrap(&e); + CHECK(v == NULL, false); + CHECK(v->key, i); + CHECK(v->val, i); + } + for (int i = 0; i < size; i += 2) + { + CHECK(contains(&rom, &i), true); + CHECK(occupied(entry_r(&rom, &i)), true); + CHECK(validate(&rom), true); + } + for (int i = 1; i < size; i += 2) + { + CHECK(contains(&rom, &i), false); + CHECK(occupied(entry_r(&rom, &i)), false); + CHECK(validate(&rom), true); + } + CHECK_END_FN(rom_clear(&rom, NULL);); } CHECK_BEGIN_STATIC_FN(romap_test_insert_shuffle) { - ccc_realtime_ordered_map s - = rom_init(s, struct val, elem, key, NULL, id_cmp, NULL); - /* Math magic ahead... */ size_t const size = 50; + ccc_realtime_ordered_map rom + = rom_init(rom, struct val, elem, key, NULL, id_cmp, NULL); + struct val vals[50] = {}; + CHECK(size > 1, true); int const prime = 53; - struct val vals[50]; - CHECK(insert_shuffled(&s, vals, size, prime), PASS); - + CHECK(insert_shuffled(&rom, vals, size, prime), PASS); int sorted_check[50]; - CHECK(inorder_fill(sorted_check, size, &s), size); - for (size_t i = 0; i < size; ++i) + CHECK(inorder_fill(sorted_check, size, &rom), size); + for (size_t i = 1; i < size; ++i) { - CHECK(vals[i].key, sorted_check[i]); + CHECK(sorted_check[i - 1] <= sorted_check[i], true); } CHECK_END_FN(); } CHECK_BEGIN_STATIC_FN(romap_test_insert_weak_srand) { - ccc_realtime_ordered_map s - = rom_init(s, struct val, elem, key, NULL, id_cmp, NULL); - /* Seed the test with any integer for reproducible random test sequence - currently this will change every test. NOLINTNEXTLINE */ - srand(time(NULL)); int const num_nodes = 1000; - struct val vals[1000]; + ccc_realtime_ordered_map rom + = rom_init(rom, struct val, elem, key, std_alloc, id_cmp, NULL); + srand(time(NULL)); /* NOLINT */ for (int i = 0; i < num_nodes; ++i) { - vals[i].key = rand(); // NOLINT - vals[i].val = i; - ccc_entry const e = insert(&s, &vals[i].elem, &(struct val){}.elem); + ccc_entry const e = insert( + &rom, &(struct val){.key = rand() /* NOLINT */, .val = i}.elem, + &(struct val){}.elem); CHECK(insert_error(&e), false); - CHECK(validate(&s), true); + CHECK(validate(&rom), true); } - CHECK(size(&s), (size_t)num_nodes); - CHECK_END_FN(); + CHECK(size(&rom), (size_t)num_nodes); + CHECK_END_FN(rom_clear(&rom, NULL);); } int main() { - return CHECK_RUN(romap_test_insert_one(), romap_test_insert_macros(), - romap_test_insert_shuffle(), - romap_test_insert_weak_srand()); + return CHECK_RUN( + romap_test_insert(), romap_test_insert_macros(), + romap_test_insert_and_find(), romap_test_insert_overwrite(), + romap_test_insert_then_bad_ideas(), romap_test_insert_via_entry(), + romap_test_insert_via_entry_macros(), romap_test_entry_api_functional(), + romap_test_entry_api_macros(), romap_test_two_sum(), + romap_test_resize(), romap_test_resize_macros(), + romap_test_resize_from_null(), romap_test_resize_from_null_macros(), + romap_test_insert_weak_srand(), romap_test_insert_shuffle()); }