From 5bc9b6e08b2555f2f1a9fe65db266dc97f4a50ba Mon Sep 17 00:00:00 2001 From: David Robillard Date: Tue, 12 Jan 2021 15:18:49 +0100 Subject: [PATCH] Update zix This fixes, once again, a potential BTree crash with GCC 10 which got lost somewhere along the way. --- NEWS | 6 + src/sord_config.h | 2 +- src/zix/btree.c | 1336 +++++++++++++++++++++++---------------------- src/zix/btree.h | 55 +- src/zix/common.h | 109 ++-- src/zix/digest.c | 120 ++-- src/zix/digest.h | 16 +- src/zix/hash.c | 289 +++++----- src/zix/hash.h | 48 +- wscript | 2 +- 10 files changed, 1004 insertions(+), 979 deletions(-) diff --git a/NEWS b/NEWS index b09bba5..325be60 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,9 @@ +sord (0.16.9) unstable; + + * Fix potential crash or incorrectness issue with GCC 10 again + + -- David Robillard Tue, 12 Jan 2021 14:17:39 +0000 + sord (0.16.8) stable; * Clean up code diff --git a/src/zix/btree.c b/src/zix/btree.c index 3279f6c..7926a56 100644 --- a/src/zix/btree.c +++ b/src/zix/btree.c @@ -25,121 +25,119 @@ // #define ZIX_BTREE_SORTED_CHECK 1 #ifndef ZIX_BTREE_PAGE_SIZE -# define ZIX_BTREE_PAGE_SIZE 4096 +# define ZIX_BTREE_PAGE_SIZE 4096 #endif #define ZIX_BTREE_NODE_SPACE (ZIX_BTREE_PAGE_SIZE - 2 * sizeof(uint16_t)) -#define ZIX_BTREE_LEAF_VALS ((ZIX_BTREE_NODE_SPACE / sizeof(void*)) - 1) +#define ZIX_BTREE_LEAF_VALS ((ZIX_BTREE_NODE_SPACE / sizeof(void*)) - 1) #define ZIX_BTREE_INODE_VALS (ZIX_BTREE_LEAF_VALS / 2) struct ZixBTreeImpl { - ZixBTreeNode* root; - ZixDestroyFunc destroy; - ZixComparator cmp; - const void* cmp_data; - size_t size; - unsigned height; ///< Number of levels, i.e. root only has height 1 + ZixBTreeNode* root; + ZixDestroyFunc destroy; + ZixComparator cmp; + const void* cmp_data; + size_t size; + unsigned height; ///< Number of levels, i.e. root only has height 1 }; struct ZixBTreeNodeImpl { - uint16_t is_leaf; - uint16_t n_vals; - // On 64-bit we rely on some padding here to get page-sized nodes - union { - struct { - void* vals[ZIX_BTREE_LEAF_VALS]; - } leaf; - - struct { - void* vals[ZIX_BTREE_INODE_VALS]; - ZixBTreeNode* children[ZIX_BTREE_INODE_VALS + 1]; - } inode; - } data; + uint16_t is_leaf; + uint16_t n_vals; + // On 64-bit we rely on some padding here to get page-sized nodes + union { + struct { + void* vals[ZIX_BTREE_LEAF_VALS]; + } leaf; + + struct { + void* vals[ZIX_BTREE_INODE_VALS]; + ZixBTreeNode* children[ZIX_BTREE_INODE_VALS + 1]; + } inode; + } data; }; #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112l) || \ - (defined(__cplusplus) && __cplusplus >= 201103L) + (defined(__cplusplus) && __cplusplus >= 201103L) static_assert(sizeof(ZixBTreeNode) == ZIX_BTREE_PAGE_SIZE, ""); #endif typedef struct { - ZixBTreeNode* node; - unsigned index; + ZixBTreeNode* node; + unsigned index; } ZixBTreeIterFrame; struct ZixBTreeIterImpl { - unsigned n_levels; ///< Maximum depth of stack - unsigned level; ///< Current level in stack - ZixBTreeIterFrame stack[]; ///< Position stack + unsigned n_levels; ///< Maximum depth of stack + unsigned level; ///< Current level in stack + ZixBTreeIterFrame stack[]; ///< Position stack }; #ifdef ZIX_BTREE_DEBUG -ZIX_PRIVATE void +static void print_node(const ZixBTreeNode* n, const char* prefix) { - printf("%s[", prefix); - for (uint16_t v = 0; v < n->n_vals; ++v) { - printf(" %lu", (uintptr_t)n->vals[v]); - } - printf(" ]\n"); + printf("%s[", prefix); + for (uint16_t v = 0; v < n->n_vals; ++v) { + printf(" %lu", (uintptr_t)n->vals[v]); + } + printf(" ]\n"); } -ZIX_PRIVATE void +static void print_tree(const ZixBTreeNode* parent, const ZixBTreeNode* node, int level) { - if (node) { - if (!parent) { - printf("TREE {\n"); - } - for (int i = 0; i < level + 1; ++i) { - printf(" "); - } - print_node(node, ""); - if (!node->is_leaf) { - for (uint16_t i = 0; i < node->n_vals + 1; ++i) { - print_tree(node, node->data.inode.children[i], level + 1); - } - } - if (!parent) { - printf("}\n"); - } - } -} - -#endif // ZIX_BTREE_DEBUG - -ZIX_PRIVATE ZixBTreeNode* + if (node) { + if (!parent) { + printf("TREE {\n"); + } + for (int i = 0; i < level + 1; ++i) { + printf(" "); + } + print_node(node, ""); + if (!node->is_leaf) { + for (uint16_t i = 0; i < node->n_vals + 1; ++i) { + print_tree(node, node->data.inode.children[i], level + 1); + } + } + if (!parent) { + printf("}\n"); + } + } +} + +#endif // ZIX_BTREE_DEBUG + +static ZixBTreeNode* zix_btree_node_new(const bool leaf) { #if !((defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112l) || \ (defined(__cplusplus) && __cplusplus >= 201103L)) - assert(sizeof(ZixBTreeNode) == ZIX_BTREE_PAGE_SIZE); + assert(sizeof(ZixBTreeNode) == ZIX_BTREE_PAGE_SIZE); #endif - ZixBTreeNode* node = (ZixBTreeNode*)malloc(sizeof(ZixBTreeNode)); - if (node) { - node->is_leaf = leaf; - node->n_vals = 0; - } - return node; + ZixBTreeNode* node = (ZixBTreeNode*)malloc(sizeof(ZixBTreeNode)); + if (node) { + node->is_leaf = leaf; + node->n_vals = 0; + } + return node; } -ZIX_PRIVATE -void* +static void* zix_btree_value(const ZixBTreeNode* const node, const unsigned i) { - assert(i < node->n_vals); - return node->is_leaf ? node->data.leaf.vals[i] : node->data.inode.vals[i]; + assert(i < node->n_vals); + return node->is_leaf ? node->data.leaf.vals[i] : node->data.inode.vals[i]; } -ZIX_PRIVATE -ZixBTreeNode* +static ZixBTreeNode* zix_btree_child(const ZixBTreeNode* const node, const unsigned i) { - assert(!node->is_leaf); - assert(i <= ZIX_BTREE_INODE_VALS); - return node->data.inode.children[i]; + assert(!node->is_leaf); + assert(i <= ZIX_BTREE_INODE_VALS); + return node->data.inode.children[i]; } ZixBTree* @@ -147,455 +145,452 @@ zix_btree_new(const ZixComparator cmp, const void* const cmp_data, const ZixDestroyFunc destroy) { - ZixBTree* t = (ZixBTree*)malloc(sizeof(ZixBTree)); - if (t) { - t->root = zix_btree_node_new(true); - t->destroy = destroy; - t->cmp = cmp; - t->cmp_data = cmp_data; - t->size = 0; - t->height = 1; - if (!t->root) { - free(t); - return NULL; - } - } - return t; -} - -ZIX_PRIVATE void + ZixBTree* t = (ZixBTree*)malloc(sizeof(ZixBTree)); + if (t) { + t->root = zix_btree_node_new(true); + t->destroy = destroy; + t->cmp = cmp; + t->cmp_data = cmp_data; + t->size = 0; + t->height = 1; + if (!t->root) { + free(t); + return NULL; + } + } + return t; +} + +static void zix_btree_free_rec(ZixBTree* const t, ZixBTreeNode* const n) { - if (n) { - if (n->is_leaf) { - if (t->destroy) { - for (uint16_t i = 0; i < n->n_vals; ++i) { - t->destroy(n->data.leaf.vals[i]); - } - } - } else { - if (t->destroy) { - for (uint16_t i = 0; i < n->n_vals; ++i) { - t->destroy(n->data.inode.vals[i]); - } - } - - for (uint16_t i = 0; i < n->n_vals + 1; ++i) { - zix_btree_free_rec(t, zix_btree_child(n, i)); - } - } - free(n); - } + if (n) { + if (n->is_leaf) { + if (t->destroy) { + for (uint16_t i = 0; i < n->n_vals; ++i) { + t->destroy(n->data.leaf.vals[i]); + } + } + } else { + if (t->destroy) { + for (uint16_t i = 0; i < n->n_vals; ++i) { + t->destroy(n->data.inode.vals[i]); + } + } + + for (uint16_t i = 0; i < n->n_vals + 1; ++i) { + zix_btree_free_rec(t, zix_btree_child(n, i)); + } + } + + free(n); + } } void zix_btree_free(ZixBTree* const t) { - if (t) { - zix_btree_free_rec(t, t->root); - free(t); - } + if (t) { + zix_btree_free_rec(t, t->root); + free(t); + } } size_t zix_btree_size(const ZixBTree* const t) { - return t->size; + return t->size; } -ZIX_PRIVATE uint16_t +static uint16_t zix_btree_max_vals(const ZixBTreeNode* const node) { - return node->is_leaf ? ZIX_BTREE_LEAF_VALS : ZIX_BTREE_INODE_VALS; + return node->is_leaf ? ZIX_BTREE_LEAF_VALS : ZIX_BTREE_INODE_VALS; } -ZIX_PRIVATE uint16_t +static uint16_t zix_btree_min_vals(const ZixBTreeNode* const node) { - return (uint16_t)(((zix_btree_max_vals(node) + 1U) / 2U) - 1U); + return (uint16_t)(((zix_btree_max_vals(node) + 1U) / 2U) - 1U); } /** Shift pointers in `array` of length `n` right starting at `i`. */ -ZIX_PRIVATE void +static void zix_btree_ainsert(void** const array, const unsigned n, const unsigned i, void* const e) { - memmove(array + i + 1, array + i, (n - i) * sizeof(e)); - array[i] = e; + memmove(array + i + 1, array + i, (n - i) * sizeof(e)); + array[i] = e; } /** Erase element `i` in `array` of length `n` and return erased element. */ -ZIX_PRIVATE void* +static void* zix_btree_aerase(void** const array, const unsigned n, const unsigned i) { - void* const ret = array[i]; - memmove(array + i, array + i + 1, (n - i) * sizeof(ret)); - return ret; + void* const ret = array[i]; + memmove(array + i, array + i + 1, (n - i) * sizeof(ret)); + return ret; } /** Split lhs, the i'th child of `n`, into two nodes. */ -ZIX_PRIVATE ZixBTreeNode* +static ZixBTreeNode* zix_btree_split_child(ZixBTreeNode* const n, const unsigned i, ZixBTreeNode* const lhs) { - assert(lhs->n_vals == zix_btree_max_vals(lhs)); - assert(n->n_vals < ZIX_BTREE_INODE_VALS); - assert(i < n->n_vals + 1U); - assert(zix_btree_child(n, i) == lhs); - - const uint16_t max_n_vals = zix_btree_max_vals(lhs); - ZixBTreeNode* rhs = zix_btree_node_new(lhs->is_leaf); - if (!rhs) { - return NULL; - } - - // LHS and RHS get roughly half, less the middle value which moves up - lhs->n_vals = max_n_vals / 2U; - rhs->n_vals = (uint16_t)(max_n_vals - lhs->n_vals - 1); - - if (lhs->is_leaf) { - // Copy large half from LHS to new RHS node - memcpy(rhs->data.leaf.vals, - lhs->data.leaf.vals + lhs->n_vals + 1, - rhs->n_vals * sizeof(void*)); - - // Move middle value up to parent - zix_btree_ainsert(n->data.inode.vals, - n->n_vals, - i, - lhs->data.leaf.vals[lhs->n_vals]); - } else { - // Copy large half from LHS to new RHS node - memcpy(rhs->data.inode.vals, - lhs->data.inode.vals + lhs->n_vals + 1, - rhs->n_vals * sizeof(void*)); - memcpy(rhs->data.inode.children, - lhs->data.inode.children + lhs->n_vals + 1, - (rhs->n_vals + 1U) * sizeof(ZixBTreeNode*)); - - // Move middle value up to parent - zix_btree_ainsert(n->data.inode.vals, - n->n_vals, - i, - lhs->data.inode.vals[lhs->n_vals]); - } - - // Insert new RHS node in parent at position i - zix_btree_ainsert((void**)n->data.inode.children, ++n->n_vals, i + 1U, rhs); - - return rhs; + assert(lhs->n_vals == zix_btree_max_vals(lhs)); + assert(n->n_vals < ZIX_BTREE_INODE_VALS); + assert(i < n->n_vals + 1U); + assert(zix_btree_child(n, i) == lhs); + + const uint16_t max_n_vals = zix_btree_max_vals(lhs); + ZixBTreeNode* rhs = zix_btree_node_new(lhs->is_leaf); + if (!rhs) { + return NULL; + } + + // LHS and RHS get roughly half, less the middle value which moves up + lhs->n_vals = max_n_vals / 2U; + rhs->n_vals = (uint16_t)(max_n_vals - lhs->n_vals - 1); + + if (lhs->is_leaf) { + // Copy large half from LHS to new RHS node + memcpy(rhs->data.leaf.vals, + lhs->data.leaf.vals + lhs->n_vals + 1, + rhs->n_vals * sizeof(void*)); + + // Move middle value up to parent + zix_btree_ainsert( + n->data.inode.vals, n->n_vals, i, lhs->data.leaf.vals[lhs->n_vals]); + } else { + // Copy large half from LHS to new RHS node + memcpy(rhs->data.inode.vals, + lhs->data.inode.vals + lhs->n_vals + 1, + rhs->n_vals * sizeof(void*)); + memcpy(rhs->data.inode.children, + lhs->data.inode.children + lhs->n_vals + 1, + (rhs->n_vals + 1U) * sizeof(ZixBTreeNode*)); + + // Move middle value up to parent + zix_btree_ainsert( + n->data.inode.vals, n->n_vals, i, lhs->data.inode.vals[lhs->n_vals]); + } + + // Insert new RHS node in parent at position i + zix_btree_ainsert((void**)n->data.inode.children, ++n->n_vals, i + 1U, rhs); + + return rhs; } #ifdef ZIX_BTREE_SORTED_CHECK /** Check that `n` is sorted with respect to search key `e`. */ -ZIX_PRIVATE bool +static bool zix_btree_node_is_sorted_with_respect_to(const ZixBTree* const t, const ZixBTreeNode* const n, const void* const e) { - if (n->n_vals <= 1) { - return true; - } + if (n->n_vals <= 1) { + return true; + } - int cmp = t->cmp(zix_btree_value(n, 0), e, t->cmp_data); - for (uint16_t i = 1; i < n->n_vals; ++i) { - const int next_cmp = t->cmp(zix_btree_value(n, i), e, t->cmp_data); - if ((cmp >= 0 && next_cmp < 0) || - (cmp > 0 && next_cmp <= 0)) { - return false; - } - cmp = next_cmp; - } + int cmp = t->cmp(zix_btree_value(n, 0), e, t->cmp_data); + for (uint16_t i = 1; i < n->n_vals; ++i) { + const int next_cmp = t->cmp(zix_btree_value(n, i), e, t->cmp_data); + if ((cmp >= 0 && next_cmp < 0) || (cmp > 0 && next_cmp <= 0)) { + return false; + } + cmp = next_cmp; + } - return true; + return true; } #endif /** Find the first value in `n` that is not less than `e` (lower bound). */ -ZIX_PRIVATE unsigned +static unsigned zix_btree_node_find(const ZixBTree* const t, const ZixBTreeNode* const n, const void* const e, bool* const equal) { #ifdef ZIX_BTREE_SORTED_CHECK - assert(zix_btree_node_is_sorted_with_respect_to(t, n, e)); + assert(zix_btree_node_is_sorted_with_respect_to(t, n, e)); #endif - unsigned first = 0U; - unsigned len = n->n_vals; - while (len > 0) { - const unsigned half = len >> 1U; - const unsigned i = first + half; - const int cmp = t->cmp(zix_btree_value(n, i), e, t->cmp_data); - if (cmp == 0) { - *equal = true; - len = half; // Keep searching for wildcard matches - } else if (cmp < 0) { - const unsigned chop = half + 1U; - first += chop; - len -= chop; - } else { - len = half; - } - } - assert(!*equal || t->cmp(zix_btree_value(n, first), e, t->cmp_data) == 0); - return first; + unsigned first = 0U; + unsigned len = n->n_vals; + while (len > 0) { + const unsigned half = len >> 1U; + const unsigned i = first + half; + const int cmp = t->cmp(zix_btree_value(n, i), e, t->cmp_data); + if (cmp == 0) { + *equal = true; + len = half; // Keep searching for wildcard matches + } else if (cmp < 0) { + const unsigned chop = half + 1U; + first += chop; + len -= chop; + } else { + len = half; + } + } + + assert(!*equal || t->cmp(zix_btree_value(n, first), e, t->cmp_data) == 0); + return first; } ZixStatus zix_btree_insert(ZixBTree* const t, void* const e) { - ZixBTreeNode* parent = NULL; // Parent of n - ZixBTreeNode* n = t->root; // Current node - unsigned i = 0; // Index of n in parent - while (n) { - if (n->n_vals == zix_btree_max_vals(n)) { - // Node is full, split to ensure there is space for a leaf split - if (!parent) { - // Root is full, grow tree upwards - if (!(parent = zix_btree_node_new(false))) { - return ZIX_STATUS_NO_MEM; - } - t->root = parent; - parent->data.inode.children[0] = n; - ++t->height; - } - - ZixBTreeNode* const rhs = zix_btree_split_child(parent, i, n); - if (!rhs) { - return ZIX_STATUS_NO_MEM; - } - - const int cmp = t->cmp(parent->data.inode.vals[i], e, t->cmp_data); - if (cmp == 0) { - return ZIX_STATUS_EXISTS; - } else if (cmp < 0) { - // Move to new RHS - n = rhs; - ++i; - } - } - - assert(!parent || zix_btree_child(parent, i) == n); - - bool equal = false; - i = zix_btree_node_find(t, n, e, &equal); - if (equal) { - return ZIX_STATUS_EXISTS; - } else if (!n->is_leaf) { - // Descend to child node left of value - parent = n; - n = zix_btree_child(n, i); - } else { - // Insert into internal node - zix_btree_ainsert(n->data.leaf.vals, n->n_vals++, i, e); - break; - } - } - - ++t->size; - - return ZIX_STATUS_SUCCESS; -} - -ZIX_PRIVATE ZixBTreeIter* + ZixBTreeNode* parent = NULL; // Parent of n + ZixBTreeNode* n = t->root; // Current node + unsigned i = 0; // Index of n in parent + while (n) { + if (n->n_vals == zix_btree_max_vals(n)) { + // Node is full, split to ensure there is space for a leaf split + if (!parent) { + // Root is full, grow tree upwards + if (!(parent = zix_btree_node_new(false))) { + return ZIX_STATUS_NO_MEM; + } + t->root = parent; + parent->data.inode.children[0] = n; + ++t->height; + } + + ZixBTreeNode* const rhs = zix_btree_split_child(parent, i, n); + if (!rhs) { + return ZIX_STATUS_NO_MEM; + } + + const int cmp = t->cmp(parent->data.inode.vals[i], e, t->cmp_data); + if (cmp == 0) { + return ZIX_STATUS_EXISTS; + } + + if (cmp < 0) { + // Move to new RHS + n = rhs; + ++i; + } + } + + assert(!parent || zix_btree_child(parent, i) == n); + + bool equal = false; + i = zix_btree_node_find(t, n, e, &equal); + if (equal) { + return ZIX_STATUS_EXISTS; + } + + if (!n->is_leaf) { + // Descend to child node left of value + parent = n; + n = zix_btree_child(n, i); + } else { + // Insert into internal node + zix_btree_ainsert(n->data.leaf.vals, n->n_vals++, i, e); + break; + } + } + + ++t->size; + + return ZIX_STATUS_SUCCESS; +} + +static ZixBTreeIter* zix_btree_iter_new(const ZixBTree* const t) { - const size_t s = t->height * sizeof(ZixBTreeIterFrame); + const size_t s = t->height * sizeof(ZixBTreeIterFrame); - ZixBTreeIter* i = (ZixBTreeIter*)calloc(1, sizeof(ZixBTreeIter) + s); - if (i) { - i->n_levels = t->height; - } - return i; + ZixBTreeIter* i = (ZixBTreeIter*)calloc(1, sizeof(ZixBTreeIter) + s); + if (i) { + i->n_levels = t->height; + } + return i; } -ZIX_PRIVATE void +static void zix_btree_iter_set_frame(ZixBTreeIter* const ti, ZixBTreeNode* const n, const unsigned i) { - if (ti) { - ti->stack[ti->level].node = n; - ti->stack[ti->level].index = i; - } + if (ti) { + ti->stack[ti->level].node = n; + ti->stack[ti->level].index = i; + } } -ZIX_PRIVATE bool +static bool zix_btree_node_is_minimal(ZixBTreeNode* const n) { - assert(n->n_vals >= zix_btree_min_vals(n)); - return n->n_vals == zix_btree_min_vals(n); + assert(n->n_vals >= zix_btree_min_vals(n)); + return n->n_vals == zix_btree_min_vals(n); } /** Enlarge left child by stealing a value from its right sibling. */ -ZIX_PRIVATE ZixBTreeNode* +static ZixBTreeNode* zix_btree_rotate_left(ZixBTreeNode* const parent, const unsigned i) { - ZixBTreeNode* const lhs = zix_btree_child(parent, i); - ZixBTreeNode* const rhs = zix_btree_child(parent, i + 1); + ZixBTreeNode* const lhs = zix_btree_child(parent, i); + ZixBTreeNode* const rhs = zix_btree_child(parent, i + 1); - assert(lhs->is_leaf == rhs->is_leaf); + assert(lhs->is_leaf == rhs->is_leaf); - if (lhs->is_leaf) { - // Move parent value to end of LHS - lhs->data.leaf.vals[lhs->n_vals++] = parent->data.inode.vals[i]; + if (lhs->is_leaf) { + // Move parent value to end of LHS + lhs->data.leaf.vals[lhs->n_vals++] = parent->data.inode.vals[i]; - // Move first value in RHS to parent - parent->data.inode.vals[i] = - zix_btree_aerase(rhs->data.leaf.vals, rhs->n_vals, 0); - } else { - // Move parent value to end of LHS - lhs->data.inode.vals[lhs->n_vals++] = parent->data.inode.vals[i]; + // Move first value in RHS to parent + parent->data.inode.vals[i] = + zix_btree_aerase(rhs->data.leaf.vals, rhs->n_vals, 0); + } else { + // Move parent value to end of LHS + lhs->data.inode.vals[lhs->n_vals++] = parent->data.inode.vals[i]; - // Move first value in RHS to parent - parent->data.inode.vals[i] = - zix_btree_aerase(rhs->data.inode.vals, rhs->n_vals, 0); + // Move first value in RHS to parent + parent->data.inode.vals[i] = + zix_btree_aerase(rhs->data.inode.vals, rhs->n_vals, 0); - // Move first child pointer from RHS to end of LHS - lhs->data.inode.children[lhs->n_vals] = (ZixBTreeNode*)zix_btree_aerase( - (void**)rhs->data.inode.children, rhs->n_vals, 0); - } + // Move first child pointer from RHS to end of LHS + lhs->data.inode.children[lhs->n_vals] = (ZixBTreeNode*)zix_btree_aerase( + (void**)rhs->data.inode.children, rhs->n_vals, 0); + } - --rhs->n_vals; + --rhs->n_vals; - return lhs; + return lhs; } /** Enlarge right child by stealing a value from its left sibling. */ -ZIX_PRIVATE ZixBTreeNode* +static ZixBTreeNode* zix_btree_rotate_right(ZixBTreeNode* const parent, const unsigned i) { - ZixBTreeNode* const lhs = zix_btree_child(parent, i - 1); - ZixBTreeNode* const rhs = zix_btree_child(parent, i); + ZixBTreeNode* const lhs = zix_btree_child(parent, i - 1); + ZixBTreeNode* const rhs = zix_btree_child(parent, i); - assert(lhs->is_leaf == rhs->is_leaf); + assert(lhs->is_leaf == rhs->is_leaf); - if (lhs->is_leaf) { - // Prepend parent value to RHS - zix_btree_ainsert(rhs->data.leaf.vals, - rhs->n_vals++, - 0, - parent->data.inode.vals[i - 1]); + if (lhs->is_leaf) { + // Prepend parent value to RHS + zix_btree_ainsert( + rhs->data.leaf.vals, rhs->n_vals++, 0, parent->data.inode.vals[i - 1]); - // Move last value from LHS to parent - parent->data.inode.vals[i - 1] = lhs->data.leaf.vals[--lhs->n_vals]; - } else { - // Prepend parent value to RHS - zix_btree_ainsert(rhs->data.inode.vals, - rhs->n_vals++, - 0, - parent->data.inode.vals[i - 1]); + // Move last value from LHS to parent + parent->data.inode.vals[i - 1] = lhs->data.leaf.vals[--lhs->n_vals]; + } else { + // Prepend parent value to RHS + zix_btree_ainsert( + rhs->data.inode.vals, rhs->n_vals++, 0, parent->data.inode.vals[i - 1]); - // Move last child pointer from LHS and prepend to RHS - zix_btree_ainsert((void**)rhs->data.inode.children, - rhs->n_vals, - 0, - lhs->data.inode.children[lhs->n_vals]); + // Move last child pointer from LHS and prepend to RHS + zix_btree_ainsert((void**)rhs->data.inode.children, + rhs->n_vals, + 0, + lhs->data.inode.children[lhs->n_vals]); - // Move last value from LHS to parent - parent->data.inode.vals[i - 1] = lhs->data.inode.vals[--lhs->n_vals]; - } + // Move last value from LHS to parent + parent->data.inode.vals[i - 1] = lhs->data.inode.vals[--lhs->n_vals]; + } - return rhs; + return rhs; } /** Move n[i] down, merge the left and right child, return the merged node. */ -ZIX_PRIVATE ZixBTreeNode* +static ZixBTreeNode* zix_btree_merge(ZixBTree* const t, ZixBTreeNode* const n, const unsigned i) { - ZixBTreeNode* const lhs = zix_btree_child(n, i); - ZixBTreeNode* const rhs = zix_btree_child(n, i + 1); - - assert(lhs->is_leaf == rhs->is_leaf); - assert(zix_btree_node_is_minimal(lhs)); - assert(lhs->n_vals + rhs->n_vals < zix_btree_max_vals(lhs)); - - // Move parent value to end of LHS - if (lhs->is_leaf) { - lhs->data.leaf.vals[lhs->n_vals++] = - zix_btree_aerase(n->data.inode.vals, n->n_vals, i); - } else { - lhs->data.inode.vals[lhs->n_vals++] = - zix_btree_aerase(n->data.inode.vals, n->n_vals, i); - } - - // Erase corresponding child pointer (to RHS) in parent - zix_btree_aerase((void**)n->data.inode.children, n->n_vals, i + 1U); - - // Add everything from RHS to end of LHS - if (lhs->is_leaf) { - memcpy(lhs->data.leaf.vals + lhs->n_vals, - rhs->data.leaf.vals, - rhs->n_vals * sizeof(void*)); - } else { - memcpy(lhs->data.inode.vals + lhs->n_vals, - rhs->data.inode.vals, - rhs->n_vals * sizeof(void*)); - memcpy(lhs->data.inode.children + lhs->n_vals, - rhs->data.inode.children, - (rhs->n_vals + 1U) * sizeof(void*)); - } - - lhs->n_vals = (uint16_t)(lhs->n_vals + rhs->n_vals); - - if (--n->n_vals == 0) { - // Root is now empty, replace it with its only child - assert(n == t->root); - t->root = lhs; - free(n); - } - - free(rhs); - return lhs; + ZixBTreeNode* const lhs = zix_btree_child(n, i); + ZixBTreeNode* const rhs = zix_btree_child(n, i + 1); + + assert(lhs->is_leaf == rhs->is_leaf); + assert(zix_btree_node_is_minimal(lhs)); + assert(lhs->n_vals + rhs->n_vals < zix_btree_max_vals(lhs)); + + // Move parent value to end of LHS + if (lhs->is_leaf) { + lhs->data.leaf.vals[lhs->n_vals++] = + zix_btree_aerase(n->data.inode.vals, n->n_vals, i); + } else { + lhs->data.inode.vals[lhs->n_vals++] = + zix_btree_aerase(n->data.inode.vals, n->n_vals, i); + } + + // Erase corresponding child pointer (to RHS) in parent + zix_btree_aerase((void**)n->data.inode.children, n->n_vals, i + 1U); + + // Add everything from RHS to end of LHS + if (lhs->is_leaf) { + memcpy(lhs->data.leaf.vals + lhs->n_vals, + rhs->data.leaf.vals, + rhs->n_vals * sizeof(void*)); + } else { + memcpy(lhs->data.inode.vals + lhs->n_vals, + rhs->data.inode.vals, + rhs->n_vals * sizeof(void*)); + memcpy(lhs->data.inode.children + lhs->n_vals, + rhs->data.inode.children, + (rhs->n_vals + 1U) * sizeof(void*)); + } + + lhs->n_vals = (uint16_t)(lhs->n_vals + rhs->n_vals); + + if (--n->n_vals == 0) { + // Root is now empty, replace it with its only child + assert(n == t->root); + t->root = lhs; + free(n); + } + + free(rhs); + return lhs; } /** Remove and return the min value from the subtree rooted at `n`. */ -ZIX_PRIVATE void* +static void* zix_btree_remove_min(ZixBTree* const t, ZixBTreeNode* n) { - while (!n->is_leaf) { - if (zix_btree_node_is_minimal(zix_btree_child(n, 0))) { - // Leftmost child is minimal, must expand - if (!zix_btree_node_is_minimal(zix_btree_child(n, 1))) { - // Child's right sibling has at least one key to steal - n = zix_btree_rotate_left(n, 0); - } else { - // Both child and right sibling are minimal, merge - n = zix_btree_merge(t, n, 0); - } - } else { - n = zix_btree_child(n, 0); - } - } + while (!n->is_leaf) { + if (zix_btree_node_is_minimal(zix_btree_child(n, 0))) { + // Leftmost child is minimal, must expand + if (!zix_btree_node_is_minimal(zix_btree_child(n, 1))) { + // Child's right sibling has at least one key to steal + n = zix_btree_rotate_left(n, 0); + } else { + // Both child and right sibling are minimal, merge + n = zix_btree_merge(t, n, 0); + } + } else { + n = zix_btree_child(n, 0); + } + } - return zix_btree_aerase(n->data.leaf.vals, --n->n_vals, 0); + return zix_btree_aerase(n->data.leaf.vals, --n->n_vals, 0); } /** Remove and return the max value from the subtree rooted at `n`. */ -ZIX_PRIVATE void* +static void* zix_btree_remove_max(ZixBTree* const t, ZixBTreeNode* n) { - while (!n->is_leaf) { - if (zix_btree_node_is_minimal(zix_btree_child(n, n->n_vals))) { - // Leftmost child is minimal, must expand - if (!zix_btree_node_is_minimal(zix_btree_child(n, n->n_vals - 1))) { - // Child's left sibling has at least one key to steal - n = zix_btree_rotate_right(n, n->n_vals); - } else { - // Both child and left sibling are minimal, merge - n = zix_btree_merge(t, n, n->n_vals - 1U); - } - } else { - n = zix_btree_child(n, n->n_vals); - } - } + while (!n->is_leaf) { + if (zix_btree_node_is_minimal(zix_btree_child(n, n->n_vals))) { + // Leftmost child is minimal, must expand + if (!zix_btree_node_is_minimal(zix_btree_child(n, n->n_vals - 1))) { + // Child's left sibling has at least one key to steal + n = zix_btree_rotate_right(n, n->n_vals); + } else { + // Both child and left sibling are minimal, merge + n = zix_btree_merge(t, n, n->n_vals - 1U); + } + } else { + n = zix_btree_child(n, n->n_vals); + } + } - return n->data.leaf.vals[--n->n_vals]; + return n->data.leaf.vals[--n->n_vals]; } ZixStatus @@ -604,120 +599,120 @@ zix_btree_remove(ZixBTree* const t, void** const out, ZixBTreeIter** const next) { - ZixBTreeNode* n = t->root; - ZixBTreeIter* ti = NULL; - const bool user_iter = next && *next; - if (next) { - if (!*next && !(*next = zix_btree_iter_new(t))) { - return ZIX_STATUS_NO_MEM; - } - ti = *next; - ti->level = 0; - } - - while (true) { - /* To remove in a single walk down, the tree is adjusted along the way - so that the current node always has at least one more value than the - minimum required in general. Thus, there is always room to remove - without adjusting on the way back up. */ - assert(n == t->root || !zix_btree_node_is_minimal(n)); - - bool equal = false; - const unsigned i = zix_btree_node_find(t, n, e, &equal); - zix_btree_iter_set_frame(ti, n, i); - if (n->is_leaf) { - if (equal) { - // Found in leaf node - *out = zix_btree_aerase(n->data.leaf.vals, --n->n_vals, i); - if (ti && i == n->n_vals) { - if (i == 0) { - ti->level = 0; - ti->stack[0].node = NULL; - } else { - --ti->stack[ti->level].index; - zix_btree_iter_increment(ti); - } - } - --t->size; - return ZIX_STATUS_SUCCESS; - } else { - // Not found in leaf node, or tree - if (ti && !user_iter) { - zix_btree_iter_free(ti); - *next = NULL; - } - return ZIX_STATUS_NOT_FOUND; - } - } else if (equal) { - // Found in internal node - ZixBTreeNode* const lhs = zix_btree_child(n, i); - ZixBTreeNode* const rhs = zix_btree_child(n, i + 1); - const size_t l_size = lhs->n_vals; - const size_t r_size = rhs->n_vals; - if (zix_btree_node_is_minimal(lhs) && - zix_btree_node_is_minimal(rhs)) { - // Both preceding and succeeding child are minimal - n = zix_btree_merge(t, n, i); - } else if (l_size >= r_size) { - // Left child can remove without merge - assert(!zix_btree_node_is_minimal(lhs)); - *out = n->data.inode.vals[i]; - n->data.inode.vals[i] = zix_btree_remove_max(t, lhs); - --t->size; - return ZIX_STATUS_SUCCESS; - } else { - // Right child can remove without merge - assert(!zix_btree_node_is_minimal(rhs)); - *out = n->data.inode.vals[i]; - n->data.inode.vals[i] = zix_btree_remove_min(t, rhs); - --t->size; - return ZIX_STATUS_SUCCESS; - } - } else { - // Not found in internal node, key is in/under children[i] - if (zix_btree_node_is_minimal(zix_btree_child(n, i))) { - if (i > 0 && - !zix_btree_node_is_minimal(zix_btree_child(n, i - 1))) { - // Steal a key from child's left sibling - n = zix_btree_rotate_right(n, i); - } else if (i < n->n_vals && - !zix_btree_node_is_minimal( - zix_btree_child(n, i + 1))) { - // Steal a key from child's right sibling - n = zix_btree_rotate_left(n, i); - } else if (n == t->root && n->n_vals == 1) { - // Root has two children, both minimal, delete it - assert(i == 0 || i == 1); - const uint16_t counts[2] = {zix_btree_child(n, 0)->n_vals, - zix_btree_child(n, 1)->n_vals}; - - n = zix_btree_merge(t, n, 0); - if (ti) { - ti->stack[ti->level].node = n; - ti->stack[ti->level].index = counts[i]; - } - } else { - // Both child's siblings are minimal, merge them - if (i < n->n_vals) { - n = zix_btree_merge(t, n, i); - } else { - n = zix_btree_merge(t, n, i - 1U); - if (ti) { - --ti->stack[ti->level].index; - } - } - } - } else { - n = zix_btree_child(n, i); - } - } - if (ti) { - ++ti->level; - } - } - - assert(false); // Not reached - return ZIX_STATUS_ERROR; + ZixBTreeNode* n = t->root; + ZixBTreeIter* ti = NULL; + const bool user_iter = next && *next; + if (next) { + if (!*next && !(*next = zix_btree_iter_new(t))) { + return ZIX_STATUS_NO_MEM; + } + ti = *next; + ti->level = 0; + } + + while (true) { + /* To remove in a single walk down, the tree is adjusted along the way + so that the current node always has at least one more value than the + minimum required in general. Thus, there is always room to remove + without adjusting on the way back up. */ + assert(n == t->root || !zix_btree_node_is_minimal(n)); + + bool equal = false; + const unsigned i = zix_btree_node_find(t, n, e, &equal); + zix_btree_iter_set_frame(ti, n, i); + if (n->is_leaf) { + if (equal) { + // Found in leaf node + *out = zix_btree_aerase(n->data.leaf.vals, --n->n_vals, i); + if (ti && i == n->n_vals) { + if (i == 0) { + ti->level = 0; + ti->stack[0].node = NULL; + } else { + --ti->stack[ti->level].index; + zix_btree_iter_increment(ti); + } + } + --t->size; + return ZIX_STATUS_SUCCESS; + } + + // Not found in leaf node, or tree + if (ti && !user_iter) { + zix_btree_iter_free(ti); + *next = NULL; + } + + return ZIX_STATUS_NOT_FOUND; + } + + if (equal) { + // Found in internal node + ZixBTreeNode* const lhs = zix_btree_child(n, i); + ZixBTreeNode* const rhs = zix_btree_child(n, i + 1); + const size_t l_size = lhs->n_vals; + const size_t r_size = rhs->n_vals; + if (zix_btree_node_is_minimal(lhs) && zix_btree_node_is_minimal(rhs)) { + // Both preceding and succeeding child are minimal + n = zix_btree_merge(t, n, i); + } else if (l_size >= r_size) { + // Left child can remove without merge + assert(!zix_btree_node_is_minimal(lhs)); + *out = n->data.inode.vals[i]; + n->data.inode.vals[i] = zix_btree_remove_max(t, lhs); + --t->size; + return ZIX_STATUS_SUCCESS; + } else { + // Right child can remove without merge + assert(!zix_btree_node_is_minimal(rhs)); + *out = n->data.inode.vals[i]; + n->data.inode.vals[i] = zix_btree_remove_min(t, rhs); + --t->size; + return ZIX_STATUS_SUCCESS; + } + } else { + // Not found in internal node, key is in/under children[i] + if (zix_btree_node_is_minimal(zix_btree_child(n, i))) { + if (i > 0 && !zix_btree_node_is_minimal(zix_btree_child(n, i - 1))) { + // Steal a key from child's left sibling + n = zix_btree_rotate_right(n, i); + } else if (i < n->n_vals && + !zix_btree_node_is_minimal(zix_btree_child(n, i + 1))) { + // Steal a key from child's right sibling + n = zix_btree_rotate_left(n, i); + } else if (n == t->root && n->n_vals == 1) { + // Root has two children, both minimal, delete it + assert(i == 0 || i == 1); + const uint16_t counts[2] = {zix_btree_child(n, 0)->n_vals, + zix_btree_child(n, 1)->n_vals}; + + n = zix_btree_merge(t, n, 0); + if (ti) { + ti->stack[ti->level].node = n; + ti->stack[ti->level].index = counts[i]; + } + } else { + // Both child's siblings are minimal, merge them + if (i < n->n_vals) { + n = zix_btree_merge(t, n, i); + } else { + n = zix_btree_merge(t, n, i - 1U); + if (ti) { + --ti->stack[ti->level].index; + } + } + } + } else { + n = zix_btree_child(n, i); + } + } + if (ti) { + ++ti->level; + } + } + + assert(false); // Not reached + return ZIX_STATUS_ERROR; } ZixStatus @@ -725,30 +720,32 @@ zix_btree_find(const ZixBTree* const t, const void* const e, ZixBTreeIter** const ti) { - ZixBTreeNode* n = t->root; - if (!(*ti = zix_btree_iter_new(t))) { - return ZIX_STATUS_NO_MEM; - } + ZixBTreeNode* n = t->root; + if (!(*ti = zix_btree_iter_new(t))) { + return ZIX_STATUS_NO_MEM; + } + + while (n) { + bool equal = false; + const unsigned i = zix_btree_node_find(t, n, e, &equal); + + zix_btree_iter_set_frame(*ti, n, i); - while (n) { - bool equal = false; - const unsigned i = zix_btree_node_find(t, n, e, &equal); + if (equal) { + return ZIX_STATUS_SUCCESS; + } - zix_btree_iter_set_frame(*ti, n, i); + if (n->is_leaf) { + break; + } - if (equal) { - return ZIX_STATUS_SUCCESS; - } else if (n->is_leaf) { - break; - } else { - ++(*ti)->level; - n = zix_btree_child(n, i); - } - } + ++(*ti)->level; + n = zix_btree_child(n, i); + } - zix_btree_iter_free(*ti); - *ti = NULL; - return ZIX_STATUS_NOT_FOUND; + zix_btree_iter_free(*ti); + *ti = NULL; + return ZIX_STATUS_NOT_FOUND; } ZixStatus @@ -756,175 +753,184 @@ zix_btree_lower_bound(const ZixBTree* const t, const void* const e, ZixBTreeIter** const ti) { - if (!t) { - *ti = NULL; - return ZIX_STATUS_BAD_ARG; - } else if (!t->root) { - *ti = NULL; - return ZIX_STATUS_SUCCESS; - } - - ZixBTreeNode* n = t->root; - bool found = false; - unsigned found_level = 0; - if (!(*ti = zix_btree_iter_new(t))) { - return ZIX_STATUS_NO_MEM; - } - - while (n) { - bool equal = false; - const unsigned i = zix_btree_node_find(t, n, e, &equal); - - zix_btree_iter_set_frame(*ti, n, i); - - if (equal) { - found_level = (*ti)->level; - found = true; - } - - if (n->is_leaf) { - break; - } else { - ++(*ti)->level; - n = zix_btree_child(n, i); - assert(n); - } - } - - const ZixBTreeIterFrame* const frame = &(*ti)->stack[(*ti)->level]; - assert(frame->node); - if (frame->index == frame->node->n_vals) { - if (found) { - // Found on a previous level but went too far - (*ti)->level = found_level; - } else { - // Reached end (key is greater than everything in tree) - (*ti)->level = 0; - (*ti)->stack[0].node = NULL; - } - } - - return ZIX_STATUS_SUCCESS; + if (!t) { + *ti = NULL; + return ZIX_STATUS_BAD_ARG; + } + + if (!t->root) { + *ti = NULL; + return ZIX_STATUS_SUCCESS; + } + + ZixBTreeNode* n = t->root; + bool found = false; + unsigned found_level = 0; + if (!(*ti = zix_btree_iter_new(t))) { + return ZIX_STATUS_NO_MEM; + } + + while (n) { + bool equal = false; + const unsigned i = zix_btree_node_find(t, n, e, &equal); + + zix_btree_iter_set_frame(*ti, n, i); + + if (equal) { + found_level = (*ti)->level; + found = true; + } + + if (n->is_leaf) { + break; + } + + ++(*ti)->level; + n = zix_btree_child(n, i); + assert(n); + } + + const ZixBTreeIterFrame* const frame = &(*ti)->stack[(*ti)->level]; + assert(frame->node); + if (frame->index == frame->node->n_vals) { + if (found) { + // Found on a previous level but went too far + (*ti)->level = found_level; + } else { + // Reached end (key is greater than everything in tree) + (*ti)->level = 0; + (*ti)->stack[0].node = NULL; + } + } + + return ZIX_STATUS_SUCCESS; } void* zix_btree_get(const ZixBTreeIter* const ti) { - const ZixBTreeIterFrame* const frame = &ti->stack[ti->level]; - assert(frame->node); - assert(frame->index < frame->node->n_vals); - return zix_btree_value(frame->node, frame->index); + const ZixBTreeIterFrame* const frame = &ti->stack[ti->level]; + assert(frame->node); + assert(frame->index < frame->node->n_vals); + return zix_btree_value(frame->node, frame->index); } ZixBTreeIter* zix_btree_begin(const ZixBTree* const t) { - ZixBTreeIter* const i = zix_btree_iter_new(t); - if (!i) { - return NULL; - } else if (t->size == 0) { - i->level = 0; - i->stack[0].node = NULL; - } else { - ZixBTreeNode* n = t->root; - i->stack[0].node = n; - i->stack[0].index = 0; - while (!n->is_leaf) { - n = zix_btree_child(n, 0); - ++i->level; - i->stack[i->level].node = n; - i->stack[i->level].index = 0; - } - } - return i; + ZixBTreeIter* const i = zix_btree_iter_new(t); + if (!i) { + return NULL; + } + + if (t->size == 0) { + i->level = 0; + i->stack[0].node = NULL; + } else { + ZixBTreeNode* n = t->root; + i->stack[0].node = n; + i->stack[0].index = 0; + while (!n->is_leaf) { + n = zix_btree_child(n, 0); + ++i->level; + i->stack[i->level].node = n; + i->stack[i->level].index = 0; + } + } + + return i; } ZixBTreeIter* zix_btree_end(const ZixBTree* const t) { - return zix_btree_iter_new(t); + return zix_btree_iter_new(t); } ZixBTreeIter* zix_btree_iter_copy(const ZixBTreeIter* const i) { - if (!i) { - return NULL; - } + if (!i) { + return NULL; + } - const size_t s = i->n_levels * sizeof(ZixBTreeIterFrame); - ZixBTreeIter* j = (ZixBTreeIter*)calloc(1, sizeof(ZixBTreeIter) + s); - if (j) { - memcpy(j, i, sizeof(ZixBTreeIter) + s); - } - return j; + const size_t s = i->n_levels * sizeof(ZixBTreeIterFrame); + ZixBTreeIter* j = (ZixBTreeIter*)calloc(1, sizeof(ZixBTreeIter) + s); + if (j) { + memcpy(j, i, sizeof(ZixBTreeIter) + s); + } + + return j; } bool zix_btree_iter_is_end(const ZixBTreeIter* const i) { - return !i || i->stack[0].node == NULL; + return !i || (i->level == 0 && i->stack[0].node == NULL); } bool -zix_btree_iter_equals(const ZixBTreeIter* const lhs, const ZixBTreeIter* const rhs) +zix_btree_iter_equals(const ZixBTreeIter* const lhs, + const ZixBTreeIter* const rhs) { - if (zix_btree_iter_is_end(lhs) && zix_btree_iter_is_end(rhs)) { - return true; - } else if (zix_btree_iter_is_end(lhs) || zix_btree_iter_is_end(rhs) || - lhs->level != rhs->level) { - return false; - } + if (zix_btree_iter_is_end(lhs) && zix_btree_iter_is_end(rhs)) { + return true; + } + + if (zix_btree_iter_is_end(lhs) || zix_btree_iter_is_end(rhs) || + lhs->level != rhs->level) { + return false; + } - return !memcmp(lhs, - rhs, - sizeof(ZixBTreeIter) + - (lhs->level + 1) * sizeof(ZixBTreeIterFrame)); + return !memcmp(lhs, + rhs, + sizeof(ZixBTreeIter) + + (lhs->level + 1) * sizeof(ZixBTreeIterFrame)); } void zix_btree_iter_increment(ZixBTreeIter* const i) { - ZixBTreeIterFrame* f = &i->stack[i->level]; - if (f->node->is_leaf) { - // Leaf, move right - assert(f->index < f->node->n_vals); - if (++f->index == f->node->n_vals) { - // Reached end of leaf, move up - f = &i->stack[i->level]; - while (i->level > 0 && f->index == f->node->n_vals) { - f = &i->stack[--i->level]; - assert(f->index <= f->node->n_vals); - } - - if (f->index == f->node->n_vals) { - // Reached end of tree - assert(i->level == 0); - f->node = NULL; - f->index = 0; - } - } - } else { - // Internal node, move down to next child - assert(f->index < f->node->n_vals); - ZixBTreeNode* child = zix_btree_child(f->node, ++f->index); - - f = &i->stack[++i->level]; - f->node = child; - f->index = 0; - - // Move down and left until we hit a leaf - while (!f->node->is_leaf) { - child = zix_btree_child(f->node, 0); - f = &i->stack[++i->level]; - f->node = child; - f->index = 0; - } - } + ZixBTreeIterFrame* f = &i->stack[i->level]; + if (f->node->is_leaf) { + // Leaf, move right + assert(f->index < f->node->n_vals); + if (++f->index == f->node->n_vals) { + // Reached end of leaf, move up + f = &i->stack[i->level]; + while (i->level > 0 && f->index == f->node->n_vals) { + f = &i->stack[--i->level]; + assert(f->index <= f->node->n_vals); + } + + if (f->index == f->node->n_vals) { + // Reached end of tree + assert(i->level == 0); + f->node = NULL; + f->index = 0; + } + } + } else { + // Internal node, move down to next child + assert(f->index < f->node->n_vals); + ZixBTreeNode* child = zix_btree_child(f->node, ++f->index); + + f = &i->stack[++i->level]; + f->node = child; + f->index = 0; + + // Move down and left until we hit a leaf + while (!f->node->is_leaf) { + child = zix_btree_child(f->node, 0); + f = &i->stack[++i->level]; + f->node = child; + f->index = 0; + } + } } void zix_btree_iter_free(ZixBTreeIter* const i) { - free(i); + free(i); } diff --git a/src/zix/btree.h b/src/zix/btree.h index 6333a91..bec0698 100644 --- a/src/zix/btree.h +++ b/src/zix/btree.h @@ -1,5 +1,5 @@ /* - Copyright 2011-2016 David Robillard + Copyright 2011-2020 David Robillard Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -54,27 +54,29 @@ typedef struct ZixBTreeIterImpl ZixBTreeIter; /** Create a new (empty) B-Tree. */ -ZIX_API ZixBTree* -zix_btree_new(ZixComparator cmp, - const void* cmp_data, - ZixDestroyFunc destroy); +ZIX_API +ZixBTree* +zix_btree_new(ZixComparator cmp, const void* cmp_data, ZixDestroyFunc destroy); /** Free `t`. */ -ZIX_API void +ZIX_API +void zix_btree_free(ZixBTree* t); /** Return the number of elements in `t`. */ -ZIX_PURE_API size_t +ZIX_PURE_API +size_t zix_btree_size(const ZixBTree* t); /** Insert the element `e` into `t`. */ -ZIX_API ZixStatus +ZIX_API +ZixStatus zix_btree_insert(ZixBTree* t, void* e); /** @@ -90,14 +92,16 @@ zix_btree_insert(ZixBTree* t, void* e); also non-NULL, the iterator is reused, otherwise a new one is allocated. To reuse an iterator, no items may have been added since its creation. */ -ZIX_API ZixStatus +ZIX_API +ZixStatus zix_btree_remove(ZixBTree* t, const void* e, void** out, ZixBTreeIter** next); /** Set `ti` to an element equal to `e` in `t`. If no such item exists, `ti` is set to NULL. */ -ZIX_API ZixStatus +ZIX_API +ZixStatus zix_btree_find(const ZixBTree* t, const void* e, ZixBTreeIter** ti); /** @@ -107,13 +111,15 @@ zix_btree_find(const ZixBTree* t, const void* e, ZixBTreeIter** ti); values in the tree, `ti` will be set to the least such element. The search key `e` is always passed as the second argument to the comparator. */ -ZIX_API ZixStatus +ZIX_API +ZixStatus zix_btree_lower_bound(const ZixBTree* t, const void* e, ZixBTreeIter** ti); /** Return the data associated with the given tree item. */ -ZIX_PURE_API void* +ZIX_PURE_API +void* zix_btree_get(const ZixBTreeIter* ti); /** @@ -121,7 +127,8 @@ zix_btree_get(const ZixBTreeIter* ti); The returned iterator must be freed with zix_btree_iter_free(). */ -ZIX_PURE_API ZixBTreeIter* +ZIX_PURE_API +ZixBTreeIter* zix_btree_begin(const ZixBTree* t); /** @@ -129,37 +136,43 @@ zix_btree_begin(const ZixBTree* t); The returned iterator must be freed with zix_btree_iter_free(). */ -ZIX_API ZixBTreeIter* +ZIX_API +ZixBTreeIter* zix_btree_end(const ZixBTree* t); /** Return a new copy of `i`. */ -ZIX_API ZixBTreeIter* +ZIX_API +ZixBTreeIter* zix_btree_iter_copy(const ZixBTreeIter* i); /** Return true iff `lhs` is equal to `rhs`. */ -ZIX_PURE_API bool +ZIX_PURE_API +bool zix_btree_iter_equals(const ZixBTreeIter* lhs, const ZixBTreeIter* rhs); /** Return true iff `i` is an iterator to the end of its tree. */ -ZIX_PURE_API bool +ZIX_PURE_API +bool zix_btree_iter_is_end(const ZixBTreeIter* i); /** Increment `i` to point to the next element in the tree. */ -ZIX_API void +ZIX_API +void zix_btree_iter_increment(ZixBTreeIter* i); /** Free `i`. */ -ZIX_API void +ZIX_API +void zix_btree_iter_free(ZixBTreeIter* i); /** @@ -168,7 +181,7 @@ zix_btree_iter_free(ZixBTreeIter* i); */ #ifdef __cplusplus -} /* extern "C" */ +} /* extern "C" */ #endif -#endif /* ZIX_BTREE_H */ +#endif /* ZIX_BTREE_H */ diff --git a/src/zix/common.h b/src/zix/common.h index cf07451..d47586c 100644 --- a/src/zix/common.h +++ b/src/zix/common.h @@ -1,5 +1,5 @@ /* - Copyright 2016 David Robillard + Copyright 2016-2020 David Robillard Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -25,41 +25,37 @@ */ /** @cond */ -#ifdef ZIX_SHARED -# ifdef _WIN32 -# define ZIX_LIB_IMPORT __declspec(dllimport) -# define ZIX_LIB_EXPORT __declspec(dllexport) -# else -# define ZIX_LIB_IMPORT __attribute__((visibility("default"))) -# define ZIX_LIB_EXPORT __attribute__((visibility("default"))) -# endif -# ifdef ZIX_INTERNAL -# define ZIX_API ZIX_LIB_EXPORT -# else -# define ZIX_API ZIX_LIB_IMPORT -# endif -# define ZIX_PRIVATE static -#elif defined(ZIX_INLINE) -# define ZIX_API static inline -# define ZIX_PRIVATE static inline +#if defined(_WIN32) && !defined(ZIX_STATIC) && defined(ZIX_INTERNAL) +# define ZIX_API __declspec(dllexport) +#elif defined(_WIN32) && !defined(ZIX_STATIC) +# define ZIX_API __declspec(dllimport) +#elif defined(__GNUC__) +# define ZIX_API __attribute__((visibility("default"))) #else -# define ZIX_API -# define ZIX_PRIVATE static +# define ZIX_API #endif #ifdef __GNUC__ -# define ZIX_PURE_FUNC __attribute__((pure)) -# define ZIX_CONST_FUNC __attribute__((const)) -# define ZIX_MALLOC_FUNC __attribute__((malloc)) +# define ZIX_PURE_FUNC __attribute__((pure)) +# define ZIX_CONST_FUNC __attribute__((const)) +# define ZIX_MALLOC_FUNC __attribute__((malloc)) #else -# define ZIX_PURE_FUNC -# define ZIX_CONST_FUNC -# define ZIX_MALLOC_FUNC +# define ZIX_PURE_FUNC +# define ZIX_CONST_FUNC +# define ZIX_MALLOC_FUNC #endif -#define ZIX_PURE_API ZIX_API ZIX_PURE_FUNC -#define ZIX_CONST_API ZIX_API ZIX_CONST_FUNC -#define ZIX_MALLOC_API ZIX_API ZIX_MALLOC_FUNC +#define ZIX_PURE_API \ + ZIX_API \ + ZIX_PURE_FUNC + +#define ZIX_CONST_API \ + ZIX_API \ + ZIX_CONST_FUNC + +#define ZIX_MALLOC_API \ + ZIX_API \ + ZIX_MALLOC_FUNC /** @endcond */ @@ -68,43 +64,50 @@ extern "C" { #endif #ifdef __GNUC__ -#define ZIX_LOG_FUNC(fmt, arg1) __attribute__((format(printf, fmt, arg1))) +# define ZIX_LOG_FUNC(fmt, arg1) __attribute__((format(printf, fmt, arg1))) #else -#define ZIX_LOG_FUNC(fmt, arg1) +# define ZIX_LOG_FUNC(fmt, arg1) #endif // Unused parameter macro to suppresses warnings and make it impossible to use #if defined(__cplusplus) -# define ZIX_UNUSED(name) +# define ZIX_UNUSED(name) #elif defined(__GNUC__) -# define ZIX_UNUSED(name) name##_unused __attribute__((__unused__)) +# define ZIX_UNUSED(name) name##_unused __attribute__((__unused__)) #else -# define ZIX_UNUSED(name) name +# define ZIX_UNUSED(name) name #endif typedef enum { - ZIX_STATUS_SUCCESS, - ZIX_STATUS_ERROR, - ZIX_STATUS_NO_MEM, - ZIX_STATUS_NOT_FOUND, - ZIX_STATUS_EXISTS, - ZIX_STATUS_BAD_ARG, - ZIX_STATUS_BAD_PERMS + ZIX_STATUS_SUCCESS, + ZIX_STATUS_ERROR, + ZIX_STATUS_NO_MEM, + ZIX_STATUS_NOT_FOUND, + ZIX_STATUS_EXISTS, + ZIX_STATUS_BAD_ARG, + ZIX_STATUS_BAD_PERMS } ZixStatus; static inline const char* zix_strerror(const ZixStatus status) { - switch (status) { - case ZIX_STATUS_SUCCESS: return "Success"; - case ZIX_STATUS_ERROR: return "Unknown error"; - case ZIX_STATUS_NO_MEM: return "Out of memory"; - case ZIX_STATUS_NOT_FOUND: return "Not found"; - case ZIX_STATUS_EXISTS: return "Exists"; - case ZIX_STATUS_BAD_ARG: return "Bad argument"; - case ZIX_STATUS_BAD_PERMS: return "Bad permissions"; - } - return "Unknown error"; + switch (status) { + case ZIX_STATUS_SUCCESS: + return "Success"; + case ZIX_STATUS_ERROR: + return "Unknown error"; + case ZIX_STATUS_NO_MEM: + return "Out of memory"; + case ZIX_STATUS_NOT_FOUND: + return "Not found"; + case ZIX_STATUS_EXISTS: + return "Exists"; + case ZIX_STATUS_BAD_ARG: + return "Bad argument"; + case ZIX_STATUS_BAD_PERMS: + return "Bad permissions"; + } + return "Unknown error"; } /** @@ -129,7 +132,7 @@ typedef void (*ZixDestroyFunc)(void* ptr); */ #ifdef __cplusplus -} /* extern "C" */ +} /* extern "C" */ #endif -#endif /* ZIX_COMMON_H */ +#endif /* ZIX_COMMON_H */ diff --git a/src/zix/digest.c b/src/zix/digest.c index 8d08ae2..47d27b9 100644 --- a/src/zix/digest.c +++ b/src/zix/digest.c @@ -17,7 +17,7 @@ #include "zix/digest.h" #ifdef __SSE4_2__ -# include +# include #endif #include @@ -30,75 +30,75 @@ uint32_t zix_digest_start(void) { - return 1; + return 1; } uint32_t zix_digest_add(uint32_t hash, const void* const buf, const size_t len) { - const uint8_t* str = (const uint8_t*)buf; - -#ifdef __x86_64__ - for (size_t i = 0; i < (len / sizeof(uint64_t)); ++i) { - hash = (uint32_t)_mm_crc32_u64(hash, *(const uint64_t*)str); - str += sizeof(uint64_t); - } - if (len & sizeof(uint32_t)) { - hash = _mm_crc32_u32(hash, *(const uint32_t*)str); - str += sizeof(uint32_t); - } -#else - for (size_t i = 0; i < (len / sizeof(uint32_t)); ++i) { - hash = _mm_crc32_u32(hash, *(const uint32_t*)str); - str += sizeof(uint32_t); - } -#endif - if (len & sizeof(uint16_t)) { - hash = _mm_crc32_u16(hash, *(const uint16_t*)str); - str += sizeof(uint16_t); - } - if (len & sizeof(uint8_t)) { - hash = _mm_crc32_u8(hash, *(const uint8_t*)str); - } - - return hash; + const uint8_t* str = (const uint8_t*)buf; + +# ifdef __x86_64__ + for (size_t i = 0; i < (len / sizeof(uint64_t)); ++i) { + hash = (uint32_t)_mm_crc32_u64(hash, *(const uint64_t*)str); + str += sizeof(uint64_t); + } + if (len & sizeof(uint32_t)) { + hash = _mm_crc32_u32(hash, *(const uint32_t*)str); + str += sizeof(uint32_t); + } +# else + for (size_t i = 0; i < (len / sizeof(uint32_t)); ++i) { + hash = _mm_crc32_u32(hash, *(const uint32_t*)str); + str += sizeof(uint32_t); + } +# endif + if (len & sizeof(uint16_t)) { + hash = _mm_crc32_u16(hash, *(const uint16_t*)str); + str += sizeof(uint16_t); + } + if (len & sizeof(uint8_t)) { + hash = _mm_crc32_u8(hash, *(const uint8_t*)str); + } + + return hash; } uint32_t zix_digest_add_64(uint32_t hash, const void* const buf, const size_t len) { - assert((uintptr_t)buf % sizeof(uint64_t) == 0); - assert(len % sizeof(uint64_t) == 0); + assert((uintptr_t)buf % sizeof(uint64_t) == 0); + assert(len % sizeof(uint64_t) == 0); -#ifdef __x86_64__ - const uint64_t* ptr = (const uint64_t*)buf; +# ifdef __x86_64__ + const uint64_t* ptr = (const uint64_t*)buf; - for (size_t i = 0; i < (len / sizeof(uint64_t)); ++i) { - hash = (uint32_t)_mm_crc32_u64(hash, *ptr); - ++ptr; - } + for (size_t i = 0; i < (len / sizeof(uint64_t)); ++i) { + hash = (uint32_t)_mm_crc32_u64(hash, *ptr); + ++ptr; + } - return hash; -#else - const uint32_t* ptr = (const uint32_t*)buf; + return hash; +# else + const uint32_t* ptr = (const uint32_t*)buf; - for (size_t i = 0; i < (len / sizeof(uint32_t)); ++i) { - hash = _mm_crc32_u32(hash, *ptr); - ++ptr; - } + for (size_t i = 0; i < (len / sizeof(uint32_t)); ++i) { + hash = _mm_crc32_u32(hash, *ptr); + ++ptr; + } - return hash; -#endif + return hash; +# endif } uint32_t zix_digest_add_ptr(const uint32_t hash, const void* const ptr) { -#ifdef __x86_64__ - return (uint32_t)_mm_crc32_u64(hash, (uintptr_t)ptr); -#else - return _mm_crc32_u32(hash, (uintptr_t)ptr); -#endif +# ifdef __x86_64__ + return (uint32_t)_mm_crc32_u64(hash, (uintptr_t)ptr); +# else + return _mm_crc32_u32(hash, (uintptr_t)ptr); +# endif } #else @@ -108,34 +108,34 @@ zix_digest_add_ptr(const uint32_t hash, const void* const ptr) uint32_t zix_digest_start(void) { - return 5381; + return 5381; } uint32_t zix_digest_add(uint32_t hash, const void* const buf, const size_t len) { - const uint8_t* str = (const uint8_t*)buf; + const uint8_t* str = (const uint8_t*)buf; - for (size_t i = 0; i < len; ++i) { - hash = (hash << 5u) + hash + str[i]; - } + for (size_t i = 0; i < len; ++i) { + hash = (hash << 5u) + hash + str[i]; + } - return hash; + return hash; } uint32_t zix_digest_add_64(uint32_t hash, const void* const buf, const size_t len) { - assert((uintptr_t)buf % sizeof(uint64_t) == 0); - assert(len % sizeof(uint64_t) == 0); + assert((uintptr_t)buf % sizeof(uint64_t) == 0); + assert(len % sizeof(uint64_t) == 0); - return zix_digest_add(hash, buf, len); + return zix_digest_add(hash, buf, len); } uint32_t zix_digest_add_ptr(const uint32_t hash, const void* const ptr) { - return zix_digest_add(hash, &ptr, sizeof(ptr)); + return zix_digest_add(hash, &ptr, sizeof(ptr)); } #endif diff --git a/src/zix/digest.h b/src/zix/digest.h index 74d13f9..1fde77a 100644 --- a/src/zix/digest.h +++ b/src/zix/digest.h @@ -29,7 +29,8 @@ extern "C" { /** Return an initial empty digest value. */ -ZIX_CONST_API uint32_t +ZIX_CONST_API +uint32_t zix_digest_start(void); /** @@ -37,7 +38,8 @@ zix_digest_start(void); This can be used for any size or alignment. */ -ZIX_PURE_API uint32_t +ZIX_PURE_API +uint32_t zix_digest_add(uint32_t hash, const void* buf, size_t len); /** @@ -45,7 +47,8 @@ zix_digest_add(uint32_t hash, const void* buf, size_t len); Both `buf` and `len` must be evenly divisible by 8 (64 bits). */ -ZIX_PURE_API uint32_t +ZIX_PURE_API +uint32_t zix_digest_add_64(uint32_t hash, const void* buf, size_t len); /** @@ -53,11 +56,12 @@ zix_digest_add_64(uint32_t hash, const void* buf, size_t len); This hashes the value of the pointer itself, and does not dereference `ptr`. */ -ZIX_CONST_API uint32_t +ZIX_CONST_API +uint32_t zix_digest_add_ptr(uint32_t hash, const void* ptr); #ifdef __cplusplus -} /* extern "C" */ +} /* extern "C" */ #endif -#endif /* ZIX_DIGEST_H */ +#endif /* ZIX_DIGEST_H */ diff --git a/src/zix/hash.c b/src/zix/hash.c index a498b0f..8183082 100644 --- a/src/zix/hash.c +++ b/src/zix/hash.c @@ -1,5 +1,5 @@ /* - Copyright 2011-2018 David Robillard + Copyright 2011-2020 David Robillard Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -25,109 +25,107 @@ from powers of two as possible. */ static const unsigned sizes[] = { - 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157, 98317, - 196613, 393241, 786433, 1572869, 3145739, 6291469, 12582917, 25165843, - 50331653, 100663319, 201326611, 402653189, 805306457, 1610612741, 0 -}; + 53, 97, 193, 389, 769, 1543, 3079, + 6151, 12289, 24593, 49157, 98317, 196613, 393241, + 786433, 1572869, 3145739, 6291469, 12582917, 25165843, 50331653, + 100663319, 201326611, 402653189, 805306457, 1610612741, 0}; typedef struct ZixHashEntry { - struct ZixHashEntry* next; ///< Next entry in bucket - uint32_t hash; ///< Non-modulo hash value - // Value follows here (access with zix_hash_value) + struct ZixHashEntry* next; ///< Next entry in bucket + uint32_t hash; ///< Non-modulo hash value + // Value follows here (access with zix_hash_value) } ZixHashEntry; struct ZixHashImpl { - ZixHashFunc hash_func; - ZixEqualFunc equal_func; - ZixHashEntry** buckets; - const unsigned* n_buckets; - size_t value_size; - unsigned count; + ZixHashFunc hash_func; + ZixEqualFunc equal_func; + ZixHashEntry** buckets; + const unsigned* n_buckets; + size_t value_size; + unsigned count; }; static inline void* zix_hash_value(ZixHashEntry* entry) { - return entry + 1; + return entry + 1; } ZixHash* -zix_hash_new(ZixHashFunc hash_func, - ZixEqualFunc equal_func, - size_t value_size) +zix_hash_new(ZixHashFunc hash_func, ZixEqualFunc equal_func, size_t value_size) { - ZixHash* hash = (ZixHash*)malloc(sizeof(ZixHash)); - if (hash) { - hash->hash_func = hash_func; - hash->equal_func = equal_func; - hash->n_buckets = &sizes[0]; - hash->value_size = value_size; - hash->count = 0; - if (!(hash->buckets = (ZixHashEntry**)calloc(*hash->n_buckets, - sizeof(ZixHashEntry*)))) { - free(hash); - return NULL; - } - } - return hash; + ZixHash* hash = (ZixHash*)malloc(sizeof(ZixHash)); + if (hash) { + hash->hash_func = hash_func; + hash->equal_func = equal_func; + hash->n_buckets = &sizes[0]; + hash->value_size = value_size; + hash->count = 0; + if (!(hash->buckets = + (ZixHashEntry**)calloc(*hash->n_buckets, sizeof(ZixHashEntry*)))) { + free(hash); + return NULL; + } + } + return hash; } void zix_hash_free(ZixHash* hash) { - if (!hash) { - return; - } - - for (unsigned b = 0; b < *hash->n_buckets; ++b) { - ZixHashEntry* bucket = hash->buckets[b]; - for (ZixHashEntry* e = bucket; e;) { - ZixHashEntry* next = e->next; - free(e); - e = next; - } - } - - free(hash->buckets); - free(hash); + if (!hash) { + return; + } + + for (unsigned b = 0; b < *hash->n_buckets; ++b) { + ZixHashEntry* bucket = hash->buckets[b]; + for (ZixHashEntry* e = bucket; e;) { + ZixHashEntry* next = e->next; + free(e); + e = next; + } + } + + free(hash->buckets); + free(hash); } size_t zix_hash_size(const ZixHash* hash) { - return hash->count; + return hash->count; } static inline void insert_entry(ZixHashEntry** bucket, ZixHashEntry* entry) { - entry->next = *bucket; - *bucket = entry; + entry->next = *bucket; + *bucket = entry; } static inline ZixStatus rehash(ZixHash* hash, unsigned new_n_buckets) { - ZixHashEntry** new_buckets = (ZixHashEntry**)calloc( - new_n_buckets, sizeof(ZixHashEntry*)); - if (!new_buckets) { - return ZIX_STATUS_NO_MEM; - } - - const unsigned old_n_buckets = *hash->n_buckets; - for (unsigned b = 0; b < old_n_buckets; ++b) { - for (ZixHashEntry* e = hash->buckets[b]; e;) { - ZixHashEntry* const next = e->next; - const unsigned h = e->hash % new_n_buckets; - insert_entry(&new_buckets[h], e); - e = next; - } - } - - free(hash->buckets); - hash->buckets = new_buckets; - - return ZIX_STATUS_SUCCESS; + ZixHashEntry** new_buckets = + (ZixHashEntry**)calloc(new_n_buckets, sizeof(ZixHashEntry*)); + if (!new_buckets) { + return ZIX_STATUS_NO_MEM; + } + + const unsigned old_n_buckets = *hash->n_buckets; + for (unsigned b = 0; b < old_n_buckets; ++b) { + for (ZixHashEntry* e = hash->buckets[b]; e;) { + ZixHashEntry* const next = e->next; + const unsigned h = e->hash % new_n_buckets; + insert_entry(&new_buckets[h], e); + e = next; + } + } + + free(hash->buckets); + hash->buckets = new_buckets; + + return ZIX_STATUS_SUCCESS; } static inline ZixHashEntry* @@ -136,100 +134,97 @@ find_entry(const ZixHash* hash, const unsigned h, const unsigned h_nomod) { - for (ZixHashEntry* e = hash->buckets[h]; e; e = e->next) { - if (e->hash == h_nomod && hash->equal_func(zix_hash_value(e), key)) { - return e; - } - } - return NULL; + for (ZixHashEntry* e = hash->buckets[h]; e; e = e->next) { + if (e->hash == h_nomod && hash->equal_func(zix_hash_value(e), key)) { + return e; + } + } + return NULL; } void* zix_hash_find(const ZixHash* hash, const void* value) { - const unsigned h_nomod = hash->hash_func(value); - const unsigned h = h_nomod % *hash->n_buckets; - ZixHashEntry* const entry = find_entry(hash, value, h, h_nomod); - return entry ? zix_hash_value(entry) : 0; + const unsigned h_nomod = hash->hash_func(value); + const unsigned h = h_nomod % *hash->n_buckets; + ZixHashEntry* const entry = find_entry(hash, value, h, h_nomod); + return entry ? zix_hash_value(entry) : 0; } ZixStatus zix_hash_insert(ZixHash* hash, const void* value, void** inserted) { - unsigned h_nomod = hash->hash_func(value); - unsigned h = h_nomod % *hash->n_buckets; - - ZixHashEntry* elem = find_entry(hash, value, h, h_nomod); - if (elem) { - assert(elem->hash == h_nomod); - if (inserted) { - *inserted = zix_hash_value(elem); - } - return ZIX_STATUS_EXISTS; - } - - elem = (ZixHashEntry*)malloc(sizeof(ZixHashEntry) + hash->value_size); - if (!elem) { - return ZIX_STATUS_NO_MEM; - } - elem->next = NULL; - elem->hash = h_nomod; - memcpy(elem + 1, value, hash->value_size); - - const unsigned next_n_buckets = *(hash->n_buckets + 1); - if (next_n_buckets != 0 && (hash->count + 1) >= next_n_buckets) { - if (!rehash(hash, next_n_buckets)) { - h = h_nomod % *(++hash->n_buckets); - } - } - - insert_entry(&hash->buckets[h], elem); - ++hash->count; - if (inserted) { - *inserted = zix_hash_value(elem); - } - return ZIX_STATUS_SUCCESS; + unsigned h_nomod = hash->hash_func(value); + unsigned h = h_nomod % *hash->n_buckets; + + ZixHashEntry* elem = find_entry(hash, value, h, h_nomod); + if (elem) { + assert(elem->hash == h_nomod); + if (inserted) { + *inserted = zix_hash_value(elem); + } + return ZIX_STATUS_EXISTS; + } + + elem = (ZixHashEntry*)malloc(sizeof(ZixHashEntry) + hash->value_size); + if (!elem) { + return ZIX_STATUS_NO_MEM; + } + elem->next = NULL; + elem->hash = h_nomod; + memcpy(elem + 1, value, hash->value_size); + + const unsigned next_n_buckets = *(hash->n_buckets + 1); + if (next_n_buckets != 0 && (hash->count + 1) >= next_n_buckets) { + if (!rehash(hash, next_n_buckets)) { + h = h_nomod % *(++hash->n_buckets); + } + } + + insert_entry(&hash->buckets[h], elem); + ++hash->count; + if (inserted) { + *inserted = zix_hash_value(elem); + } + return ZIX_STATUS_SUCCESS; } ZixStatus zix_hash_remove(ZixHash* hash, const void* value) { - const unsigned h_nomod = hash->hash_func(value); - const unsigned h = h_nomod % *hash->n_buckets; - - ZixHashEntry** next_ptr = &hash->buckets[h]; - for (ZixHashEntry* e = hash->buckets[h]; e; e = e->next) { - if (h_nomod == e->hash && - hash->equal_func(zix_hash_value(e), value)) { - *next_ptr = e->next; - free(e); - return ZIX_STATUS_SUCCESS; - } - next_ptr = &e->next; - } - - if (hash->n_buckets != sizes) { - const unsigned prev_n_buckets = *(hash->n_buckets - 1); - if (hash->count - 1 <= prev_n_buckets) { - if (!rehash(hash, prev_n_buckets)) { - --hash->n_buckets; - } - } - } - - --hash->count; - return ZIX_STATUS_NOT_FOUND; + const unsigned h_nomod = hash->hash_func(value); + const unsigned h = h_nomod % *hash->n_buckets; + + ZixHashEntry** next_ptr = &hash->buckets[h]; + for (ZixHashEntry* e = hash->buckets[h]; e; e = e->next) { + if (h_nomod == e->hash && hash->equal_func(zix_hash_value(e), value)) { + *next_ptr = e->next; + free(e); + return ZIX_STATUS_SUCCESS; + } + next_ptr = &e->next; + } + + if (hash->n_buckets != sizes) { + const unsigned prev_n_buckets = *(hash->n_buckets - 1); + if (hash->count - 1 <= prev_n_buckets) { + if (!rehash(hash, prev_n_buckets)) { + --hash->n_buckets; + } + } + } + + --hash->count; + return ZIX_STATUS_NOT_FOUND; } void -zix_hash_foreach(ZixHash* hash, - ZixHashVisitFunc f, - void* user_data) +zix_hash_foreach(ZixHash* hash, ZixHashVisitFunc f, void* user_data) { - for (unsigned b = 0; b < *hash->n_buckets; ++b) { - ZixHashEntry* bucket = hash->buckets[b]; - for (ZixHashEntry* e = bucket; e; e = e->next) { - f(zix_hash_value(e), user_data); - } - } + for (unsigned b = 0; b < *hash->n_buckets; ++b) { + ZixHashEntry* bucket = hash->buckets[b]; + for (ZixHashEntry* e = bucket; e; e = e->next) { + f(zix_hash_value(e), user_data); + } + } } diff --git a/src/zix/hash.h b/src/zix/hash.h index ce7fdc6..3905038 100644 --- a/src/zix/hash.h +++ b/src/zix/hash.h @@ -1,5 +1,5 @@ /* - Copyright 2011-2015 David Robillard + Copyright 2011-2020 David Robillard Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -43,8 +43,7 @@ typedef uint32_t (*ZixHashFunc)(const void* value); /** Function to visit a hash element. */ -typedef void (*ZixHashVisitFunc)(void* value, - void* user_data); +typedef void (*ZixHashVisitFunc)(void* value, void* user_data); /** Create a new hash table. @@ -59,21 +58,22 @@ typedef void (*ZixHashVisitFunc)(void* value, @param equal_func A function to test value equality. @param value_size The size of the values to be stored. */ -ZIX_API ZixHash* -zix_hash_new(ZixHashFunc hash_func, - ZixEqualFunc equal_func, - size_t value_size); +ZIX_API +ZixHash* +zix_hash_new(ZixHashFunc hash_func, ZixEqualFunc equal_func, size_t value_size); /** Free `hash`. */ -ZIX_API void +ZIX_API +void zix_hash_free(ZixHash* hash); /** Return the number of elements in `hash`. */ -ZIX_PURE_API size_t +ZIX_PURE_API +size_t zix_hash_size(const ZixHash* hash); /** @@ -90,10 +90,9 @@ zix_hash_size(const ZixHash* hash); @param inserted The copy of `value` in the hash table. @return ZIX_STATUS_SUCCESS, ZIX_STATUS_EXISTS, or ZIX_STATUS_NO_MEM. */ -ZIX_API ZixStatus -zix_hash_insert(ZixHash* hash, - const void* value, - void** inserted); +ZIX_API +ZixStatus +zix_hash_insert(ZixHash* hash, const void* value, void** inserted); /** Remove an item from `hash`. @@ -102,9 +101,9 @@ zix_hash_insert(ZixHash* hash, @param value The value to remove. @return ZIX_STATUS_SUCCES or ZIX_STATUS_NOT_FOUND. */ -ZIX_API ZixStatus -zix_hash_remove(ZixHash* hash, - const void* value); +ZIX_API +ZixStatus +zix_hash_remove(ZixHash* hash, const void* value); /** Search for an item in `hash`. @@ -112,9 +111,9 @@ zix_hash_remove(ZixHash* hash, @param hash The hash table. @param value The value to search for. */ -ZIX_API void* -zix_hash_find(const ZixHash* hash, - const void* value); +ZIX_API +void* +zix_hash_find(const ZixHash* hash, const void* value); /** Call `f` on each value in `hash`. @@ -123,10 +122,9 @@ zix_hash_find(const ZixHash* hash, @param f The function to call on each value. @param user_data The user_data parameter passed to `f`. */ -ZIX_API void -zix_hash_foreach(ZixHash* hash, - ZixHashVisitFunc f, - void* user_data); +ZIX_API +void +zix_hash_foreach(ZixHash* hash, ZixHashVisitFunc f, void* user_data); /** @} @@ -134,7 +132,7 @@ zix_hash_foreach(ZixHash* hash, */ #ifdef __cplusplus -} /* extern "C" */ +} /* extern "C" */ #endif -#endif /* ZIX_HASH_H */ +#endif /* ZIX_HASH_H */ 2.29.2