diff --git a/.gitignore b/.gitignore index 2f103d2..f620f64 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ /sord-0.16.2.tar.bz2 /sord-0.16.4.tar.bz2 /sord-0.16.6.tar.bz2 +/sord-0.16.8.tar.bz2 diff --git a/sord-update-zix.patch b/sord-update-zix.patch new file mode 100644 index 0000000..395dc15 --- /dev/null +++ b/sord-update-zix.patch @@ -0,0 +1,2642 @@ +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 + diff --git a/sord.spec b/sord.spec index e69bc0d..06e5d2e 100644 --- a/sord.spec +++ b/sord.spec @@ -2,13 +2,15 @@ %{!?_pkgdocdir: %global _pkgdocdir %{_docdir}/%{name}-%{version}} Name: sord -Version: 0.16.6 -Release: 2%{?dist} +Version: 0.16.8 +Release: 1%{?dist} Summary: A lightweight Resource Description Framework (RDF) C library License: ISC URL: http://drobilla.net/software/sord/ Source0: http://download.drobilla.net/%{name}-%{version}.tar.bz2 +# This fixes a potential BTree crash with GCC 10, patch from upstream +Patch0: %{name}-update-zix.patch BuildRequires: boost-devel BuildRequires: doxygen @@ -36,7 +38,7 @@ Framework (RDF) data in memory. This package contains the headers and development libraries for %{name}. %prep -%setup -q +%autosetup -p1 # Do not run ldconfig, and add our optflags sed -i -e "s|bld.add_post_fun(autowaf.run_ldconfig)||" \ -e "s|cflags = [ '-DSORD_INTERNAL' ]\ @@ -44,12 +46,6 @@ sed -i -e "s|bld.add_post_fun(autowaf.run_ldconfig)||" \ %build %set_build_flags -# Work around a possible GCC 10.1 bug -# GCC 10.1 crashes on these arches in for loop with ZixBTreeIter -%ifarch %{power64} %{arm} aarch64 s390 s390x -CFLAGS+=" -O1" -CXXFLAGS+=" -O1" -%endif export LINKFLAGS="%{__global_ldflags}" %{python3} waf configure \ --prefix=%{_prefix} \ @@ -84,6 +80,10 @@ install -pm 644 AUTHORS NEWS README.md %{buildroot}%{_pkgdocdir} %{_mandir}/man3/%{name}*.3* %changelog +* Sun Feb 28 2021 Guido Aulisi - 0.16.8-1 +- Update to 0.16.8 +- Fix a potential BTree crash with GCC 10 + * Wed Jan 27 2021 Fedora Release Engineering - 0.16.6-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild diff --git a/sources b/sources index 91c267c..4795341 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (sord-0.16.6.tar.bz2) = 1d3c2bf47ff7e4b533e4e737e2ece8e29bace78bb00c41a252ad5c583abdcba3baa05b189cb8651c212861a2eea3c690354c99d684fd0f343b40e74c94572f98 +SHA512 (sord-0.16.8.tar.bz2) = 24ed50de8e5bb321e557bac6d3e441b2ed49adabf828bf0e1b33a080c89306dde80443dc8b563098fcc184c4d6e53b7e716b523ddccdf56d08301d1b0120f2b2