diff --git a/README.md b/README.md index cdbcab7..a3414d4 100644 --- a/README.md +++ b/README.md @@ -15,14 +15,13 @@ compression reduces the tree height and horizontal compression decreases a node using namespace art; int main() { - art m; + art> m; - int v = 2; - // set k to v - m.set("k", &v); + // set k + m.set("k", std::make_shared(new MyResource())); - // get value of k - int * v_ptr = m.get("k"); + // get k + std::shared_ptr ptr = m.get("k"); // delete k m.del("k"); @@ -100,6 +99,9 @@ query sparse zipf: =============================================================================== ``` +You can replicate using `make release && make bench` + + ## References * [The Adaptive Radix Tree: ARTful Indexing for Main-Memory Databases](http://www-db.in.tum.de/~leis/papers/ART.pdf) diff --git a/bench/delete.cpp b/bench/delete.cpp index 81e3b5b..b251a33 100644 --- a/bench/delete.cpp +++ b/bench/delete.cpp @@ -15,7 +15,7 @@ using picobench::state; PICOBENCH_SUITE("delete"); static void art_delete_sparse(state &s) { - art::art m; + art::art m; int v = 1; std::mt19937_64 g1(0); for (auto i __attribute__((unused)) : s) { diff --git a/bench/insert.cpp b/bench/insert.cpp index 90d3bcc..6381fa0 100644 --- a/bench/insert.cpp +++ b/bench/insert.cpp @@ -17,7 +17,7 @@ using picobench::state; PICOBENCH_SUITE("insert"); static void art_insert_sparse(state &s) { - art::art m; + art::art m; int v = 1; std::mt19937_64 rng(0); for (auto i __attribute__((unused)) : s) { diff --git a/bench/mixed.cpp b/bench/mixed.cpp index 0cd4ff2..fbd385e 100644 --- a/bench/mixed.cpp +++ b/bench/mixed.cpp @@ -28,7 +28,7 @@ using std::unordered_map; PICOBENCH_SUITE("mixed"); static void art_mixed_sparse(state &s) { - art::art m; + art::art m; fast_zipf rng(10000000); hash h; string k; diff --git a/bench/mixed_dense.cpp b/bench/mixed_dense.cpp index 1ac6cba..621f2f5 100644 --- a/bench/mixed_dense.cpp +++ b/bench/mixed_dense.cpp @@ -44,7 +44,7 @@ static void art_mixed(state &s) { int v = 1; fast_zipf rng(n); - art::art m; + art::art m; string k; for (auto i __attribute__((unused)) : s) { k = dataset[rng()]; diff --git a/bench/query_iteration.cpp b/bench/query_iteration.cpp index f20fb7f..a2eb057 100644 --- a/bench/query_iteration.cpp +++ b/bench/query_iteration.cpp @@ -22,11 +22,11 @@ using std::unordered_map; PICOBENCH_SUITE("query iteration"); static void full_scan_zipf(state &s) { - art::art m; + art::art m; hash h; int v = 1; int *v_ptr = &v; - art::tree_it it, it_end; + art::tree_it it, it_end; fast_zipf rng(1000000); for (int i = 0; i < 100000; ++i) { m.set(to_string(h(rng())).c_str(), v_ptr); @@ -38,11 +38,11 @@ static void full_scan_zipf(state &s) { PICOBENCH(full_scan_zipf).iterations({1000}); static void full_scan_uniform(state &s) { - art::art m; + art::art m; hash h; int v = 1; int *v_ptr = &v; - art::tree_it it, it_end; + art::tree_it it, it_end; mt19937_64 rng(0); for (int i = 0; i < 100000; ++i) { m.set(to_string(h(rng())).c_str(), v_ptr); diff --git a/bench/query_sparse_uniform.cpp b/bench/query_sparse_uniform.cpp index c5e891e..4f77df3 100644 --- a/bench/query_sparse_uniform.cpp +++ b/bench/query_sparse_uniform.cpp @@ -24,7 +24,7 @@ using std::unordered_map; PICOBENCH_SUITE("query sparse uniform"); static void art_q_s_u(state &s) { - art::art m; + art::art m; hash h; int v = 1; int *v_ptr = &v; diff --git a/bench/query_sparse_uniform_64.cpp b/bench/query_sparse_uniform_64.cpp index 7ce5d12..11eb0a1 100644 --- a/bench/query_sparse_uniform_64.cpp +++ b/bench/query_sparse_uniform_64.cpp @@ -27,7 +27,7 @@ PICOBENCH_SUITE("query sparse uniform base 64 keys"); std::string to_base64(const std::string& string_); static void art_q_s_u(state &s) { - art::art m; + art::art m; hash h; int v = 1; int *v_ptr = &v; diff --git a/bench/query_sparse_zipf.cpp b/bench/query_sparse_zipf.cpp index edcbb55..e1b2a94 100644 --- a/bench/query_sparse_zipf.cpp +++ b/bench/query_sparse_zipf.cpp @@ -23,7 +23,7 @@ using std::unordered_map; PICOBENCH_SUITE("query sparse zipf"); static void art_q_s_z(state &s) { - art::art m; + art::art m; hash h; int v = 1; int *v_ptr = &v; diff --git a/include/art/art.hpp b/include/art/art.hpp index a297fba..5ceae94 100644 --- a/include/art/art.hpp +++ b/include/art/art.hpp @@ -25,9 +25,9 @@ template class art { * Finds the value associated with the given key. * * @param key - The key to find. - * @return the value associated with the key or a nullptr. + * @return the value associated with the key or a default constructed value. */ - T *get(const char *key) const; + T get(const char *key) const; /** * Associates the given key with the given value. @@ -39,7 +39,7 @@ template class art { * @return a nullptr if no other value is associated with they or the * previously associated value. */ - T *set(const char *key, T *value); + T set(const char *key, T value); /** * Deletes the given key and returns it's associated value. @@ -50,7 +50,7 @@ template class art { * @param key - The key to delete. * @return the values assciated with they key or a nullptr otherwise. */ - T *del(const char *key); + T del(const char *key); /** * Forward iterator that traverses the tree in lexicographic order. @@ -97,33 +97,35 @@ template art::~art() { } } -template T *art::get(const char *key) const { +template +T art::get(const char *key) const { node *cur = root_, **child; int depth = 0, key_len = std::strlen(key) + 1; while (cur != nullptr) { if (cur->prefix_len_ != cur->check_prefix(key + depth, key_len - depth)) { /* prefix mismatch */ - return nullptr; + return T{}; } if (cur->prefix_len_ == key_len - depth) { /* exact match */ - return cur->is_leaf() ? static_cast*>(cur)->value_ : nullptr; + return cur->is_leaf() ? static_cast*>(cur)->value_ : T{}; } child = static_cast*>(cur)->find_child(key[depth + cur->prefix_len_]); depth += (cur->prefix_len_ + 1); cur = child != nullptr ? *child : nullptr; } - return nullptr; + return T{}; } -template T *art::set(const char *key, T *value) { +template +T art::set(const char *key, T value) { int key_len = std::strlen(key) + 1, depth = 0, prefix_match_len; if (root_ == nullptr) { root_ = new leaf_node(value); root_->prefix_ = new char[key_len]; std::copy(key, key + key_len + 1, root_->prefix_); root_->prefix_len_ = key_len; - return nullptr; + return T{}; } node **cur = &root_, **child; @@ -155,7 +157,7 @@ template T *art::set(const char *key, T *value) { /* cur must be a leaf */ auto cur_leaf = static_cast*>(*cur); - T *old_value = cur_leaf->value_; + T old_value = cur_leaf->value_; cur_leaf->value_ = value; return old_value; } @@ -205,7 +207,7 @@ template T *art::set(const char *key, T *value) { new_parent->set_child(key[depth + prefix_match_len], new_node); *cur = new_parent; - return nullptr; + return T{}; } /* must be inner node */ @@ -235,7 +237,7 @@ template T *art::set(const char *key, T *value) { new_node->prefix_); new_node->prefix_len_ = key_len - depth - (**cur).prefix_len_ - 1; (**cur_inner).set_child(child_partial_key, new_node); - return nullptr; + return T{}; } /* propagate down and repeat: @@ -251,11 +253,12 @@ template T *art::set(const char *key, T *value) { } } -template T *art::del(const char *key) { +template +T art::del(const char *key) { int depth = 0, key_len = std::strlen(key) + 1; if (root_ == nullptr) { - return nullptr; + return T{}; } /* pointer to parent, current and child node */ @@ -270,13 +273,13 @@ template T *art::del(const char *key) { (**cur).check_prefix(key + depth, key_len - depth)) { /* prefix mismatch => key doesn't exist */ - return nullptr; + return T{}; } if (key_len == depth + (**cur).prefix_len_) { /* exact match */ if (!(**cur).is_leaf()) { - return nullptr; + return T{}; } auto value = static_cast*>(*cur)->value_; auto n_siblings = par != nullptr ? (**par).n_children() - 1 : 0; @@ -374,7 +377,7 @@ template T *art::del(const char *key) { par = reinterpret_cast**>(cur); cur = (**par).find_child(cur_partial_key); } - return nullptr; + return T{}; } template tree_it art::begin() { @@ -385,7 +388,9 @@ template tree_it art::begin(const char *key) { return tree_it::greater_equal(this->root_, key); } -template tree_it art::end() { return tree_it(); } +template tree_it art::end() { + return tree_it(); +} } // namespace art diff --git a/include/art/child_it.hpp b/include/art/child_it.hpp index ee828b2..259b616 100644 --- a/include/art/child_it.hpp +++ b/include/art/child_it.hpp @@ -15,13 +15,13 @@ template class inner_node; template class child_it { public: child_it() = default; + explicit child_it(inner_node *n); + child_it(inner_node *n, int relative_index); child_it(const child_it &other) = default; child_it(child_it &&other) noexcept = default; child_it &operator=(const child_it &other) = default; child_it &operator=(child_it &&other) noexcept = default; - explicit child_it(inner_node *n); - child_it(inner_node *n, int relative_index); using iterator_category = std::bidirectional_iterator_tag; using value_type = const char; diff --git a/include/art/inner_node.hpp b/include/art/inner_node.hpp index 1003805..83e2f2e 100644 --- a/include/art/inner_node.hpp +++ b/include/art/inner_node.hpp @@ -21,13 +21,12 @@ namespace art { template class inner_node : public node { public: - virtual ~inner_node() = default; - inner_node() = default; inner_node(const inner_node &other) = default; inner_node(inner_node &&other) noexcept = default; inner_node &operator=(const inner_node &other) = default; inner_node &operator=(inner_node &&other) noexcept = default; + virtual ~inner_node() = default; bool is_leaf() const override; diff --git a/include/art/leaf_node.hpp b/include/art/leaf_node.hpp index 6ff388e..aeaaeaf 100644 --- a/include/art/leaf_node.hpp +++ b/include/art/leaf_node.hpp @@ -14,14 +14,14 @@ template class art; template class leaf_node : public node { public: - explicit leaf_node(T *value); + explicit leaf_node(T value); bool is_leaf() const override; - T *value_; + T value_; }; template -leaf_node::leaf_node(T *value): value_(value) {} +leaf_node::leaf_node(T value): value_(value) {} template bool leaf_node::is_leaf() const { return true; } diff --git a/include/art/node_16.hpp b/include/art/node_16.hpp index 93a4026..8801363 100644 --- a/include/art/node_16.hpp +++ b/include/art/node_16.hpp @@ -112,6 +112,7 @@ template inner_node *node_16::grow() { auto new_node = new node_48(); new_node->prefix_ = this->prefix_; new_node->prefix_len_ = this->prefix_len_; + new_node->n_children_ = this->n_children_; std::copy(this->children_, this->children_ + this->n_children_, new_node->children_); for (int i = 0; i < n_children_; ++i) { new_node->indexes_[(uint8_t) this->keys_[i]] = i; diff --git a/include/art/tree_it.hpp b/include/art/tree_it.hpp index d2d2c3e..4870e04 100644 --- a/include/art/tree_it.hpp +++ b/include/art/tree_it.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include namespace art { @@ -22,26 +23,34 @@ template class leaf_node; template class tree_it { public: struct step { - node *node_; + node *child_node_; // no ownership int depth_; + char *key_; // ownership child_it child_it_; child_it child_it_end_; + step(); step(int depth, child_it c_it, child_it c_it_end); - step(node *node, int depth, child_it c_it, child_it c_it_end); + step(node *node, int depth, const char *key, child_it c_it, child_it c_it_end); + step(const step &other); + step(step &&other); + ~step(); + + step& operator=(const step &other); + step& operator=(step &&other) noexcept; step &operator++(); step operator++(int); }; - tree_it() = default; - explicit tree_it(std::vector traversal_stack); + tree_it(); + explicit tree_it(node *root, std::vector traversal_stack); static tree_it min(node *root); static tree_it greater_equal(node *root, const char *key); using iterator_category = std::forward_iterator_tag; - using value_type = T *; + using value_type = T; using difference_type = int; using pointer = value_type *; /* using reference = const value_type &; */ @@ -54,32 +63,56 @@ template class tree_it { bool operator==(const tree_it &rhs) const; bool operator!=(const tree_it &rhs) const; - node *get_node() const; - int get_depth() const; + template void key(OutputIt key) const; + int get_key_len() const; + const std::string key() const; + private: step &get_step(); const step &get_step() const; + node *get_node() const; + const char *get_key() const; + int get_depth() const; + void seek_leaf(); + node *root_; std::vector traversal_stack_; }; template -tree_it::step::step(node *node, int depth, child_it c_it, child_it c_it_end) - : node_(node), depth_(depth), child_it_(c_it), child_it_end_(c_it_end) {} +tree_it::step::step() + : step(nullptr, 0, nullptr, {}, {}) {} template tree_it::step::step(int depth, child_it c_it, child_it c_it_end) - : step(c_it != c_it_end ? c_it.get_child_node() : nullptr, depth, c_it, c_it_end) {} + : child_node_(c_it != c_it_end ? c_it.get_child_node() : nullptr), + depth_(depth), + key_(depth ? new char[depth] : nullptr), + child_it_(c_it), + child_it_end_(c_it_end) {} + +template +tree_it::step::step(node *node, int depth, const char *key, child_it c_it, child_it c_it_end) + : child_node_(node), + depth_(depth), + key_(depth ? new char[depth] : nullptr), + child_it_(c_it), + child_it_end_(c_it_end) { + std::copy_n(key, depth, key_); + } template typename tree_it::step &tree_it::step::operator++() { assert(child_it_ != child_it_end_); ++child_it_; - node_ = child_it_ != child_it_end_ + child_node_ = child_it_ != child_it_end_ ? child_it_.get_child_node() : nullptr; + key_[depth_ - 1] = child_it_ != child_it_end_ + ? child_it_.get_partial_key() + : '\0'; return *this; } @@ -91,7 +124,75 @@ typename tree_it::step tree_it::step::operator++(int) { } template -tree_it::tree_it(std::vector traversal_stack) : traversal_stack_(traversal_stack) { +tree_it::step::step(const tree_it::step &other) + : step(other.child_node_, other.depth_, other.key_, other.child_it_, other.child_it_end_) {} + +template +tree_it::step::step(tree_it::step &&other) + : child_node_(other.child_node_), + depth_(other.depth_), + key_(other.key_), + child_it_(other.child_it_), + child_it_end_(other.child_it_end_) { + other.child_node_ = nullptr; + other.depth_ = 0; + other.key_ = nullptr; + other.child_it_ = {}; + other.child_it_end_ = {}; +} + +template +tree_it::step::~step() { + delete [] key_; +} + +template +typename tree_it::step& tree_it::step::operator=(const tree_it::step &other) { + if (this != &other) { + node *node = other.child_node_; + int depth = other.depth_; + char *key = depth ? new char[depth] : nullptr; + std::copy_n(other.key_, other.depth_, key); + child_it c_it = other.child_it_; + child_it c_it_end = other.child_it_end_; + + child_node_ = node; + depth_ = depth; + delete [] key_; + key_ = key; + child_it_ = c_it; + child_it_end_ = c_it_end; + } + return *this; +} + +template +typename tree_it::step& tree_it::step::operator=(tree_it::step &&other) noexcept { + if (this != &other) { + child_node_ = other.child_node_; + other.child_node_ = nullptr; + + depth_ = other.depth_; + other.depth_ = 0; + + delete [] key_; + key_ = other.key_; + other.key_ = nullptr; + + child_it_ = other.child_it_; + other.child_it_ = {}; + + child_it_end_ = other.child_it_end_; + other.child_it_end_ = {}; + } + return *this; +} + +template +tree_it::tree_it() {} + +template +tree_it::tree_it(node *root, std::vector traversal_stack) : root_(root), traversal_stack_(traversal_stack) { seek_leaf(); } @@ -104,42 +205,46 @@ tree_it tree_it::greater_equal(node *root, const char *key) { assert(root != nullptr); int key_len = std::strlen(key); - inner_node *cur_inner_node; - child_it child_it, child_it_end; std::vector::step> traversal_stack; // sentinel child iterator for root - traversal_stack.push_back({root, 0, {nullptr, -2}, {nullptr, -1}}); + traversal_stack.push_back({root, 0, nullptr, {nullptr, -2}, {nullptr, -1}}); while (true) { tree_it::step &cur_step = traversal_stack.back(); - node *cur_node = cur_step.node_; + node *cur_node = cur_step.child_node_; int cur_depth = cur_step.depth_; int prefix_match_len = std::min(cur_node->check_prefix(key + cur_depth, key_len - cur_depth), key_len - cur_depth); // if search key "equals" the prefix if (key_len == cur_depth + prefix_match_len) { - return tree_it(traversal_stack); + return tree_it(root, traversal_stack); } // if search key is "greater than" the prefix if (prefix_match_len < cur_node->prefix_len_ && key[cur_depth + prefix_match_len] > cur_node->prefix_[prefix_match_len]) { ++cur_step; - return tree_it(traversal_stack); + return tree_it(root, traversal_stack); } if (cur_node->is_leaf()) { continue; } // seek subtree where search key is "lesser than or equal" the subtree partial key - cur_inner_node = static_cast *>(cur_node); - child_it = cur_inner_node->begin(); - child_it_end = cur_inner_node->end(); + inner_node *cur_inner_node = static_cast *>(cur_node); + child_it c_it = cur_inner_node->begin(); + child_it c_it_end = cur_inner_node->end(); // TODO more efficient with specialized node search method? - for (; child_it != child_it_end; ++child_it) { - if (key[cur_depth + cur_node->prefix_len_] <= child_it.get_partial_key()) { + for (; c_it != c_it_end; ++c_it) { + if (key[cur_depth + cur_node->prefix_len_] <= c_it.get_partial_key()) { break; } } - traversal_stack.push_back({cur_depth + cur_node->prefix_len_ + 1, child_it, child_it_end}); + int depth = cur_depth + cur_node->prefix_len_ + 1; + tree_it::step child(depth, c_it, c_it_end); + /* compute child key: cur_key + cur_node->prefix_ + child_partial_key */ + std::copy_n(cur_step.key_, cur_depth, child.key_); + std::copy_n(cur_node->prefix_, cur_node->prefix_len_, child.key_ + cur_depth); + child.key_[cur_depth + cur_node->prefix_len_] = c_it.get_partial_key(); + traversal_stack.push_back(child); } } @@ -167,44 +272,48 @@ template tree_it tree_it::operator++(int) { } template bool tree_it::operator==(const tree_it &rhs) const { - return (traversal_stack_.empty() && rhs.traversal_stack_.empty()) || - (!traversal_stack_.empty() && !rhs.traversal_stack_.empty() && - get_node() == rhs.get_node()); + if (traversal_stack_.empty() && rhs.traversal_stack_.empty()) { + /* both are empty */ + return true; + } + if (traversal_stack_.empty() || rhs.traversal_stack_.empty()) { + /* one is empty */ + return false; + } + return get_node() == rhs.get_node(); } template bool tree_it::operator!=(const tree_it &rhs) const { return !(*this == rhs); } -template -node * tree_it::get_node() const { - return get_step().node_; +template +template +void tree_it::key(OutputIt key) const { + std::copy_n(get_key(), get_depth(), key); + std::copy_n(get_node()->prefix_, get_node()->prefix_len_, key + get_depth()); } template -int tree_it::get_depth() const { - return get_step().depth_; -} - -template -typename tree_it::step &tree_it::get_step() { - assert(!traversal_stack_.empty()); - return traversal_stack_.back(); +int tree_it::get_key_len() const { + return get_depth() + get_node()->prefix_len_; } -template -const typename tree_it::step &tree_it::get_step() const { - assert(!traversal_stack_.empty()); - return traversal_stack_.back(); +template +const std::string tree_it::key() const { + std::string str(get_depth() + get_node()->prefix_len_ - 1, 0); + key(str.begin()); + return str; } template void tree_it::seek_leaf() { - /* traverse up until a node on the right is found or stack gets empty */ for (; get_step().child_it_ == get_step().child_it_end_; ++get_step()) { traversal_stack_.pop_back(); - if (traversal_stack_.empty()) { + if (get_step().child_node_ == root_) { // root guard + traversal_stack_.pop_back(); + assert(traversal_stack_.empty()); return; } } @@ -212,13 +321,45 @@ void tree_it::seek_leaf() { /* find leftmost leaf node */ while (!get_node()->is_leaf()) { inner_node *cur_inner_node = static_cast *>(get_node()); - int depth = get_step().depth_ + get_node()->prefix_len_ + 1; + int depth = get_depth() + get_node()->prefix_len_ + 1; child_it c_it = cur_inner_node->begin(); child_it c_it_end = cur_inner_node->end(); - traversal_stack_.push_back({depth, c_it, c_it_end}); + tree_it::step child(depth, c_it, c_it_end); + /* compute child key: cur_key + cur_node->prefix_ + child_partial_key */ + std::copy_n(get_key(), get_depth(), child.key_); + std::copy_n(get_node()->prefix_, get_node()->prefix_len_, child.key_ + get_depth()); + child.key_[get_depth() + get_node()->prefix_len_] = c_it.get_partial_key(); + traversal_stack_.push_back(child); } } +template +node * tree_it::get_node() const { + return get_step().child_node_; +} + +template +int tree_it::get_depth() const { + return get_step().depth_; +} + +template +const char * tree_it::get_key() const { + return get_step().key_; +} + +template +typename tree_it::step &tree_it::get_step() { + assert(!traversal_stack_.empty()); + return traversal_stack_.back(); +} + +template +const typename tree_it::step &tree_it::get_step() const { + assert(!traversal_stack_.empty()); + return traversal_stack_.back(); +} + } // namespace art #endif diff --git a/src/example.cpp b/src/example.cpp index 91b3e0b..1fc59ce 100644 --- a/src/example.cpp +++ b/src/example.cpp @@ -8,6 +8,7 @@ #include #include #include +#include using std::string; @@ -22,7 +23,7 @@ void art_bench() { file.close(); fast_zipf rng(n); - art::art m; + art::art m; /* std::map m; */ /* std::unordered_map m; */ int v = 1; @@ -35,7 +36,7 @@ void art_bench() { } /* void art_sparse_uniform() { */ -/* art::art m; */ +/* art::art m; */ /* int n = 1000; */ /* int v = 1; */ /* std::mt19937_64 rng1(0); */ @@ -70,7 +71,7 @@ void art_bench() { /* } */ /* void art_compressions_dense_insert() { */ -/* art::art m; */ +/* art::art m; */ /* int v = 1; */ /* std::string k; */ /* int i; */ @@ -85,7 +86,7 @@ void art_bench() { /* } */ /* void art_compressions_dense_delete() { */ -/* art::art m; */ +/* art::art m; */ /* int v = 1; */ /* std::string k; */ /* int i; */ @@ -105,7 +106,7 @@ void art_bench() { /* } */ /* void art_compressions_paths_insert() { */ -/* art::art m; */ +/* art::art m; */ /* int v = 1; */ /* auto file = std::ifstream("dataset.txt"); */ /* std::string line; */ @@ -122,7 +123,7 @@ void art_bench() { /* } */ /* void art_compressions_paths_delete() { */ -/* art::art m; */ +/* art::art m; */ /* int v = 1; */ /* auto file = std::ifstream("dataset.txt"); */ /* std::string line; */ @@ -145,7 +146,7 @@ void art_bench() { /* } */ /* void art_compressions_sparse_insert() { */ -/* art::art m; */ +/* art::art m; */ /* int v = 1; */ /* std::mt19937_64 rng1(0); */ /* std::string k; */ @@ -161,7 +162,7 @@ void art_bench() { /* } */ /* void art_compressions_sparse_delete() { */ -/* art::art m; */ +/* art::art m; */ /* int v = 1; */ /* std::mt19937_64 rng1(0); */ /* std::string k; */ @@ -183,7 +184,7 @@ void art_bench() { /* } */ void casual_stress_test(int n) { - art::art m; + art::art m; int v = 1; std::mt19937_64 rng1(0); std::string k; @@ -213,23 +214,20 @@ int main() { /* art_compressions_dense_delete(); */ /* casual_stress_test(16 * 1000 * 1000); */ - int int0 = 0; - int int1 = 1; - int int2 = 2; - int int3 = 4; - int int4 = 5; - int int5 = 5; - int int6 = 6; + + // + // simple example + // art::art m; - m.set("aa", &int0); - m.set("aaaa", &int1); - m.set("aaaaaaa", &int2); - m.set("aaaaaaaaaa", &int3); - m.set("aaaaaaaba", &int4); - m.set("aaaabaa", &int5); - m.set("aaaabaaaaa", &int6); + m.set("aa", 0); + m.set("aaaa", 1); + m.set("aaaaaaa", 2); + m.set("aaaaaaaaaa", 3); + m.set("aaaaaaaba", 4); + m.set("aaaabaa", 5); + m.set("aaaabaaaaa", 6); /* The above statements construct the following tree: * @@ -249,7 +247,8 @@ int main() { auto it = m.begin(); auto it_end = m.end(); for (int i = 0; it != it_end; ++i, ++it) { - std::cout << i << ": " << **it << std::endl; + std::cout << it.key() << " -> " << *it << std::endl; } + return 0; } diff --git a/test/art.cpp b/test/art.cpp index b3e2ec9..086ae6f 100644 --- a/test/art.cpp +++ b/test/art.cpp @@ -10,6 +10,7 @@ #include #include #include +#include using std::array; using std::hash; @@ -24,7 +25,7 @@ TEST_SUITE("art") { TEST_CASE("set") { - art::art trie; + art::art trie; int dummy_value_1; int dummy_value_2; @@ -78,7 +79,7 @@ TEST_SUITE("art") { values[i] = new int(); } - art::art m; + art::art m; for (int i = 0; i < n; i += 1) { m.set(keys[i].c_str(), values[i]); @@ -118,7 +119,7 @@ TEST_SUITE("art") { auto key9 = "aaaaaaaaaac"; auto int9 = 9; - art::art m; + art::art m; m.set(key0, &int0); m.set(key1, &int1); @@ -273,7 +274,7 @@ TEST_SUITE("art") { } TEST_CASE("monte carlo delete") { - art::art m; + art::art m; mt19937_64 rng1(0); for (int i = 0; i < 1000000; ++i) { auto k = to_string(rng1()); diff --git a/test/inner_node.cpp b/test/inner_node.cpp index 14725aa..af790eb 100644 --- a/test/inner_node.cpp +++ b/test/inner_node.cpp @@ -28,12 +28,12 @@ using std::string; TEST_SUITE("inner_node") { TEST_CASE("iteration") { - node_4 m; + node_4 m; - leaf_node n0(nullptr); - leaf_node n1(nullptr); - leaf_node n2(nullptr); - leaf_node n3(nullptr); + leaf_node n0(nullptr); + leaf_node n1(nullptr); + leaf_node n2(nullptr); + leaf_node n3(nullptr); m.set_child(0, &n0); m.set_child(5, &n1); @@ -103,12 +103,12 @@ TEST_SUITE("inner_node") { } TEST_CASE("reverse iteration") { - node_4 m; + node_4 m; - leaf_node n0(nullptr); - leaf_node n1(nullptr); - leaf_node n2(nullptr); - leaf_node n3(nullptr); + leaf_node n0(nullptr); + leaf_node n1(nullptr); + leaf_node n2(nullptr); + leaf_node n3(nullptr); m.set_child(0, &n0); m.set_child(5, &n1); diff --git a/test/node.cpp b/test/node.cpp index 737cb6b..bf26b48 100644 --- a/test/node.cpp +++ b/test/node.cpp @@ -28,7 +28,7 @@ using std::string; TEST_SUITE("node") { TEST_CASE("check_prefix") { - leaf_node node(nullptr); + leaf_node node(nullptr); string key = "000100001"; int key_len = key.length() + 1; // +1 for \0 string prefix = "0000"; diff --git a/test/node_16.cpp b/test/node_16.cpp index 71a1ee3..f082503 100644 --- a/test/node_16.cpp +++ b/test/node_16.cpp @@ -22,14 +22,14 @@ TEST_SUITE("node 16") { TEST_CASE("monte carlo") { /* set up */ array partial_keys; - array *, 256> children; + array *, 256> children; for (int i = 0; i < 256; i += 1) { /* populate partial_keys with all values in the partial_keys_t domain */ partial_keys[i] = i; /* populate child nodes */ - children[i] = new leaf_node(nullptr); + children[i] = new leaf_node(nullptr); } /* rng */ @@ -38,7 +38,7 @@ TEST_SUITE("node 16") { for (int experiment = 0; experiment < 1000; experiment += 1) { /* test subject */ - node_16 node; + node_16 node; /* shuffle in order to make a seemingly random insertion order */ shuffle(partial_keys.begin(), partial_keys.end(), g); @@ -68,15 +68,15 @@ TEST_SUITE("node 16") { } TEST_CASE("delete child") { - leaf_node n0(nullptr); - leaf_node n1(nullptr); - leaf_node n2(nullptr); - leaf_node n3(nullptr); - leaf_node n4(nullptr); - leaf_node n5(nullptr); - leaf_node n6(nullptr); + leaf_node n0(nullptr); + leaf_node n1(nullptr); + leaf_node n2(nullptr); + leaf_node n3(nullptr); + leaf_node n4(nullptr); + leaf_node n5(nullptr); + leaf_node n6(nullptr); - node_16 subject; + node_16 subject; subject.set_child(1, &n1); subject.set_child(2, &n2); @@ -141,7 +141,7 @@ TEST_SUITE("node 16") { } TEST_CASE("next partial key") { - node_16 n; + node_16 n; SUBCASE("completely empty node") { REQUIRE_THROWS_AS(n.next_partial_key(0), std::out_of_range); @@ -189,7 +189,7 @@ TEST_SUITE("node 16") { } TEST_CASE("previous partial key") { - node_16 n; + node_16 n; SUBCASE("completely empty node") { REQUIRE_THROWS_AS(n.prev_partial_key(127), std::out_of_range); diff --git a/test/node_256.cpp b/test/node_256.cpp index 71436e7..48d60b3 100644 --- a/test/node_256.cpp +++ b/test/node_256.cpp @@ -22,12 +22,12 @@ TEST_SUITE("node 256") { TEST_CASE("monte carlo") { /* set up */ array partial_keys; - array *, 256> children; + array *, 256> children; for (int i = 0; i < 256; i += 1) { /* populate partial_keys with all values in the char domain */ partial_keys[i] = i - 128; - children[i] = new leaf_node(nullptr); + children[i] = new leaf_node(nullptr); } /* rng */ @@ -35,7 +35,7 @@ TEST_SUITE("node 256") { for (int experiment = 0; experiment < 100; experiment += 1) { /* test subject */ - node_256 node; + node_256 node; /* shuffle in order to make a seemingly random insertion order */ shuffle(partial_keys.begin(), partial_keys.end(), g); @@ -64,15 +64,15 @@ TEST_SUITE("node 256") { } TEST_CASE("delete child") { - leaf_node n0(nullptr); - leaf_node n1(nullptr); - leaf_node n2(nullptr); - leaf_node n3(nullptr); - leaf_node n4(nullptr); - leaf_node n5(nullptr); - leaf_node n6(nullptr); + leaf_node n0(nullptr); + leaf_node n1(nullptr); + leaf_node n2(nullptr); + leaf_node n3(nullptr); + leaf_node n4(nullptr); + leaf_node n5(nullptr); + leaf_node n6(nullptr); - node_256 subject; + node_256 subject; subject.set_child(1, &n1); subject.set_child(2, &n2); @@ -137,14 +137,14 @@ TEST_SUITE("node 256") { } TEST_CASE("next partial key") { - node_256 n; + node_256 n; SUBCASE("completely empty node") { REQUIRE_THROWS_AS(n.next_partial_key(-128), std::out_of_range); } SUBCASE("child at -128") { - leaf_node n0(nullptr); + leaf_node n0(nullptr); n.set_child(-128, &n0); REQUIRE_EQ(-128, n.next_partial_key(-128)); for (int i = 1; i < 256; ++i) { @@ -153,7 +153,7 @@ TEST_SUITE("node 256") { } SUBCASE("child at 127") { - leaf_node n0(nullptr); + leaf_node n0(nullptr); n.set_child(127, &n0); for (int i = 0; i < 256; ++i) { REQUIRE_EQ(127, n.next_partial_key(i - 128)); @@ -161,10 +161,10 @@ TEST_SUITE("node 256") { } SUBCASE("dense children") { - leaf_node n0(nullptr); - leaf_node n1(nullptr); - leaf_node n2(nullptr); - leaf_node n3(nullptr); + leaf_node n0(nullptr); + leaf_node n1(nullptr); + leaf_node n2(nullptr); + leaf_node n3(nullptr); n.set_child(0, &n0); n.set_child(1, &n1); n.set_child(2, &n2); @@ -177,10 +177,10 @@ TEST_SUITE("node 256") { } SUBCASE("sparse children") { - leaf_node n0(nullptr); - leaf_node n1(nullptr); - leaf_node n2(nullptr); - leaf_node n3(nullptr); + leaf_node n0(nullptr); + leaf_node n1(nullptr); + leaf_node n2(nullptr); + leaf_node n3(nullptr); n.set_child(0, &n0); n.set_child(5, &n1); n.set_child(10, &n2); @@ -194,7 +194,7 @@ TEST_SUITE("node 256") { } TEST_CASE("previous partial key") { - node_256 n; + node_256 n; SUBCASE("completely empty node") { for (int i = 0; i < 256; ++i) { @@ -203,7 +203,7 @@ TEST_SUITE("node 256") { } SUBCASE("child at -128") { - leaf_node n0(nullptr); + leaf_node n0(nullptr); n.set_child(-128, &n0); for (int i = 0; i < 256; ++i) { REQUIRE_EQ(-128, n.prev_partial_key(i - 128)); @@ -211,7 +211,7 @@ TEST_SUITE("node 256") { } SUBCASE("child at 127") { - leaf_node n0(nullptr); + leaf_node n0(nullptr); n.set_child(127, &n0); REQUIRE_EQ(127, n.prev_partial_key(127)); for (int i = 0; i < 255; ++i) { @@ -220,10 +220,10 @@ TEST_SUITE("node 256") { } SUBCASE("dense children") { - leaf_node n0(nullptr); - leaf_node n1(nullptr); - leaf_node n2(nullptr); - leaf_node n3(nullptr); + leaf_node n0(nullptr); + leaf_node n1(nullptr); + leaf_node n2(nullptr); + leaf_node n3(nullptr); n.set_child(1, &n0); n.set_child(2, &n1); n.set_child(3, &n2); @@ -237,10 +237,10 @@ TEST_SUITE("node 256") { } SUBCASE("sparse children") { - leaf_node n0(nullptr); - leaf_node n1(nullptr); - leaf_node n2(nullptr); - leaf_node n3(nullptr); + leaf_node n0(nullptr); + leaf_node n1(nullptr); + leaf_node n2(nullptr); + leaf_node n3(nullptr); n.set_child(1, &n0); n.set_child(5, &n1); n.set_child(10, &n2); diff --git a/test/node_4.cpp b/test/node_4.cpp index 511928c..39e6346 100644 --- a/test/node_4.cpp +++ b/test/node_4.cpp @@ -23,14 +23,14 @@ TEST_SUITE("node 4") { TEST_CASE("monte carlo insert") { /* set up */ array partial_keys; - array *, 256> children; + array *, 256> children; for (int i = 0; i < 256; i += 1) { /* populate partial_keys with all values in the partial_keys_t domain */ partial_keys[i] = i; /* populate child nodes */ - children[i] = new leaf_node(nullptr); + children[i] = new leaf_node(nullptr); } /* rng */ @@ -39,7 +39,7 @@ TEST_SUITE("node 4") { for (int experiment = 0; experiment < 10000; experiment += 1) { /* test subject */ - node_4 node; + node_4 node; /* shuffle in order to make a seemingly random insertion order */ shuffle(partial_keys.begin(), partial_keys.end(), g); @@ -69,15 +69,15 @@ TEST_SUITE("node 4") { } TEST_CASE("delete child") { - leaf_node n0(nullptr); - leaf_node n1(nullptr); - leaf_node n2(nullptr); - leaf_node n3(nullptr); - leaf_node n4(nullptr); - leaf_node n5(nullptr); - leaf_node n6(nullptr); - - node_4 subject; + leaf_node n0(nullptr); + leaf_node n1(nullptr); + leaf_node n2(nullptr); + leaf_node n3(nullptr); + leaf_node n4(nullptr); + leaf_node n5(nullptr); + leaf_node n6(nullptr); + + node_4 subject; subject.set_child(1, &n1); subject.set_child(2, &n2); diff --git a/test/node_48.cpp b/test/node_48.cpp index 4e04487..7a41d97 100644 --- a/test/node_48.cpp +++ b/test/node_48.cpp @@ -21,14 +21,14 @@ TEST_SUITE("node 48") { TEST_CASE("monte carlo") { /* set up */ array partial_keys; - array *, 256> children; + array *, 256> children; for (int i = 0; i < 256; i += 1) { /* populate partial_keys with all values in the partial_keys_t domain */ partial_keys[i] = i - 128; /* populate child nodes */ - children[i] = new leaf_node(nullptr); + children[i] = new leaf_node(nullptr); } /* rng */ @@ -36,7 +36,7 @@ TEST_SUITE("node 48") { for (int experiment = 0; experiment < 10000; experiment += 1) { /* test subject */ - node_48 node; + node_48 node; /* shuffle in order to make a seemingly random insertion order */ shuffle(partial_keys.begin(), partial_keys.end(), g); @@ -67,15 +67,15 @@ TEST_SUITE("node 48") { } TEST_CASE("delete child") { - leaf_node n0(nullptr); - leaf_node n1(nullptr); - leaf_node n2(nullptr); - leaf_node n3(nullptr); - leaf_node n4(nullptr); - leaf_node n5(nullptr); - leaf_node n6(nullptr); + leaf_node n0(nullptr); + leaf_node n1(nullptr); + leaf_node n2(nullptr); + leaf_node n3(nullptr); + leaf_node n4(nullptr); + leaf_node n5(nullptr); + leaf_node n6(nullptr); - node_48 subject; + node_48 subject; subject.set_child(1, &n1); subject.set_child(2, &n2); @@ -140,7 +140,7 @@ TEST_SUITE("node 48") { } TEST_CASE("next partial key") { - node_48 n; + node_48 n; SUBCASE("completely empty node") { for (int i = 0; i < 256; ++i) { @@ -189,7 +189,7 @@ TEST_SUITE("node 48") { } TEST_CASE("previous partial key") { - node_48 n; + node_48 n; SUBCASE("completely empty node") { REQUIRE_THROWS_AS(n.prev_partial_key(127), std::out_of_range); diff --git a/test/tree_it.cpp b/test/tree_it.cpp index 852bcdb..7929e92 100644 --- a/test/tree_it.cpp +++ b/test/tree_it.cpp @@ -10,6 +10,7 @@ #include #include #include +#include using std::array; using std::hash; @@ -31,7 +32,7 @@ TEST_SUITE("tree_it") { int int5 = 5; int int6 = 6; - art::art m; + art::art m; m.set("aa", &int0); m.set("aaaa", &int1); @@ -58,40 +59,63 @@ TEST_SUITE("tree_it") { auto it = m.begin(); auto it_end = m.end(); + std::string key; + key.reserve(20); // 0 REQUIRE(it != it_end); REQUIRE_EQ(&int0, *it); + it.key(key.begin()); + REQUIRE(std::equal(key.begin(), key.begin() + 3, "aa")); + REQUIRE_EQ("aa", it.key()); ++it; // 1 REQUIRE(it != it_end); REQUIRE_EQ(&int1, *it); + it.key(key.begin()); + REQUIRE(std::equal(key.begin(), key.begin() + 5, "aaaa")); + REQUIRE_EQ("aaaa", it.key()); ++it; // 2 REQUIRE(it != it_end); REQUIRE_EQ(&int2, *it); + it.key(key.begin()); + REQUIRE(std::equal(key.begin(), key.begin() + 8, "aaaaaaa")); + REQUIRE_EQ("aaaaaaa", it.key()); ++it; // 3 REQUIRE(it != it_end); REQUIRE_EQ(&int3, *it); + it.key(key.begin()); + REQUIRE(std::equal(key.begin(), key.begin() + 11, "aaaaaaaaaa")); + REQUIRE_EQ("aaaaaaaaaa", it.key()); ++it; // 4 REQUIRE(it != it_end); REQUIRE_EQ(&int4, *it); + it.key(key.begin()); + REQUIRE(std::equal(key.begin(), key.begin() + 10, "aaaaaaaba")); + REQUIRE_EQ("aaaaaaaba", it.key()); ++it; // 5 REQUIRE(it != it_end); REQUIRE_EQ(&int5, *it); + it.key(key.begin()); + REQUIRE(std::equal(key.begin(), key.begin() + 8, "aaaabaa")); + REQUIRE_EQ("aaaabaa", it.key()); ++it; // 6 REQUIRE(it != it_end); REQUIRE_EQ(&int6, *it); + it.key(key.begin()); + REQUIRE(std::equal(key.begin(), key.begin() + 11, "aaaabaaaaa")); + REQUIRE_EQ("aaaabaaaaa", it.key()); ++it; // 7 (overflow) @@ -102,7 +126,7 @@ TEST_SUITE("tree_it") { int n = 0x10000; char key[5]; int value; - art::art m; + art::art m; for (int i = 0; i < n; ++i) { std::snprintf(key, 5, "%04X", i); m.set(key, &value); @@ -128,7 +152,7 @@ TEST_SUITE("tree_it") { int int5 = 5; int int6 = 6; - art::art m; + art::art m; m.set("aa", &int0); m.set("aaaa", &int1); @@ -184,7 +208,7 @@ TEST_SUITE("tree_it") { int n = 1 << (n_bytes * 4); char keys[n][n_bytes + 1]; int value; - art::art m; + art::art m; for (int i = 0; i < n; ++i) { std::snprintf(keys[i], n_bytes + 1, "%04X", i); // note: change format if you change n_bytes m.set(keys[i], &value);