diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/sord.c | 2 | ||||
-rw-r--r-- | src/zix/btree.c | 170 | ||||
-rw-r--r-- | src/zix/btree.h | 29 | ||||
-rw-r--r-- | src/zix/common.h | 29 | ||||
-rw-r--r-- | src/zix/digest.h | 4 |
5 files changed, 181 insertions, 53 deletions
@@ -303,7 +303,7 @@ sord_quad_match(const SordQuad x, const SordQuad y) other possible ID, except itself. */ static int -sord_quad_compare(const void* x_ptr, const void* y_ptr, void* user_data) +sord_quad_compare(const void* x_ptr, const void* y_ptr, const void* user_data) { const int* const ordering = (const int*)user_data; const SordNode*const*const x = (const SordNode*const*)x_ptr; diff --git a/src/zix/btree.c b/src/zix/btree.c index d830008..bc07f51 100644 --- a/src/zix/btree.c +++ b/src/zix/btree.c @@ -1,5 +1,5 @@ /* - Copyright 2011-2014 David Robillard <http://drobilla.net> + Copyright 2011-2019 David Robillard <http://drobilla.net> Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -14,15 +14,15 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "zix/btree.h" + #include <assert.h> #include <stdint.h> -#include <stdio.h> #include <stdlib.h> #include <string.h> -#include "zix/btree.h" - // #define ZIX_BTREE_DEBUG 1 +// #define ZIX_BTREE_SORTED_CHECK 1 #ifndef ZIX_BTREE_PAGE_SIZE # define ZIX_BTREE_PAGE_SIZE 4096 @@ -36,7 +36,7 @@ struct ZixBTreeImpl { ZixBTreeNode* root; ZixDestroyFunc destroy; ZixComparator cmp; - void* cmp_data; + const void* cmp_data; size_t size; unsigned height; ///< Number of levels, i.e. root only has height 1 }; @@ -55,6 +55,7 @@ typedef struct { } ZixBTreeIterFrame; struct ZixBTreeIterImpl { + unsigned n_levels; ///< Maximum depth of stack unsigned level; ///< Current level in stack ZixBTreeIterFrame stack[]; ///< Position stack }; @@ -109,7 +110,7 @@ zix_btree_node_new(const bool leaf) ZIX_API ZixBTree* zix_btree_new(const ZixComparator cmp, - void* const cmp_data, + const void* const cmp_data, const ZixDestroyFunc destroy) { ZixBTree* t = (ZixBTree*)malloc(sizeof(ZixBTree)); @@ -170,14 +171,14 @@ zix_btree_max_vals(const ZixBTreeNode* const node) ZIX_PRIVATE uint16_t zix_btree_min_vals(const ZixBTreeNode* const node) { - return ((zix_btree_max_vals(node) + 1) / 2) - 1; + 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 zix_btree_ainsert(void** const array, - const uint16_t n, - const uint16_t i, + const unsigned n, + const unsigned i, void* const e) { memmove(array + i + 1, array + i, (n - i) * sizeof(e)); @@ -186,7 +187,7 @@ zix_btree_ainsert(void** const array, /** Erase element `i` in `array` of length `n` and return erased element. */ ZIX_PRIVATE void* -zix_btree_aerase(void** const array, const uint16_t n, const uint16_t i) +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)); @@ -196,12 +197,12 @@ zix_btree_aerase(void** const array, const uint16_t n, const uint16_t i) /** Split lhs, the i'th child of `n`, into two nodes. */ ZIX_PRIVATE ZixBTreeNode* zix_btree_split_child(ZixBTreeNode* const n, - const uint16_t i, + 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 + 1); + assert(i < n->n_vals + 1U); assert(n->children[i] == lhs); const uint16_t max_n_vals = zix_btree_max_vals(lhs); @@ -211,8 +212,8 @@ zix_btree_split_child(ZixBTreeNode* const n, } // LHS and RHS get roughly half, less the middle value which moves up - lhs->n_vals = max_n_vals / 2; - rhs->n_vals = max_n_vals - lhs->n_vals - 1; + lhs->n_vals = max_n_vals / 2U; + rhs->n_vals = (uint16_t)(max_n_vals - lhs->n_vals - 1); // Copy large half of values from LHS to new RHS node memcpy(rhs->vals, @@ -223,36 +224,64 @@ zix_btree_split_child(ZixBTreeNode* const n, if (!lhs->is_leaf) { memcpy(rhs->children, lhs->children + lhs->n_vals + 1, - (rhs->n_vals + 1) * sizeof(ZixBTreeNode*)); + (rhs->n_vals + 1U) * sizeof(ZixBTreeNode*)); } // Move middle value up to parent zix_btree_ainsert(n->vals, n->n_vals, i, lhs->vals[lhs->n_vals]); // Insert new RHS node in parent at position i - zix_btree_ainsert((void**)n->children, ++n->n_vals, i + 1, rhs); + zix_btree_ainsert((void**)n->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 +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; + } + + int cmp = t->cmp(n->vals[0], e, t->cmp_data); + for (uint16_t i = 1; i < n->n_vals; ++i) { + const int next_cmp = t->cmp(n->vals[i], e, t->cmp_data); + if ((cmp >= 0 && next_cmp < 0)) { + return false; + } + cmp = next_cmp; + } + + return true; +} +#endif + /** Find the first value in `n` that is not less than `e` (lower bound). */ -ZIX_PRIVATE uint16_t +ZIX_PRIVATE unsigned zix_btree_node_find(const ZixBTree* const t, const ZixBTreeNode* const n, const void* const e, bool* const equal) { - uint16_t first = 0; - uint16_t len = n->n_vals; +#ifdef ZIX_BTREE_SORTED_CHECK + 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 uint16_t half = len >> 1; - const uint16_t i = first + half; + const unsigned half = len >> 1U; + const unsigned i = first + half; const int cmp = t->cmp(n->vals[i], e, t->cmp_data); if (cmp == 0) { *equal = true; len = half; // Keep searching for wildcard matches } else if (cmp < 0) { - const uint16_t chop = half + 1; + const unsigned chop = half + 1U; first += chop; len -= chop; } else { @@ -268,7 +297,7 @@ zix_btree_insert(ZixBTree* const t, void* const e) { ZixBTreeNode* parent = NULL; // Parent of n ZixBTreeNode* n = t->root; // Current node - uint16_t i = 0; // Index of n in parent + 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 @@ -324,13 +353,17 @@ zix_btree_iter_new(const ZixBTree* const t) { const size_t s = t->height * sizeof(ZixBTreeIterFrame); - return (ZixBTreeIter*)calloc(1, sizeof(ZixBTreeIter) + s); + ZixBTreeIter* i = (ZixBTreeIter*)calloc(1, sizeof(ZixBTreeIter) + s); + if (i) { + i->n_levels = t->height; + } + return i; } ZIX_PRIVATE void zix_btree_iter_set_frame(ZixBTreeIter* const ti, ZixBTreeNode* const n, - const uint16_t i) + const unsigned i) { if (ti) { ti->stack[ti->level].node = n; @@ -347,7 +380,7 @@ zix_btree_node_is_minimal(ZixBTreeNode* const n) /** Enlarge left child by stealing a value from its right sibling. */ ZIX_PRIVATE ZixBTreeNode* -zix_btree_rotate_left(ZixBTreeNode* const parent, const uint16_t i) +zix_btree_rotate_left(ZixBTreeNode* const parent, const unsigned i) { ZixBTreeNode* const lhs = parent->children[i]; ZixBTreeNode* const rhs = parent->children[i + 1]; @@ -369,7 +402,7 @@ zix_btree_rotate_left(ZixBTreeNode* const parent, const uint16_t i) /** Enlarge right child by stealing a value from its left sibling. */ ZIX_PRIVATE ZixBTreeNode* -zix_btree_rotate_right(ZixBTreeNode* const parent, const uint16_t i) +zix_btree_rotate_right(ZixBTreeNode* const parent, const unsigned i) { ZixBTreeNode* const lhs = parent->children[i - 1]; ZixBTreeNode* const rhs = parent->children[i]; @@ -393,7 +426,7 @@ zix_btree_rotate_right(ZixBTreeNode* const parent, const uint16_t i) /** Move n[i] down, merge the left and right child, return the merged node. */ ZIX_PRIVATE ZixBTreeNode* -zix_btree_merge(ZixBTree* const t, ZixBTreeNode* const n, const uint16_t i) +zix_btree_merge(ZixBTree* const t, ZixBTreeNode* const n, const unsigned i) { ZixBTreeNode* const lhs = n->children[i]; ZixBTreeNode* const rhs = n->children[i + 1]; @@ -405,16 +438,16 @@ zix_btree_merge(ZixBTree* const t, ZixBTreeNode* const n, const uint16_t i) lhs->vals[lhs->n_vals++] = zix_btree_aerase(n->vals, n->n_vals, i); // Erase corresponding child pointer (to RHS) in parent - zix_btree_aerase((void**)n->children, n->n_vals, i + 1); + zix_btree_aerase((void**)n->children, n->n_vals, i + 1U); // Add everything from RHS to end of LHS memcpy(lhs->vals + lhs->n_vals, rhs->vals, rhs->n_vals * sizeof(void*)); if (!lhs->is_leaf) { memcpy(lhs->children + lhs->n_vals, rhs->children, - (rhs->n_vals + 1) * sizeof(void*)); + (rhs->n_vals + 1U) * sizeof(void*)); } - lhs->n_vals += rhs->n_vals; + 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 @@ -461,7 +494,7 @@ zix_btree_remove_max(ZixBTree* const t, ZixBTreeNode* n) 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 - 1); + n = zix_btree_merge(t, n, n->n_vals - 1U); } } else { n = n->children[n->n_vals]; @@ -496,7 +529,7 @@ zix_btree_remove(ZixBTree* const t, assert(n == t->root || !zix_btree_node_is_minimal(n)); bool equal = false; - const uint16_t i = zix_btree_node_find(t, n, e, &equal); + 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) { @@ -504,7 +537,8 @@ zix_btree_remove(ZixBTree* const t, *out = zix_btree_aerase(n->vals, --n->n_vals, i); if (ti && i == n->n_vals) { if (i == 0) { - ti->stack[ti->level = 0].node = NULL; + ti->level = 0; + ti->stack[0].node = NULL; } else { --ti->stack[ti->level].index; zix_btree_iter_increment(ti); @@ -522,21 +556,26 @@ zix_btree_remove(ZixBTree* const t, } } else if (equal) { // Found in internal node - if (!zix_btree_node_is_minimal(n->children[i])) { + const size_t l_size = n->children[i]->n_vals; + const size_t r_size = n->children[i + 1]->n_vals; + if (zix_btree_node_is_minimal(n->children[i]) && + zix_btree_node_is_minimal(n->children[i + 1])) { + // 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(n->children[i])); *out = n->vals[i]; n->vals[i] = zix_btree_remove_max(t, n->children[i]); --t->size; return ZIX_STATUS_SUCCESS; - } else if (!zix_btree_node_is_minimal(n->children[i + 1])) { + } else { // Right child can remove without merge + assert(!zix_btree_node_is_minimal(n->children[i + 1])); *out = n->vals[i]; n->vals[i] = zix_btree_remove_min(t, n->children[i + 1]); --t->size; return ZIX_STATUS_SUCCESS; - } else { - // Both preceding and succeeding child are minimal - n = zix_btree_merge(t, n, i); } } else { // Not found in internal node, key is in/under children[i] @@ -548,12 +587,21 @@ zix_btree_remove(ZixBTree* const t, !zix_btree_node_is_minimal(n->children[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] = {n->children[0]->n_vals, + n->children[1]->n_vals}; + + n = zix_btree_merge(t, n, 0); + 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 - 1); + n = zix_btree_merge(t, n, i - 1U); if (ti) { --ti->stack[ti->level].index; } @@ -584,7 +632,7 @@ zix_btree_find(const ZixBTree* const t, while (n) { bool equal = false; - const uint16_t i = zix_btree_node_find(t, n, e, &equal); + const unsigned i = zix_btree_node_find(t, n, e, &equal); zix_btree_iter_set_frame(*ti, n, i); @@ -622,7 +670,7 @@ zix_btree_lower_bound(const ZixBTree* const t, while (n) { bool equal = false; - const uint16_t i = zix_btree_node_find(t, n, e, &equal); + const unsigned i = zix_btree_node_find(t, n, e, &equal); zix_btree_iter_set_frame(*ti, n, i); @@ -648,6 +696,7 @@ zix_btree_lower_bound(const ZixBTree* const t, (*ti)->level = found_level; } else { // Reached end (key is greater than everything in tree) + (*ti)->level = 0; (*ti)->stack[0].node = NULL; } } @@ -671,6 +720,7 @@ zix_btree_begin(const ZixBTree* const t) if (!i) { return NULL; } else if (t->size == 0) { + i->level = 0; i->stack[0].node = NULL; } else { ZixBTreeNode* n = t->root; @@ -686,12 +736,48 @@ zix_btree_begin(const ZixBTree* const t) return i; } +ZIX_API ZixBTreeIter* +zix_btree_end(const ZixBTree* const t) +{ + return zix_btree_iter_new(t); +} + +ZIX_API ZixBTreeIter* +zix_btree_iter_copy(const ZixBTreeIter* const i) +{ + 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; +} + ZIX_API bool zix_btree_iter_is_end(const ZixBTreeIter* const i) { return !i || (i->level == 0 && i->stack[0].node == NULL); } +ZIX_API bool +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 (!lhs || !rhs || lhs->n_levels != rhs->n_levels) { + return false; + } + + return !memcmp(lhs, + rhs, + sizeof(ZixBTreeIter) + + lhs->n_levels * sizeof(ZixBTreeIterFrame)); +} + ZIX_API void zix_btree_iter_increment(ZixBTreeIter* const i) { diff --git a/src/zix/btree.h b/src/zix/btree.h index 91b38cb..4c972f6 100644 --- a/src/zix/btree.h +++ b/src/zix/btree.h @@ -17,14 +17,13 @@ #ifndef ZIX_BTREE_H #define ZIX_BTREE_H -#include <stddef.h> - #include "zix/common.h" +#include <stdbool.h> +#include <stddef.h> + #ifdef __cplusplus extern "C" { -#else -# include <stdbool.h> #endif /** @@ -57,7 +56,7 @@ typedef struct ZixBTreeIterImpl ZixBTreeIter; */ ZIX_API ZixBTree* zix_btree_new(ZixComparator cmp, - void* cmp_data, + const void* cmp_data, ZixDestroyFunc destroy); /** @@ -126,6 +125,26 @@ ZIX_API ZixBTreeIter* zix_btree_begin(const ZixBTree* t); /** + Return an iterator to the end of `t` (one past the last element). + + The returned iterator must be freed with zix_btree_iter_free(). +*/ +ZIX_API ZixBTreeIter* +zix_btree_end(const ZixBTree* t); + +/** + Return a new copy of `i`. +*/ +ZIX_API ZixBTreeIter* +zix_btree_iter_copy(const ZixBTreeIter* i); + +/** + Return true iff `lhs` is equal to `rhs`. +*/ +ZIX_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_API bool diff --git a/src/zix/common.h b/src/zix/common.h index 7e5e2f0..a59c033 100644 --- a/src/zix/common.h +++ b/src/zix/common.h @@ -1,5 +1,5 @@ /* - Copyright 2012 David Robillard <http://drobilla.net> + Copyright 2016 David Robillard <http://drobilla.net> Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -52,6 +52,12 @@ extern "C" { # include <stdbool.h> #endif +#ifdef __GNUC__ +#define ZIX_UNUSED __attribute__((__unused__)) +#else +#define ZIX_UNUSED +#endif + typedef enum { ZIX_STATUS_SUCCESS, ZIX_STATUS_ERROR, @@ -59,13 +65,30 @@ typedef enum { ZIX_STATUS_NOT_FOUND, ZIX_STATUS_EXISTS, ZIX_STATUS_BAD_ARG, - ZIX_STATUS_BAD_PERMS, + 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"; +} + /** Function for comparing two elements. */ -typedef int (*ZixComparator)(const void* a, const void* b, void* user_data); +typedef int (*ZixComparator)(const void* a, + const void* b, + const void* user_data); /** Function for testing equality of two elements. diff --git a/src/zix/digest.h b/src/zix/digest.h index 8c96492..264c918 100644 --- a/src/zix/digest.h +++ b/src/zix/digest.h @@ -17,11 +17,11 @@ #ifndef ZIX_DIGEST_H #define ZIX_DIGEST_H +#include "zix/common.h" + #include <stddef.h> #include <stdint.h> -#include "zix/common.h" - #ifdef __cplusplus extern "C" { #endif |