Files
gps/tools/local/circt/include/llvm/ADT/RadixTree.h
T
2026-07-12 11:01:51 +03:00

357 lines
12 KiB
C++

//===-- llvm/ADT/RadixTree.h - Radix Tree implementation --------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//===----------------------------------------------------------------------===//
//
// This file implements a Radix Tree.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_ADT_RADIXTREE_H
#define LLVM_ADT_RADIXTREE_H
#include "llvm/ADT/ADL.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/iterator.h"
#include "llvm/ADT/iterator_range.h"
#include <cassert>
#include <cstddef>
#include <iterator>
#include <list>
#include <optional>
#include <utility>
#include <vector>
namespace llvm {
/// \brief A Radix Tree implementation.
///
/// A Radix Tree (also known as a compact prefix tree or radix trie) is a
/// data structure that stores a dynamic set or associative array where keys
/// are strings and values are associated with these keys. Unlike a regular
/// trie, the edges of a radix tree can be labeled with sequences of characters
/// as well as single characters. This makes radix trees more efficient for
/// storing sparse data sets, where many nodes in a regular trie would have
/// only one child.
///
/// This implementation supports arbitrary key types that can be iterated over
/// (e.g., `std::string`, `std::vector<char>`, `ArrayRef<char>`). The key type
/// must provide `begin()` and `end()` for iteration.
///
/// The tree stores `std::pair<const KeyType, T>` as its value type.
///
/// Example usage:
/// \code
/// llvm::RadixTree<StringRef, int> Tree;
/// Tree.emplace("apple", 1);
/// Tree.emplace("grapefruit", 2);
/// Tree.emplace("grape", 3);
///
/// // Find prefixes
/// for (const auto &[Key, Value] : Tree.find_prefixes("grapefruit juice")) {
/// // pair will be {"grape", 3}
/// // pair will be {"grapefruit", 2}
/// llvm::outs() << Key << ": " << Value << "\n";
/// }
///
/// // Iterate over all elements
/// for (const auto &[Key, Value] : Tree)
/// llvm::outs() << Key << ": " << Value << "\n";
/// \endcode
///
/// \note
/// The `RadixTree` takes ownership of the `KeyType` and `T` objects
/// inserted into it. When an element is removed or the tree is destroyed,
/// these objects will be destructed.
/// However, if `KeyType` is a reference-like type, e.g., StringRef or range,
/// the user must guarantee that the referenced data has a lifetime longer than
/// the tree.
template <typename KeyType, typename T> class RadixTree {
public:
using key_type = KeyType;
using mapped_type = T;
using value_type = std::pair<const KeyType, mapped_type>;
private:
using KeyConstIteratorType =
decltype(adl_begin(std::declval<const key_type &>()));
using KeyConstIteratorRangeType = iterator_range<KeyConstIteratorType>;
using KeyValueType =
remove_cvref_t<decltype(*adl_begin(std::declval<key_type &>()))>;
using ContainerType = std::list<value_type>;
/// Represents an internal node in the Radix Tree.
struct Node {
KeyConstIteratorRangeType Key{KeyConstIteratorType{},
KeyConstIteratorType{}};
std::vector<Node> Children;
/// An iterator to the value associated with this node.
///
/// If this node does not have a value (i.e., it's an internal node that
/// only serves as a path to other values), this iterator will be equal
/// to default constructed `ContainerType::iterator()`.
std::optional<typename ContainerType::iterator> Value;
/// The first character of the Key. Used for fast child lookup.
KeyValueType KeyFront;
Node() = default;
Node(const KeyConstIteratorRangeType &Key)
: Key(Key), KeyFront(*Key.begin()) {
assert(!Key.empty());
}
Node(Node &&) = default;
Node &operator=(Node &&) = default;
Node(const Node &) = delete;
Node &operator=(const Node &) = delete;
const Node *findChild(const KeyConstIteratorRangeType &Key) const {
if (Key.empty())
return nullptr;
for (const Node &Child : Children) {
assert(!Child.Key.empty()); // Only root can be empty.
if (Child.KeyFront == *Key.begin())
return &Child;
}
return nullptr;
}
Node *findChild(const KeyConstIteratorRangeType &Query) {
const Node *This = this;
return const_cast<Node *>(This->findChild(Query));
}
size_t countNodes() const {
size_t R = 1;
for (const Node &C : Children)
R += C.countNodes();
return R;
}
///
/// Splits the current node into two.
///
/// This function is used when a new key needs to be inserted that shares
/// a common prefix with the current node's key, but then diverges.
/// The current `Key` is truncated to the common prefix, and a new child
/// node is created for the remainder of the original node's `Key`.
///
/// \param SplitPoint An iterator pointing to the character in the current
/// `Key` where the split should occur.
void split(KeyConstIteratorType SplitPoint) {
Node Child(make_range(SplitPoint, Key.end()));
Key = make_range(Key.begin(), SplitPoint);
Children.swap(Child.Children);
std::swap(Value, Child.Value);
Children.emplace_back(std::move(Child));
}
};
/// Root always corresponds to the empty key, which is the shortest possible
/// prefix for everything.
Node Root;
ContainerType KeyValuePairs;
/// Finds or creates a new tail or leaf node corresponding to the `Key`.
Node &findOrCreate(KeyConstIteratorRangeType Key) {
Node *Curr = &Root;
if (Key.empty())
return *Curr;
for (;;) {
// The root node's Key is default-constructed with singular iterators
// that cannot be passed to std::mismatch. Since the root's key is
// conceptually empty the mismatch is a no-op: skip it.
if (Curr != &Root) {
auto [I1, I2] = llvm::mismatch(Key, Curr->Key);
Key = make_range(I1, Key.end());
if (I2 != Curr->Key.end()) {
// Match is partial. Either query is too short, or there is
// mismatching character. Split either way, and put new node in
// between of the current and its children.
Curr->split(I2);
// Split was caused by mismatch, so `findChild` would fail.
break;
}
}
Node *Child = Curr->findChild(Key);
if (!Child)
break;
// Move to child with the same first character.
Curr = Child;
}
if (Key.empty()) {
// The current node completely matches the key, return it.
return *Curr;
}
// `Key` is a suffix of original `Key` unmatched by path from the `Root` to
// the `Curr`, and we have no candidate in the children to match more.
// Create a new one.
return Curr->Children.emplace_back(Key);
}
///
/// An iterator for traversing prefixes search results.
///
/// This iterator is used by `find_prefixes` to traverse the tree and find
/// elements that are prefixes to the given key. It's a forward iterator.
///
/// \tparam MappedType The type of the value pointed to by the iterator.
/// This will be `value_type` for non-const iterators
/// and `const value_type` for const iterators.
template <typename MappedType>
class IteratorImpl
: public iterator_facade_base<IteratorImpl<MappedType>,
std::forward_iterator_tag, MappedType> {
const Node *Curr = nullptr;
KeyConstIteratorRangeType Query{KeyConstIteratorType{},
KeyConstIteratorType{}};
void findNextValid() {
while (Curr && !Curr->Value.has_value())
advance();
}
void advance() {
assert(Curr);
if (Query.empty()) {
Curr = nullptr;
return;
}
Curr = Curr->findChild(Query);
if (!Curr) {
Curr = nullptr;
return;
}
auto [I1, I2] = llvm::mismatch(Query, Curr->Key);
if (I2 != Curr->Key.end()) {
Curr = nullptr;
return;
}
Query = make_range(I1, Query.end());
}
friend class RadixTree;
IteratorImpl(const Node *C, const KeyConstIteratorRangeType &Q)
: Curr(C), Query(Q) {
findNextValid();
}
public:
IteratorImpl() = default;
MappedType &operator*() const { return **Curr->Value; }
IteratorImpl &operator++() {
advance();
findNextValid();
return *this;
}
bool operator==(const IteratorImpl &Other) const {
return Curr == Other.Curr;
}
};
public:
RadixTree() = default;
RadixTree(RadixTree &&) = default;
RadixTree &operator=(RadixTree &&) = default;
using prefix_iterator = IteratorImpl<value_type>;
using const_prefix_iterator = IteratorImpl<const value_type>;
using iterator = typename ContainerType::iterator;
using const_iterator = typename ContainerType::const_iterator;
/// Returns true if the tree is empty.
bool empty() const { return KeyValuePairs.empty(); }
/// Returns the number of elements in the tree.
size_t size() const { return KeyValuePairs.size(); }
/// Returns the number of nodes in the tree.
///
/// This function counts all internal nodes in the tree. It can be useful for
/// understanding the memory footprint or complexity of the tree structure.
size_t countNodes() const { return Root.countNodes(); }
/// Returns an iterator to the first element.
iterator begin() { return KeyValuePairs.begin(); }
const_iterator begin() const { return KeyValuePairs.begin(); }
/// Returns an iterator to the end of the tree.
iterator end() { return KeyValuePairs.end(); }
const_iterator end() const { return KeyValuePairs.end(); }
/// Constructs and inserts a new element into the tree.
///
/// This function constructs an element in place within the tree. If an
/// element with the same key already exists, the insertion fails and the
/// function returns an iterator to the existing element along with `false`.
/// Otherwise, the new element is inserted and the function returns an
/// iterator to the new element along with `true`.
///
/// \param Key The key of the element to construct.
/// \param Args Arguments to forward to the constructor of the mapped_type.
/// \return A pair consisting of an iterator to the inserted element (or to
/// the element that prevented insertion) and a boolean value
/// indicating whether the insertion took place.
template <typename... Ts>
std::pair<iterator, bool> emplace(key_type &&Key, Ts &&...Args) {
// We want to make new `Node` to refer key in the container, not the one
// from the argument.
// FIXME: Determine that we need a new node, before expanding
// `KeyValuePairs`.
const value_type &NewValue = KeyValuePairs.emplace_front(
std::move(Key), T(std::forward<Ts>(Args)...));
Node &Node = findOrCreate(NewValue.first);
bool HasValue = Node.Value.has_value();
if (!HasValue)
Node.Value = KeyValuePairs.begin();
else
KeyValuePairs.pop_front();
return {*Node.Value, !HasValue};
}
///
/// Finds all elements whose keys are prefixes of the given `Key`.
///
/// This function returns an iterator range over all elements in the tree
/// whose keys are prefixes of the provided `Key`. For example, if the tree
/// contains "abcde", "abc", "abcdefgh", and `Key` is "abcde", this function
/// would return iterators to "abcde" and "abc".
///
/// \param Key The key to search for prefixes of.
/// \return An `iterator_range` of `const_prefix_iterator`s, allowing
/// iteration over the found prefix elements.
/// \note The returned iterators reference the `Key` provided by the caller.
/// The caller must ensure that `Key` remains valid for the lifetime
/// of the iterators.
iterator_range<const_prefix_iterator>
find_prefixes(const key_type &Key) const {
return iterator_range<const_prefix_iterator>{
const_prefix_iterator(&Root, KeyConstIteratorRangeType(Key)),
const_prefix_iterator{}};
}
};
} // namespace llvm
#endif // LLVM_ADT_RADIXTREE_H