| @@ -326,6 +326,14 @@ lilv_world_find_nodes_internal(LilvWorld* world, | |||
| const SordNode* predicate, | |||
| const SordNode* object); | |||
| SordModel* | |||
| lilv_world_filter_model(LilvWorld* world, | |||
| SordModel* model, | |||
| const SordNode* subject, | |||
| const SordNode* predicate, | |||
| const SordNode* object, | |||
| const SordNode* graph); | |||
| #define FOREACH_MATCH(iter) \ | |||
| for (; !sord_iter_end(iter); sord_iter_next(iter)) | |||
| @@ -142,12 +142,15 @@ lilv_plugin_load(LilvPlugin* p) | |||
| SerdReader* reader = sord_new_reader(p->world->model, env, SERD_TURTLE, | |||
| bundle_uri_node); | |||
| SordIter* prototypes = sord_search(p->world->model, | |||
| p->plugin_uri->node, | |||
| p->world->uris.lv2_prototype, | |||
| NULL, NULL); | |||
| FOREACH_MATCH(prototypes) { | |||
| const SordNode* t = sord_iter_get_node(prototypes, SORD_OBJECT); | |||
| SordModel* prototypes = lilv_world_filter_model(p->world, | |||
| p->world->model, | |||
| p->plugin_uri->node, | |||
| p->world->uris.lv2_prototype, | |||
| NULL, NULL); | |||
| SordModel* skel = sord_new(p->world->world, SORD_SPO, false); | |||
| SordIter* iter = sord_begin(prototypes); | |||
| for (; !sord_iter_end(iter); sord_iter_next(iter)) { | |||
| const SordNode* t = sord_iter_get_node(iter, SORD_OBJECT); | |||
| LilvNode* prototype = lilv_node_new_from_node(p->world, t); | |||
| lilv_world_load_resource(p->world, prototype); | |||
| @@ -158,13 +161,22 @@ lilv_plugin_load(LilvPlugin* p) | |||
| SordQuad quad; | |||
| sord_iter_get(statements, quad); | |||
| quad[0] = p->plugin_uri->node; | |||
| sord_add(p->world->model, quad); | |||
| sord_add(skel, quad); | |||
| } | |||
| sord_iter_free(statements); | |||
| lilv_node_free(prototype); | |||
| } | |||
| sord_iter_free(prototypes); | |||
| sord_iter_free(iter); | |||
| for (iter = sord_begin(skel); !sord_iter_end(iter); sord_iter_next(iter)) { | |||
| SordQuad quad; | |||
| sord_iter_get(iter, quad); | |||
| sord_add(p->world->model, quad); | |||
| } | |||
| sord_iter_free(iter); | |||
| sord_free(skel); | |||
| sord_free(prototypes); | |||
| // Parse all the plugin's data files into RDF model | |||
| LILV_FOREACH(nodes, i, p->data_uris) { | |||
| @@ -241,6 +241,25 @@ lilv_world_ask(LilvWorld* world, | |||
| world->model, subject->node, predicate->node, object->node, NULL); | |||
| } | |||
| SordModel* | |||
| lilv_world_filter_model(LilvWorld* world, | |||
| SordModel* model, | |||
| const SordNode* subject, | |||
| const SordNode* predicate, | |||
| const SordNode* object, | |||
| const SordNode* graph) | |||
| { | |||
| SordModel* results = sord_new(world->world, SORD_SPO, false); | |||
| SordIter* i = sord_search(model, subject, predicate, object, graph); | |||
| for (; !sord_iter_end(i); sord_iter_next(i)) { | |||
| SordQuad quad; | |||
| sord_iter_get(i, quad); | |||
| sord_add(results, quad); | |||
| } | |||
| sord_iter_free(i); | |||
| return results; | |||
| } | |||
| LilvNodes* | |||
| lilv_world_find_nodes_internal(LilvWorld* world, | |||
| const SordNode* subject, | |||
| @@ -508,6 +527,7 @@ lilv_world_load_dyn_manifest(LilvWorld* world, | |||
| rewind(fd); | |||
| // Parse generated data file | |||
| // FIXME | |||
| const SerdNode* base = sord_node_to_serd_node(dmanifest); | |||
| SerdEnv* env = serd_env_new(base); | |||
| SerdReader* reader = sord_new_reader( | |||
| @@ -606,15 +626,13 @@ static int | |||
| lilv_world_drop_graph(LilvWorld* world, LilvNode* graph) | |||
| { | |||
| SordIter* i = sord_search(world->model, NULL, NULL, NULL, graph->node); | |||
| while (!sord_iter_end(i)) { | |||
| // Get quad and increment iter so sord_remove doesn't invalidate it | |||
| SordQuad quad; | |||
| sord_iter_get(i, quad); | |||
| sord_iter_next(i); | |||
| // Remove quad (nodes may now be deleted, quad is invalid) | |||
| sord_remove(world->model, quad); | |||
| const SerdStatus st = sord_erase(world->model, i); | |||
| if (st) { | |||
| LILV_ERRORF("Error removing statement from <%s> (%s)\n", | |||
| lilv_node_as_uri(graph), serd_strerror(st)); | |||
| return st; | |||
| } | |||
| } | |||
| sord_iter_free(i); | |||
| @@ -825,13 +843,16 @@ lilv_world_load_resource(LilvWorld* world, | |||
| return -1; | |||
| } | |||
| SordModel* files = lilv_world_filter_model(world, | |||
| world->model, | |||
| resource->node, | |||
| world->uris.rdfs_seeAlso, | |||
| NULL, NULL); | |||
| SordIter* f = sord_begin(files); | |||
| int n_read = 0; | |||
| SordIter* files = sord_search(world->model, | |||
| resource->node, | |||
| world->uris.rdfs_seeAlso, | |||
| NULL, NULL); | |||
| FOREACH_MATCH(files) { | |||
| const SordNode* file = sord_iter_get_node(files, SORD_OBJECT); | |||
| FOREACH_MATCH(f) { | |||
| const SordNode* file = sord_iter_get_node(f, SORD_OBJECT); | |||
| const uint8_t* file_str = sord_node_get_string(file); | |||
| LilvNode* file_node = lilv_node_new_from_node(world, file); | |||
| if (sord_node_get_type(file) != SORD_URI) { | |||
| @@ -841,8 +862,9 @@ lilv_world_load_resource(LilvWorld* world, | |||
| } | |||
| lilv_node_free(file_node); | |||
| } | |||
| sord_iter_free(files); | |||
| sord_iter_free(f); | |||
| sord_free(files); | |||
| return n_read; | |||
| } | |||
| @@ -856,13 +878,16 @@ lilv_world_unload_resource(LilvWorld* world, | |||
| return -1; | |||
| } | |||
| SordModel* files = lilv_world_filter_model(world, | |||
| world->model, | |||
| resource->node, | |||
| world->uris.rdfs_seeAlso, | |||
| NULL, NULL); | |||
| SordIter* f = sord_begin(files); | |||
| int n_dropped = 0; | |||
| SordIter* files = sord_search(world->model, | |||
| resource->node, | |||
| world->uris.rdfs_seeAlso, | |||
| NULL, NULL); | |||
| FOREACH_MATCH(files) { | |||
| const SordNode* file = sord_iter_get_node(files, SORD_OBJECT); | |||
| FOREACH_MATCH(f) { | |||
| const SordNode* file = sord_iter_get_node(f, SORD_OBJECT); | |||
| LilvNode* file_node = lilv_node_new_from_node(world, file); | |||
| if (sord_node_get_type(file) != SORD_URI) { | |||
| LILV_ERRORF("rdfs:seeAlso node `%s' is not a URI\n", | |||
| @@ -873,8 +898,9 @@ lilv_world_unload_resource(LilvWorld* world, | |||
| } | |||
| lilv_node_free(file_node); | |||
| } | |||
| sord_iter_free(files); | |||
| sord_iter_free(f); | |||
| sord_free(files); | |||
| return n_dropped; | |||
| } | |||
| @@ -159,6 +159,7 @@ cleanup(void) | |||
| #define TEST_ASSERT(check) do {\ | |||
| test_count++;\ | |||
| if (!(check)) {\ | |||
| assert(false);\ | |||
| error_count++;\ | |||
| fprintf(stderr, "lilv_test.c:%d: error: %s\n", __LINE__, #check);\ | |||
| }\ | |||
| @@ -1,8 +1,9 @@ | |||
| sord (0.12.3) unstable; | |||
| sord (0.13.0) unstable; | |||
| * Reduce memory usage and increase performance with a better data structure | |||
| * Add sord_erase() for erasing statements via an iterator. | |||
| -- David Robillard <d@drobilla.net> Tue, 23 Sep 2014 00:27:22 -0400 | |||
| -- David Robillard <d@drobilla.net> Wed, 24 Sep 2014 14:58:49 -0400 | |||
| sord (0.12.2) stable; | |||
| @@ -443,6 +443,8 @@ sord_contains(SordModel* model, const SordQuad pat); | |||
| /** | |||
| Add a quad to a model. | |||
| Calling this function invalidates all iterators on `model`. | |||
| */ | |||
| SORD_API | |||
| bool | |||
| @@ -451,12 +453,25 @@ sord_add(SordModel* model, const SordQuad quad); | |||
| /** | |||
| Remove a quad from a model. | |||
| Note that is it illegal to remove while iterating over `model`. | |||
| Calling this function invalidates all iterators on `model`. To remove quads | |||
| while iterating, use sord_erase() instead. | |||
| */ | |||
| SORD_API | |||
| void | |||
| sord_remove(SordModel* model, const SordQuad quad); | |||
| /** | |||
| Remove a quad from a model via an iterator. | |||
| Calling this function invalidates all iterators on `model` except `iter`. | |||
| @param iter Iterator to the element to erase, which is incremented to the | |||
| next value on return. | |||
| */ | |||
| SORD_API | |||
| SerdStatus | |||
| sord_erase(SordModel* model, SordIter* iter); | |||
| /** | |||
| @} | |||
| @name Inserter | |||
| @@ -119,6 +119,7 @@ struct SordModelImpl { | |||
| ZixBTree* indices[NUM_ORDERS]; | |||
| size_t n_quads; | |||
| size_t n_iters; | |||
| }; | |||
| /** Mode for searching or iteration */ | |||
| @@ -135,7 +136,7 @@ struct SordIterImpl { | |||
| const SordModel* sord; ///< Model being iterated over | |||
| ZixBTreeIter* cur; ///< Current DB cursor | |||
| SordQuad pat; ///< Pattern (in ordering order) | |||
| int ordering[TUP_LEN]; ///< Store ordering | |||
| SordOrder order; ///< Store order (which index) | |||
| SearchMode mode; ///< Iteration mode | |||
| int n_prefix; ///< Prefix for RANGE and FILTER_RANGE | |||
| bool end; ///< True iff reached end | |||
| @@ -370,7 +371,7 @@ sord_iter_seek_match_range(SordIter* iter) | |||
| return false; // Found match | |||
| for (int i = 0; i < iter->n_prefix; ++i) { | |||
| const int idx = iter->ordering[i]; | |||
| const int idx = orderings[iter->order][i]; | |||
| if (!sord_id_match(key[idx], iter->pat[idx])) { | |||
| iter->end = true; // Reached end of valid range | |||
| return true; | |||
| @@ -385,18 +386,16 @@ static SordIter* | |||
| sord_iter_new(const SordModel* sord, ZixBTreeIter* cur, const SordQuad pat, | |||
| SordOrder order, SearchMode mode, int n_prefix) | |||
| { | |||
| const int* ordering = orderings[order]; | |||
| SordIter* iter = (SordIter*)malloc(sizeof(SordIter)); | |||
| iter->sord = sord; | |||
| iter->cur = cur; | |||
| iter->order = order; | |||
| iter->mode = mode; | |||
| iter->n_prefix = n_prefix; | |||
| iter->end = false; | |||
| iter->skip_graphs = order < GSPO; | |||
| for (int i = 0; i < TUP_LEN; ++i) { | |||
| iter->pat[i] = pat[i]; | |||
| iter->ordering[i] = ordering[i]; | |||
| iter->pat[i] = pat[i]; | |||
| } | |||
| switch (iter->mode) { | |||
| @@ -422,6 +421,8 @@ sord_iter_new(const SordModel* sord, ZixBTreeIter* cur, const SordQuad pat, | |||
| (void*)iter, TUP_FMT_ARGS(pat), TUP_FMT_ARGS(value), | |||
| iter->end, iter->skip_graphs); | |||
| #endif | |||
| ++((SordModel*)sord)->n_iters; | |||
| return iter; | |||
| } | |||
| @@ -469,7 +470,7 @@ sord_iter_next(SordIter* iter) | |||
| key = (const SordNode**)zix_btree_get(iter->cur); | |||
| assert(key); | |||
| for (int i = 0; i < iter->n_prefix; ++i) { | |||
| const int idx = iter->ordering[i]; | |||
| const int idx = orderings[iter->order][i]; | |||
| if (!sord_id_match(key[idx], iter->pat[idx])) { | |||
| iter->end = true; | |||
| SORD_ITER_LOG("%p reached non-match end\n", (void*)iter); | |||
| @@ -515,6 +516,7 @@ sord_iter_free(SordIter* iter) | |||
| { | |||
| SORD_ITER_LOG("%p Free\n", (void*)iter); | |||
| if (iter) { | |||
| --((SordModel*)iter->sord)->n_iters; | |||
| zix_btree_iter_free(iter->cur); | |||
| free(iter); | |||
| } | |||
| @@ -632,6 +634,7 @@ sord_new(SordWorld* world, unsigned indices, bool graphs) | |||
| SordModel* sord = (SordModel*)malloc(sizeof(struct SordModelImpl)); | |||
| sord->world = world; | |||
| sord->n_quads = 0; | |||
| sord->n_iters = 0; | |||
| for (unsigned i = 0; i < (NUM_ORDERS / 2); ++i) { | |||
| const int* const ordering = orderings[i]; | |||
| @@ -767,7 +770,7 @@ sord_begin(const SordModel* sord) | |||
| return NULL; | |||
| } else { | |||
| ZixBTreeIter* cur = zix_btree_begin(sord->indices[DEFAULT_ORDER]); | |||
| SordQuad pat = { 0, 0, 0, 0 }; | |||
| SordQuad pat = { 0, 0, 0, 0 }; | |||
| return sord_iter_new(sord, cur, pat, DEFAULT_ORDER, ALL, 0); | |||
| } | |||
| } | |||
| @@ -1183,6 +1186,8 @@ sord_add(SordModel* sord, const SordQuad tup) | |||
| error(sord->world, SERD_ERR_BAD_ARG, | |||
| "attempt to add quad with NULL field\n"); | |||
| return false; | |||
| } else if (sord->n_iters > 0) { | |||
| error(sord->world, SERD_ERR_BAD_ARG, "added tuple during iteration\n"); | |||
| } | |||
| const SordNode** quad = (const SordNode**)malloc(sizeof(SordQuad)); | |||
| @@ -1209,11 +1214,14 @@ void | |||
| sord_remove(SordModel* sord, const SordQuad tup) | |||
| { | |||
| SORD_WRITE_LOG("Remove " TUP_FMT "\n", TUP_FMT_ARGS(tup)); | |||
| if (sord->n_iters > 0) { | |||
| error(sord->world, SERD_ERR_BAD_ARG, "remove with iterator\n"); | |||
| } | |||
| SordNode* quad = NULL; | |||
| for (unsigned i = 0; i < NUM_ORDERS; ++i) { | |||
| if (sord->indices[i]) { | |||
| if (zix_btree_remove(sord->indices[i], tup, (void**)&quad)) { | |||
| if (zix_btree_remove(sord->indices[i], tup, (void**)&quad, NULL)) { | |||
| assert(i == 0); // Assuming index coherency | |||
| return; // Quad not found, do nothing | |||
| } | |||
| @@ -1227,3 +1235,35 @@ sord_remove(SordModel* sord, const SordQuad tup) | |||
| --sord->n_quads; | |||
| } | |||
| SerdStatus | |||
| sord_erase(SordModel* sord, SordIter* iter) | |||
| { | |||
| if (sord->n_iters > 1) { | |||
| error(sord->world, SERD_ERR_BAD_ARG, "erased with many iterators\n"); | |||
| } | |||
| SordQuad tup; | |||
| sord_iter_get(iter, tup); | |||
| SORD_WRITE_LOG("Remove " TUP_FMT "\n", TUP_FMT_ARGS(tup)); | |||
| SordNode* quad = NULL; | |||
| for (unsigned i = 0; i < NUM_ORDERS; ++i) { | |||
| if (sord->indices[i]) { | |||
| if (zix_btree_remove(sord->indices[i], tup, (void**)&quad, | |||
| i == iter->order ? &iter->cur : NULL)) { | |||
| return (i == 0) ? SERD_ERR_NOT_FOUND : SERD_ERR_INTERNAL; | |||
| } | |||
| } | |||
| } | |||
| iter->end = zix_btree_iter_is_end(iter->cur); | |||
| free(quad); | |||
| for (int i = 0; i < TUP_LEN; ++i) | |||
| sord_drop_quad_ref(sord, tup[i], (SordQuadIndex)i); | |||
| --sord->n_quads; | |||
| return SERD_SUCCESS; | |||
| } | |||
| @@ -73,7 +73,7 @@ generate(SordWorld* world, | |||
| } | |||
| for (unsigned j = 0; j < n_objects_per; ++j) { | |||
| SordQuad tup = { ids[0], ids[1], ids[2 + j] }; | |||
| SordQuad tup = { ids[0], ids[1], ids[2 + j], graph }; | |||
| if (!sord_add(sord, tup)) { | |||
| return test_fail("Fail: Failed to add quad\n"); | |||
| } | |||
| @@ -91,7 +91,7 @@ generate(SordWorld* world, | |||
| tup[0] = uri(world, 98); | |||
| tup[1] = uri(world, 4); | |||
| tup[2] = sord_new_literal(world, 0, USTR("hello"), NULL); | |||
| tup[3] = 0; | |||
| tup[3] = graph; | |||
| sord_add(sord, tup); | |||
| sord_node_free(world, (SordNode*)tup[2]); | |||
| tup[2] = sord_new_literal(world, uri(world, 5), USTR("hello"), NULL); | |||
| @@ -103,7 +103,7 @@ generate(SordWorld* world, | |||
| tup[0] = uri(world, 96); | |||
| tup[1] = uri(world, 4); | |||
| tup[2] = sord_new_literal(world, uri(world, 4), USTR("hello"), NULL); | |||
| tup[3] = 0; | |||
| tup[3] = graph; | |||
| sord_add(sord, tup); | |||
| sord_node_free(world, (SordNode*)tup[2]); | |||
| tup[2] = sord_new_literal(world, uri(world, 5), USTR("hello"), NULL); | |||
| @@ -115,7 +115,7 @@ generate(SordWorld* world, | |||
| tup[0] = uri(world, 94); | |||
| tup[1] = uri(world, 5); | |||
| tup[2] = sord_new_literal(world, 0, USTR("hello"), NULL); | |||
| tup[3] = 0; | |||
| tup[3] = graph; | |||
| sord_add(sord, tup); | |||
| sord_node_free(world, (SordNode*)tup[2]); | |||
| tup[2] = sord_new_literal(world, NULL, USTR("hello"), "en-gb"); | |||
| @@ -127,7 +127,7 @@ generate(SordWorld* world, | |||
| tup[0] = uri(world, 92); | |||
| tup[1] = uri(world, 6); | |||
| tup[2] = sord_new_literal(world, 0, USTR("hello"), "en-us"); | |||
| tup[3] = 0; | |||
| tup[3] = graph; | |||
| sord_add(sord, tup); | |||
| sord_node_free(world, (SordNode*)tup[2]); | |||
| tup[2] = sord_new_literal(world, NULL, USTR("hello"), "en-gb"); | |||
| @@ -518,7 +518,7 @@ main(int argc, char** argv) | |||
| } | |||
| // Test removing | |||
| sord = sord_new(world, SORD_SPO, false); | |||
| sord = sord_new(world, SORD_SPO, true); | |||
| tup[0] = uri(world, 1); | |||
| tup[1] = uri(world, 2); | |||
| tup[2] = sord_new_literal(world, 0, USTR("hello"), NULL); | |||
| @@ -545,6 +545,36 @@ main(int argc, char** argv) | |||
| } | |||
| sord_iter_free(iter); | |||
| // Load a couple graphs | |||
| SordNode* graph42 = uri(world, 42); | |||
| SordNode* graph43 = uri(world, 43); | |||
| generate(world, sord, 1, graph42); | |||
| generate(world, sord, 1, graph43); | |||
| // Remove one graph via iterator | |||
| SerdStatus st; | |||
| iter = sord_search(sord, NULL, NULL, NULL, graph43); | |||
| while (!sord_iter_end(iter)) { | |||
| if ((st = sord_erase(sord, iter))) { | |||
| fprintf(stderr, "Remove by iterator failed (%s)\n", | |||
| serd_strerror(st)); | |||
| goto fail; | |||
| } | |||
| } | |||
| sord_iter_free(iter); | |||
| // Ensure only the other graph is left | |||
| SordQuad quad; | |||
| SordQuad pat = { 0, 0, 0, graph42 }; | |||
| for (iter = sord_begin(sord); !sord_iter_end(iter); sord_iter_next(iter)) { | |||
| sord_iter_get(iter, quad); | |||
| if (!sord_quad_match(quad, pat)) { | |||
| fprintf(stderr, "Graph removal via iteration failed\n"); | |||
| goto fail; | |||
| } | |||
| } | |||
| sord_iter_free(iter); | |||
| sord_free(sord); | |||
| sord_world_free(world); | |||
| @@ -317,6 +317,28 @@ zix_btree_insert(ZixBTree* const t, void* const e) | |||
| return ZIX_STATUS_SUCCESS; | |||
| } | |||
| ZIX_PRIVATE ZixBTreeIter* | |||
| zix_btree_iter_new(const ZixBTree* const t) | |||
| { | |||
| const size_t s = t->height * sizeof(ZixBTreeIterFrame); | |||
| ZixBTreeIter* const i = (ZixBTreeIter*)malloc(sizeof(ZixBTreeIter) + s); | |||
| if (i) { | |||
| i->level = 0; | |||
| } | |||
| return i; | |||
| } | |||
| ZIX_PRIVATE void | |||
| zix_btree_iter_set_frame(ZixBTreeIter* const ti, | |||
| ZixBTreeNode* const n, | |||
| const uint16_t i) | |||
| { | |||
| if (ti) { | |||
| ti->stack[ti->level].node = n; | |||
| ti->stack[ti->level].index = i; | |||
| } | |||
| } | |||
| ZIX_PRIVATE bool | |||
| zix_btree_node_is_minimal(ZixBTreeNode* const n) | |||
| { | |||
| @@ -450,67 +472,100 @@ zix_btree_remove_max(ZixBTree* const t, ZixBTreeNode* n) | |||
| return n->vals[--n->n_vals]; | |||
| } | |||
| ZIX_PRIVATE ZixStatus | |||
| zix_btree_remove_rec(ZixBTree* const t, | |||
| ZixBTreeNode* const n, | |||
| const void* const e, | |||
| void** const removed) | |||
| ZIX_API ZixStatus | |||
| zix_btree_remove(ZixBTree* const t, | |||
| const void* const e, | |||
| void** const out, | |||
| ZixBTreeIter** const next) | |||
| { | |||
| /* 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)); | |||
| 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; | |||
| } | |||
| bool equal = false; | |||
| const uint16_t i = zix_btree_node_find(t, n, e, &equal); | |||
| 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)); | |||
| if (n->is_leaf) { | |||
| if (equal) { | |||
| // Found in leaf node | |||
| *removed = zix_btree_aerase(n->vals, --n->n_vals, i); | |||
| return ZIX_STATUS_SUCCESS; | |||
| } else { | |||
| // Not found in leaf node, or tree | |||
| return ZIX_STATUS_NOT_FOUND; | |||
| } | |||
| } else if (equal) { | |||
| // Found in internal node | |||
| if (!zix_btree_node_is_minimal(n->children[i])) { | |||
| // Left child can remove without merge | |||
| *removed = n->vals[i]; | |||
| n->vals[i] = zix_btree_remove_max(t, n->children[i]); | |||
| return ZIX_STATUS_SUCCESS; | |||
| } else if (!zix_btree_node_is_minimal(n->children[i + 1])) { | |||
| // Right child can remove without merge | |||
| *removed = n->vals[i]; | |||
| n->vals[i] = zix_btree_remove_min(t, n->children[i + 1]); | |||
| return ZIX_STATUS_SUCCESS; | |||
| } else { | |||
| // Both preceding and succeeding child are minimal | |||
| ZixBTreeNode* const merged = zix_btree_merge(t, n, i); | |||
| return zix_btree_remove_rec(t, merged, e, removed); | |||
| } | |||
| } else { | |||
| // Not found in internal node, key is in/under children[i] | |||
| if (zix_btree_node_is_minimal(n->children[i])) { | |||
| if (i > 0 && !zix_btree_node_is_minimal(n->children[i - 1])) { | |||
| // Child's left sibling has at least one key to steal | |||
| ZixBTreeNode* const rhs = zix_btree_rotate_right(n, i); | |||
| return zix_btree_remove_rec(t, rhs, e, removed); | |||
| } else if (i < n->n_vals && | |||
| !zix_btree_node_is_minimal(n->children[i + 1])) { | |||
| // Child's right sibling has at least one key to steal | |||
| ZixBTreeNode* const lhs = zix_btree_rotate_left(n, i); | |||
| return zix_btree_remove_rec(t, lhs, e, removed); | |||
| bool equal = false; | |||
| const uint16_t 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->vals, --n->n_vals, i); | |||
| if (ti && i == n->n_vals) { | |||
| if (i == 0) { | |||
| ti->stack[ti->level = 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 | |||
| if (!zix_btree_node_is_minimal(n->children[i])) { | |||
| // Left child can remove without merge | |||
| *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])) { | |||
| // Right child can remove without merge | |||
| *out = n->vals[i]; | |||
| n->vals[i] = zix_btree_remove_min(t, n->children[i + 1]); | |||
| --t->size; | |||
| return ZIX_STATUS_SUCCESS; | |||
| } else { | |||
| // Both child's siblings are minimal, merge them | |||
| const uint16_t m = (i < n->n_vals) ? i : i - 1; | |||
| ZixBTreeNode* const merged = zix_btree_merge(t, n, m); | |||
| return zix_btree_remove_rec(t, merged, e, removed); | |||
| // Both preceding and succeeding child are minimal | |||
| n = zix_btree_merge(t, n, i); | |||
| } | |||
| } else { | |||
| return zix_btree_remove_rec(t, n->children[i], e, removed); | |||
| // Not found in internal node, key is in/under children[i] | |||
| if (zix_btree_node_is_minimal(n->children[i])) { | |||
| if (i > 0 && !zix_btree_node_is_minimal(n->children[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(n->children[i + 1])) { | |||
| // Steal a key from child's right sibling | |||
| n = zix_btree_rotate_left(n, 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); | |||
| if (ti) { | |||
| --ti->stack[ti->level].index; | |||
| } | |||
| } | |||
| } | |||
| } else { | |||
| n = n->children[i]; | |||
| } | |||
| } | |||
| if (ti) { | |||
| ++ti->level; | |||
| } | |||
| } | |||
| @@ -518,27 +573,6 @@ zix_btree_remove_rec(ZixBTree* const t, | |||
| return ZIX_STATUS_ERROR; | |||
| } | |||
| ZIX_API ZixStatus | |||
| zix_btree_remove(ZixBTree* const t, const void* const e, void** const removed) | |||
| { | |||
| const ZixStatus st = zix_btree_remove_rec(t, t->root, e, removed); | |||
| if (!st) { | |||
| --t->size; | |||
| } | |||
| return st; | |||
| } | |||
| ZIX_PRIVATE ZixBTreeIter* | |||
| zix_btree_iter_new(const ZixBTree* const t) | |||
| { | |||
| const size_t s = t->height * sizeof(ZixBTreeIterFrame); | |||
| ZixBTreeIter* const i = (ZixBTreeIter*)malloc(sizeof(ZixBTreeIter) + s); | |||
| if (i) { | |||
| i->level = 0; | |||
| } | |||
| return i; | |||
| } | |||
| ZIX_API ZixStatus | |||
| zix_btree_find(const ZixBTree* const t, | |||
| const void* const e, | |||
| @@ -553,9 +587,7 @@ zix_btree_find(const ZixBTree* const t, | |||
| bool equal = false; | |||
| const uint16_t i = zix_btree_node_find(t, n, e, &equal); | |||
| // Update iterator stack | |||
| (*ti)->stack[(*ti)->level].node = n; | |||
| (*ti)->stack[(*ti)->level].index = i; | |||
| zix_btree_iter_set_frame(*ti, n, i); | |||
| if (equal) { | |||
| return ZIX_STATUS_SUCCESS; | |||
| @@ -588,9 +620,7 @@ zix_btree_lower_bound(const ZixBTree* const t, | |||
| bool equal = false; | |||
| const uint16_t i = zix_btree_node_find(t, n, e, &equal); | |||
| // Update iterator stack | |||
| (*ti)->stack[(*ti)->level].node = n; | |||
| (*ti)->stack[(*ti)->level].index = i; | |||
| zix_btree_iter_set_frame(*ti, n, i); | |||
| if (equal) { | |||
| found_level = (*ti)->level; | |||
| @@ -78,10 +78,16 @@ ZIX_API ZixStatus | |||
| zix_btree_insert(ZixBTree* t, void* e); | |||
| /** | |||
| Remove the value `e` from `t`, set `removed` to the removed pointer. | |||
| Remove the value `e` from `t`. | |||
| @param out Set to point to the removed pointer (which may not equal `e`). | |||
| @param next If non-NULL, pointed to the value following `e`. If *next is | |||
| 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_btree_remove(ZixBTree* t, const void* e, void** removed); | |||
| zix_btree_remove(ZixBTree* t, const void* e, void** out, ZixBTreeIter** next); | |||
| /** | |||
| Set `ti` to an element equal to `e` in `t`. | |||
| @@ -10,7 +10,7 @@ import waflib.extras.autowaf as autowaf | |||
| # major increment <=> incompatible changes | |||
| # minor increment <=> compatible changes (additions) | |||
| # micro increment <=> no interface changes | |||
| SORD_VERSION = '0.12.3' | |||
| SORD_VERSION = '0.13.0' | |||
| SORD_MAJOR_VERSION = '0' | |||
| # Mandatory waf variables | |||