/* * RadixTree.h * * This source file is part of the FoundationDB open source project * * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FLOW__RADIXTREE_H #define FLOW__RADIXTREE_H #pragma once #include #include #include #include #include #include #include #include #include "fdbserver/IKeyValueContainer.h" #include "flow/Arena.h" // forward declaration const int LEAF_BYTE = -1; const int INLINE_KEY_SIZE = sizeof(StringRef); StringRef radix_substr(const StringRef& key, int begin, int num) { int size = key.size(); if (begin > size) { throw std::out_of_range("out of range in radix_substr"); } if ((begin + num) > size) { num = size - begin; } return key.substr(begin, num); } StringRef radix_join(const StringRef& key1, const StringRef& key2, Arena& arena) { int rsize = key1.size() + key2.size(); uint8_t* s = new (arena) uint8_t[rsize]; memcpy(s, key1.begin(), key1.size()); if (key2.size() > 0) { memcpy(s + key1.size(), key2.begin(), key2.size()); } return StringRef(s, rsize); } StringRef radix_constructStr(const StringRef& key, int begin, int num, Arena& arena) { int size = key.size(); if (begin > size) { throw std::out_of_range("out of range in radix_substr"); } if ((begin + num) > size) { num = size - begin; } return StringRef(arena, key.substr(begin, num)); } class radix_tree { public: typedef std::size_t size_type; private: // union used inside both base node and leaf node union inlineUnion { inlineUnion() : data() {} // constructor uint8_t inlineData[INLINE_KEY_SIZE]; StringRef data; }; struct node { // constructor for all kinds of node (root/internal/leaf) node() : m_is_leaf(0), m_is_inline(0), m_inline_length(0), m_depth(0), key(), arena(), m_parent(nullptr) {} node(const node&) = delete; // delete node& operator=(const node& other) { m_is_leaf = other.m_is_leaf; m_is_inline = other.m_is_inline; m_inline_length = other.m_inline_length; m_depth = other.m_depth; memcpy(key.inlineData, other.key.inlineData, INLINE_KEY_SIZE); arena = other.arena; m_parent = other.m_parent; return *this; } void setKey(const StringRef& content, int start, int num) { bool isInline = num <= INLINE_KEY_SIZE; if (isInline) { memcpy(key.inlineData, content.begin() + start, num); m_inline_length = num; if (!m_is_inline) arena = Arena(); } else { Arena new_arena(num); key.data = radix_constructStr(content, start, num, new_arena); arena = new_arena; } m_is_inline = isInline; } StringRef getKey() const { if (m_is_inline) { return StringRef(&key.inlineData[0], m_inline_length); } else { return key.data; } } inline int getKeySize() const { return m_is_inline ? m_inline_length : key.data.size(); } inline int16_t getFirstByte() const { if (m_is_inline) { return m_inline_length == 0 ? LEAF_BYTE : key.inlineData[0]; } else { return key.data.size() == 0 ? LEAF_BYTE : key.data[0]; } } inline size_type getArenaSize() const { return m_is_inline ? 0 : arena.getSize(); } uint32_t m_is_leaf : 1; uint32_t m_is_fixed : 1; // if true, then we have fixed number of children (3) uint32_t m_is_inline : 1; uint32_t m_inline_length : 4; // m_depth can be seen as common prefix length with your ancestors uint32_t m_depth : 25; // key is the prefix, a substring that shared by your children inlineUnion key; // arena assign memory for key Arena arena; node* m_parent; }; struct leafNode : FastAllocated { leafNode(const StringRef& content) : base(), is_inline(0), inline_length(0), arena() { base.m_is_leaf = 1; setValue(content); } ~leafNode() = default; void setValue(const StringRef& content) { bool isInline = content.size() <= INLINE_KEY_SIZE; if (isInline) { memcpy(value.inlineData, content.begin(), content.size()); inline_length = content.size(); if (!is_inline) arena = Arena(); } else { Arena new_arena(content.size()); value.data = StringRef(new_arena, content); arena = new_arena; } is_inline = isInline; } StringRef getValue() { if (is_inline) { return StringRef(&value.inlineData[0], inline_length); } else { return value.data; } } inline size_type getLeafArenaSize() { return is_inline ? 0 : arena.getSize(); } node base; // 32 bytes uint32_t is_inline : 1; uint32_t inline_length : 31; inlineUnion value; // using the same data structure to store value Arena arena; }; struct internalNode : FastAllocated { internalNode() : base(), m_children(std::vector>()) { m_children.reserve(4); base.m_is_fixed = 0; } ~internalNode() { for (auto it = 0; it < m_children.size(); ++it) { node* current = m_children[it].second; if (current->m_is_leaf) { delete (leafNode*)current; } else { current->m_is_fixed ? delete (internalNode4*)current : delete (internalNode*)current; } } m_children.clear(); } node base; // ordered map by char, m_children.begin() return the smallest value std::vector> m_children; }; struct internalNode4 : FastAllocated { internalNode4() : base(), num_children(0) { base.m_is_fixed = 1; memset(keys, 0, sizeof(keys)); memset(m_children, 0, sizeof(m_children)); } ~internalNode4() { num_children = 0; } node base; int16_t num_children; int16_t keys[3]; node* m_children[3]; }; public: class iterator : public std::iterator> { public: node* m_pointee; iterator() : m_pointee(nullptr) {} iterator(const iterator& r) : m_pointee(r.m_pointee) {} iterator(node* p) : m_pointee(p) {} iterator& operator=(const iterator& r) { m_pointee = r.m_pointee; return *this; } ~iterator() = default; const iterator& operator++(); const iterator& operator--(); bool operator!=(const iterator& lhs) const; bool operator==(const iterator& lhs) const; StringRef getKey(uint8_t* content) const; StringRef getValue() const { ASSERT(m_pointee->m_is_leaf); return ((leafNode*)m_pointee)->getValue(); } private: node* increment(node* target) const; node* decrement(node* target) const; }; explicit radix_tree() : m_size(0), m_node(0), inline_keys(0), total_bytes(0), m_root(nullptr) {} ~radix_tree() {} radix_tree(const radix_tree& other) = delete; // delete radix_tree& operator=(const radix_tree other) = delete; // delete inline std::tuple size() const { return std::make_tuple(m_size, m_node, inline_keys); } // Return the amount of memory used by an entry in the RadixTree static int getElementBytes(node* node) { int result = 0; if (node->m_is_leaf) { result = sizeof(leafNode) + ((leafNode*)node)->getLeafArenaSize(); } else if (node->m_is_fixed) { result = sizeof(internalNode4); } else { ASSERT(!node->m_is_fixed); result = sizeof(internalNode); } return result; } // dummy method interface(to keep every interface same as IndexedSet ) static int getElementBytes() { ASSERT(false); return 0; } bool empty() const { return m_size == 0; } void clear() { if (m_root != nullptr) { delete (internalNode*)m_root; m_root = nullptr; } m_size = 0; m_node = 0; inline_keys = 0; total_bytes = 0; } // iterators iterator find(const StringRef& key); iterator begin(); iterator end() const; iterator previous(iterator i); // modifications std::pair insert(const StringRef& key, const StringRef& val, bool replaceExisting = true); int insert(const std::vector>& pairs, bool replaceExisting = true) { // dummy method interface(to keep every interface same as IndexedSet ) ASSERT(false); return 0; } void erase(iterator it); void erase(iterator begin, iterator end); // lookups iterator lower_bound(const StringRef& key); iterator upper_bound(const StringRef& key); // access uint64_t sumTo(iterator to) const; private: size_type m_size; // number of nodes that has been created size_type m_node; // number of nodes with key.size() <= 12 size_type inline_keys; uint64_t total_bytes; node* m_root; // modification void add_child(node* parent, node* child); void add_child_vector(node* parent, node* child); void add_child4(node* parent, node* child); void delete_child(node* parent, node* child); void delete_child_vector(node* parent, node* child); void delete_child4(node* parent, node* child); // access static int find_child(node* parent, int16_t ch); // return index static int child_size(node* parent); // how many children does parent node have static node* get_child(node* parent, int index); // return node pointer // direction 0 = left, 1 = right template static node* descend(node* i) { while (!i->m_is_leaf) { ASSERT(child_size(i) != 0); if (reverse) { i = get_child(i, child_size(i) - 1); } else { i = get_child(i, 0); } } return i; } node* find_node(const StringRef& key, node* node, int depth); node* append(node* parent, const StringRef& key, const StringRef& val); node* prepend(node* node, const StringRef& key, const StringRef& val); bool erase(node* child); iterator lower_bound(const StringRef& key, node* node); iterator upper_bound(const StringRef& key, node* node); }; /////////////////////// iterator ////////////////////////// void radix_tree::add_child(node* parent, node* child) { if (parent->m_is_fixed) { add_child4(parent, child); } else { add_child_vector(parent, child); } } void radix_tree::add_child4(node* parent, node* child) { int16_t ch = child->getFirstByte(); internalNode4* parent_ref = (internalNode4*)parent; int i = 0; for (; i < parent_ref->num_children; ++i) { if (parent_ref->keys[i] >= ch) break; } if (!parent_ref->num_children) { // empty parent_ref->num_children++; parent_ref->keys[0] = ch; parent_ref->m_children[0] = child; // DEBUG total_bytes += getElementBytes(child) + child->getArenaSize(); } else if (i >= 0 && i < parent_ref->num_children && parent_ref->keys[i] == ch) { // replace node* original = parent_ref->m_children[i]; total_bytes -= (getElementBytes(original) + original->getArenaSize()); parent_ref->m_children[i] = child; total_bytes += getElementBytes(child) + child->getArenaSize(); } else if (parent_ref->num_children < 3) { // Shift to make room memmove(parent_ref->keys + i + 1, parent_ref->keys + i, (parent_ref->num_children - i) * sizeof(int16_t)); memmove( parent_ref->m_children + i + 1, parent_ref->m_children + i, (parent_ref->num_children - i) * sizeof(void*)); // Insert element parent_ref->keys[i] = ch; parent_ref->m_children[i] = child; parent_ref->num_children++; // DEBUG total_bytes += getElementBytes(child) + child->getArenaSize(); } else { ASSERT(parent_ref->num_children >= 3); internalNode* new_node = new radix_tree::internalNode(); new_node->base = parent_ref->base; // equal operator for (int index = 0; index < parent_ref->num_children; index++) { new_node->m_children.emplace_back(parent_ref->keys[index], parent_ref->m_children[index]); parent_ref->m_children[index]->m_parent = (node*)new_node; } // Insert new element new_node->m_children.insert(new_node->m_children.begin() + i, std::make_pair(ch, child)); child->m_parent = (node*)new_node; // update parent info add_child(new_node->base.m_parent, (node*)new_node); // DEBUG total_bytes += new_node->m_children.size() * sizeof(std::pair) + getElementBytes(child) + child->getArenaSize(); delete parent_ref; } } void radix_tree::add_child_vector(node* parent, node* child) { int16_t ch = child->getFirstByte(); internalNode* parent_ref = (internalNode*)parent; int i = 0; for (; i < parent_ref->m_children.size(); ++i) { if (parent_ref->m_children[i].first >= ch) break; } if (parent_ref->m_children.empty() || i == parent_ref->m_children.size() || parent_ref->m_children[i].first > ch) { parent_ref->m_children.insert(parent_ref->m_children.begin() + i, std::make_pair(ch, child)); // DEBUG total_bytes += getElementBytes(child) + child->getArenaSize() + sizeof(std::pair); } else { ASSERT(parent_ref->m_children[i].first == ch); // replace with the new child node* original = parent_ref->m_children[i].second; total_bytes -= (getElementBytes(original) + original->getArenaSize()); parent_ref->m_children[i] = std::make_pair(ch, child); // replace with the new child total_bytes += getElementBytes(child) + child->getArenaSize(); } } void radix_tree::delete_child(radix_tree::node* parent, radix_tree::node* child) { if (parent->m_is_fixed) { delete_child4(parent, child); } else { delete_child_vector(parent, child); } } void radix_tree::delete_child4(radix_tree::node* parent, radix_tree::node* child) { int16_t ch = child->getFirstByte(); internalNode4* parent_ref = (internalNode4*)parent; int i = 0; for (; i < parent_ref->num_children; i++) { if (parent_ref->keys[i] == ch) break; } ASSERT(i != parent_ref->num_children); memmove(parent_ref->keys + i, parent_ref->keys + i + 1, (parent_ref->num_children - 1 - i) * sizeof(int16_t)); memmove( parent_ref->m_children + i, parent_ref->m_children + i + 1, (parent_ref->num_children - 1 - i) * sizeof(void*)); parent_ref->num_children--; total_bytes -= (getElementBytes(child) + child->getArenaSize()); } void radix_tree::delete_child_vector(radix_tree::node* parent, radix_tree::node* child) { int16_t ch = child->getFirstByte(); internalNode* parent_ref = (internalNode*)parent; int i = 0; for (; i < parent_ref->m_children.size(); i++) { if (parent_ref->m_children[i].first == ch) break; } ASSERT(i != parent_ref->m_children.size()); parent_ref->m_children.erase(parent_ref->m_children.begin() + i); total_bytes -= (getElementBytes(child) + child->getArenaSize() + sizeof(std::pair)); if (parent_ref->m_children.size() && parent_ref->m_children.size() <= parent_ref->m_children.capacity() / 4) parent_ref->m_children.shrink_to_fit(); } int radix_tree::find_child(radix_tree::node* parent, int16_t ch) { int i = 0; if (parent->m_is_fixed) { internalNode4* parent_ref = (internalNode4*)parent; for (; i < parent_ref->num_children; ++i) { if (parent_ref->keys[i] == ch) return i; } } else { internalNode* parent_ref = (internalNode*)parent; for (; i != parent_ref->m_children.size(); ++i) { if (parent_ref->m_children[i].first == ch) return i; } } return i; } int radix_tree::child_size(radix_tree::node* parent) { if (parent->m_is_fixed) { return ((internalNode4*)parent)->num_children; } else { return ((internalNode*)parent)->m_children.size(); } } radix_tree::node* radix_tree::get_child(node* parent, int index) { if (parent->m_is_fixed) { ASSERT(index < ((internalNode4*)parent)->num_children); return ((internalNode4*)parent)->m_children[index]; } else { return ((internalNode*)parent)->m_children[index].second; } } radix_tree::node* radix_tree::iterator::increment(node* target) const { radix_tree::node* parent = target->m_parent; if (parent == nullptr) return nullptr; int index = find_child(parent, target->getFirstByte()); ASSERT(index != child_size(parent)); ++index; if (index == child_size(parent)) return increment(target->m_parent); else return descend<0>(get_child(parent, index)); } radix_tree::node* radix_tree::iterator::decrement(radix_tree::node* target) const { radix_tree::node* parent = target->m_parent; if (parent == nullptr) return nullptr; int index = find_child(parent, target->getFirstByte()); ASSERT(index != child_size(parent)); if (index == 0) return decrement(target->m_parent); else { --index; return descend<1>(get_child(parent, index)); } } bool radix_tree::iterator::operator!=(const radix_tree::iterator& lhs) const { return m_pointee != lhs.m_pointee; } bool radix_tree::iterator::operator==(const radix_tree::iterator& lhs) const { return m_pointee == lhs.m_pointee; } const radix_tree::iterator& radix_tree::iterator::operator++() { if (m_pointee != nullptr) // it is undefined behaviour to dereference iterator that is out of bounds... m_pointee = increment(m_pointee); return *this; } const radix_tree::iterator& radix_tree::iterator::operator--() { if (m_pointee != nullptr && m_pointee->m_is_leaf) { m_pointee = decrement(m_pointee); } return *this; } /* * reconstruct the key */ StringRef radix_tree::iterator::getKey(uint8_t* content) const { if (m_pointee == nullptr) return StringRef(); ASSERT(m_pointee->m_is_leaf); // memset(content, 0, len); auto node = m_pointee; uint32_t pos = m_pointee->m_depth; while (true) { if (node->getKeySize() > 0) { memcpy(content + pos, node->getKey().begin(), node->getKeySize()); } node = node->m_parent; if (node == nullptr || pos <= 0) break; pos -= node->getKeySize(); } return StringRef(content, (m_pointee->m_depth + m_pointee->getKeySize())); } radix_tree::iterator radix_tree::end() const { return iterator(nullptr); } radix_tree::iterator radix_tree::begin() { if (m_root == nullptr || m_size == 0) return iterator(nullptr); else { return descend<0>(m_root); } } /////////////////////// lookup ////////////////////////// radix_tree::iterator radix_tree::find(const StringRef& key) { if (m_root == nullptr) return iterator(nullptr); auto node = find_node(key, m_root, 0); StringRef key_sub = radix_substr(key, node->m_depth, (key.size() - node->m_depth)); if (node->m_is_leaf && key_sub == node->getKey()) return node; else return nullptr; } /* * corner case : insert "apache, append", then search for "appends". find_node() will return leaf node with m_key == * "pend"; if search for "ap", find_node() will return internal node with m_key = ap */ radix_tree::node* radix_tree::find_node(const StringRef& key, node* node, int depth) { if (node->m_is_leaf) return node; int size = child_size(node); // printf("try to find key %s on node %s [%d]\n", printable(key).c_str(), printable(node->getKey()).c_str(), size); for (int it = 0; it < size; ++it) { auto current = get_child(node, it); // for leaf node with empty key, exact match if (depth == key.size() && current->getKeySize() == 0) { ASSERT(current->m_is_leaf); // find the exact match return current; } // they have at least one byte in common if (depth < key.size() && key[depth] == current->getFirstByte()) { int len_node = current->getKeySize(); StringRef key_sub = radix_substr(key, depth, len_node); if (key_sub == current->getKey()) { if (current->m_is_leaf) return current; else return find_node(key, current, depth + len_node); } else { // return the current match (which is the smallest match) // radix tree won't have siblings that share the same prefix return current; } } } return node; } /* * Returns the smallest node x such that *x>=key, or end() */ radix_tree::iterator radix_tree::lower_bound(const StringRef& key) { if (m_root == nullptr || m_size == 0) return iterator(nullptr); return lower_bound(key, m_root); } radix_tree::iterator radix_tree::lower_bound(const StringRef& key, node* node) { iterator result(nullptr); int size = child_size(node); for (int it = 0; it < size; ++it) { auto current = get_child(node, it); // short cut as find_node if (key.size() == current->m_depth && current->getKeySize() == 0) { return iterator(current); } StringRef key_sub = radix_substr(key, current->m_depth, current->getKeySize()); StringRef node_data = current->getKey(); if (key_sub == node_data) { if (current->m_is_leaf && ((key.size() - current->m_depth) == current->getKeySize())) return iterator(current); // exact match else if (!current->m_is_leaf) result = lower_bound(key, current); } else if (node_data > key_sub) { return descend<0>(current); } if (result != end()) return result; } return result; } /* * Returns the smallest x such that *x>key, or end() */ radix_tree::iterator radix_tree::upper_bound(const StringRef& key) { if (m_root == nullptr || m_size == 0) return iterator(nullptr); return upper_bound(key, m_root); } radix_tree::iterator radix_tree::upper_bound(const StringRef& key, node* node) { if (node == nullptr || node->m_is_leaf) return iterator(node); iterator result(nullptr); int size = child_size(node); for (int it = 0; it < size; ++it) { auto current = get_child(node, it); StringRef key_sub = radix_substr(key, current->m_depth, current->getKeySize()); StringRef node_data = current->getKey(); if (!current->m_is_leaf && node_data == key_sub) { result = upper_bound(key, current); } else if (node_data > key_sub) { return descend<0>(current); } if (result != end()) return result; } return result; } // Return the sum of getT(x) for begin()<=x(m_root); } else if (i == begin()) { return iterator(nullptr); } else { --i; return i; } } /////////////////////// modification ////////////////////////// /* * @param parent : direct parent of this newly inserted node * @param val : using val to create a newly inserted node */ radix_tree::node* radix_tree::append(node* parent, const StringRef& key, const StringRef& val) { int depth = parent->m_depth + parent->getKeySize(); int len = key.size() - depth; radix_tree::node* node_c = (node*)new radix_tree::leafNode(val); node_c->m_depth = depth; node_c->m_parent = parent; if (len == 0) { // node_c->key is empty (len = 0); inline_keys++; } else { node_c->setKey(key, depth, len); // DEBUG if (len <= INLINE_KEY_SIZE) inline_keys++; } // printf("node_c key is %s value is %s %p\n", printable(node_c->getKey()).c_str(), // printable(((leafNode*)node_c)->getValue()).c_str(), node_c); add_child(parent, node_c); m_node++; return node_c; } /* * step one : find common substring of node->m_key and val(findnode() method has already guaranteed that they have * something in common) step two : split the existing node into two based on the common substring step three : append * newly inserted node to node_a * * @param node : split node * @param val : using val to create a newly inserted node */ radix_tree::node* radix_tree::prepend(node* split, const StringRef& key, const StringRef& val) { int len1 = split->getKeySize(); int len2 = key.size() - split->m_depth; int count = 0; // deep copy original data using a temp_arena(becomes invalid once out) Arena temp_arena(split->getKeySize()); StringRef original_data(temp_arena, split->getKey()); for (; count < len1 && count < len2; count++) { if (!(original_data[count] == key[count + split->m_depth])) break; } ASSERT(count != 0); // create a new internal node node* node_a = (node*)new radix_tree::internalNode4(); m_node++; node_a->m_parent = split->m_parent; node_a->setKey(original_data, 0, count); node_a->m_depth = split->m_depth; add_child(node_a->m_parent, node_a); // replace original node* with node_a* // DEBUG if (count <= INLINE_KEY_SIZE) inline_keys++; if (split->getKeySize() > INLINE_KEY_SIZE && (len1 - count) <= INLINE_KEY_SIZE) inline_keys++; // modify original internal node split->m_depth += count; split->m_parent = node_a; split->setKey(original_data, count, len1 - count); add_child(node_a, split); return append(node_a, key, val); } std::pair radix_tree::insert(const StringRef& key, const StringRef& val, bool replaceExisting) { if (m_root == nullptr) { m_root = (node*)new radix_tree::internalNode(); total_bytes += getElementBytes(m_root); } auto node = find_node(key, m_root, 0); // short cut for root node if (node == m_root) { m_size++; return std::pair(append(m_root, key, val), true); } StringRef key_sub = radix_substr(key, node->m_depth, node->getKeySize()); if (key_sub == node->getKey()) { if (node->m_is_leaf) { if (node->m_depth + node->getKeySize() == key.size()) { // case one : exact match, replace with new value bool inserted = false; if (replaceExisting) { size_type original_value = ((leafNode*)node)->getLeafArenaSize(); ((leafNode*)node)->setValue(val); total_bytes += ((leafNode*)node)->getLeafArenaSize() - original_value; inserted = true; } return std::pair(node, inserted); } else { // case two : prepend (e.g leaf is "a", inserted key is "ab"); m_size++; return std::pair(prepend(node, key, val), true); } } else { m_size++; return std::pair(append(node, key, val), true); } } else { m_size++; return std::pair(prepend(node, key, val), true); } } void radix_tree::erase(iterator it) { erase(it.m_pointee); } bool radix_tree::erase(radix_tree::node* child) { if (m_root == nullptr) return false; ASSERT(child != nullptr); if (!child->m_is_leaf) return false; radix_tree::node* parent; parent = child->m_parent; delete_child(parent, child); // DEBUG if (child->getKeySize() <= INLINE_KEY_SIZE) inline_keys--; delete (leafNode*)child; m_size--; m_node--; // can't do the merge if parent is root node if (parent == m_root) return true; if (child_size(parent) > 1) return true; ASSERT(child_size(parent) == 1); // parent has only one child left, merge parent with the sibling node* brother = get_child(parent, 0); // DEBUG if (brother->getKeySize() <= INLINE_KEY_SIZE) inline_keys--; delete_child(parent, brother); Arena temp_arena; StringRef new_data = radix_join(parent->getKey(), brother->getKey(), temp_arena); brother->setKey(new_data, 0, new_data.size()); brother->m_depth = parent->m_depth; brother->m_parent = parent->m_parent; // delete parent and replace with brother add_child(parent->m_parent, brother); // DEBUG if (brother->getKeySize() <= INLINE_KEY_SIZE) inline_keys++; if (parent->getKeySize() <= INLINE_KEY_SIZE) inline_keys--; parent->m_is_fixed ? delete (internalNode4*)parent : delete (internalNode*)parent; m_node--; return true; } // Erase the items in the indicated range. void radix_tree::erase(radix_tree::iterator begin, radix_tree::iterator end) { std::vector node_set; for (auto it = begin; it != end; ++it) { node_set.push_back(it.m_pointee); } for (int i = 0; i < node_set.size(); ++i) { erase(node_set[i]); } } #endif