21 case E::INVALID_NODE_TYPE:
22 return "The processed node type is invalid";
23 case E::INVALID_CURSOR_ACCESS:
24 return "A trie cursor in an invalid state has been accessed (e.g. " 26 case E::KEY_NOT_FOUND:
27 return "The requested key was not found";
29 return "Unknown TrieCursor error";
34 [[nodiscard]] outcome::result<void>
39 path_.emplace_back(*current_as_branch, index);
41 return outcome::success();
45 std::shared_ptr<const PolkadotTrie> trie)
47 trie_{std::move(trie)},
52 outcome::result<std::unique_ptr<PolkadotTrieCursorImpl>>
55 const std::shared_ptr<const PolkadotTrie> &trie) {
56 auto cursor = std::make_unique<PolkadotTrieCursorImpl>(trie);
57 OUTCOME_TRY(search_state, cursor->makeSearchStateAt(key));
58 cursor->state_ = std::move(search_state);
70 if (
trie_->getRoot() ==
nullptr) {
76 if (res.has_error()) {
85 state_ = std::move(res.value());
86 auto ¤t = std::get<SearchState>(
state_).getCurrent();
89 if (not current.value.has_value()) {
98 auto *current =
trie_->getRoot().get();
99 if (current ==
nullptr) {
104 auto &search_state = std::get<SearchState>(
state_);
108 auto type = current->getTrieType();
111 auto &branch =
dynamic_cast<const BranchNode &
>(*current);
114 if (branch.children.at(i) !=
nullptr) {
117 current = &search_state.getCurrent();
131 const TrieNode ¤t, gsl::span<const uint8_t> sought_nibbles) {
133 auto [sought_nibbles_mismatch, current_mismatch] =
134 std::mismatch(sought_nibbles.begin(),
135 sought_nibbles.end(),
139 bool sought_is_prefix = sought_nibbles_mismatch == sought_nibbles.end();
140 bool current_is_prefix = current_mismatch == current.
key_nibbles.end();
144 bool sought_less_or_eq =
146 or (not current_is_prefix
147 and *sought_nibbles_mismatch < *current_mismatch);
149 "The sought key '{}' is {} than current '{}'",
151 sought_less_or_eq ?
"less or eq" :
"greater",
153 if (sought_less_or_eq) {
157 SL_TRACE(
log_,
"We're in a branch and search next node in subtree");
159 return outcome::success();
162 SL_TRACE(
log_,
"We're in a leaf {} and done",
key().
value());
163 return outcome::success();
173 bool sought_is_longer = current_is_prefix and not sought_is_prefix;
174 if (sought_is_longer) {
178 auto mismatch_pos = sought_nibbles_mismatch - sought_nibbles.begin();
179 auto &branch =
dynamic_cast<const BranchNode &
>(current);
184 std::get<SearchState>(
state_).getPath().back().child_idx;
186 "We're in a branch and proceed to child {:x}",
188 if (child_idx > sought_nibbles[mismatch_pos]) {
192 *child, sought_nibbles.subspan(mismatch_pos + 1));
207 bool longer_or_greater =
209 or (not(sought_is_prefix or current_is_prefix)
210 and *sought_nibbles_mismatch > *current_mismatch);
211 if (longer_or_greater) {
212 SL_TRACE(
log_,
"We're looking for next node with value in outer tree");
218 return outcome::success();
225 if (
trie_->getRoot() ==
nullptr) {
226 SL_TRACE(
log_,
"Seek lower bound for {} -> null root", key);
228 return outcome::success();
233 return outcome::success();
237 BOOST_ASSERT(std::holds_alternative<SearchState>(
state_));
238 auto &search_state = std::get<SearchState>(
state_);
240 const auto &search_path = search_state.getPath();
241 while (not search_path.empty()) {
242 auto [parent, idx] = search_path.back();
243 BOOST_VERIFY_MSG(search_state.leaveChild(),
244 "Guaranteed by the loop condition");
246 if (child !=
nullptr) {
248 "A greater child exists (idx {}), proceed to it",
249 search_path.back().child_idx);
259 auto *current = &parent;
260 while (not current->value.has_value()) {
261 if (not current->isBranch()) {
266 "Proceed to child {:x}",
267 (
int)std::get<SearchState>(
state_).getPath().back().child_idx);
268 BOOST_ASSERT_MSG(child !=
nullptr,
"Branch node must contain a leaf");
271 return outcome::success();
276 SL_TRACE(
log_,
"Seek upper bound for {}", sought_key);
278 if (
auto key = this->
key();
key.has_value() &&
key.value() == sought_key) {
281 return outcome::success();
284 outcome::result<const TrieNode *>
287 BOOST_ASSERT(std::holds_alternative<SearchState>(
state_));
288 auto &search_state = std::get<SearchState>(
state_);
290 auto &branch =
dynamic_cast<const BranchNode &
>(parent);
291 if (branch.children.at(i)) {
292 OUTCOME_TRY(child,
trie_->retrieveChild(branch, i));
293 BOOST_ASSERT(child !=
nullptr);
294 OUTCOME_TRY(search_state.visitChild(i, *child));
302 return std::holds_alternative<SearchState>(
state_);
306 if (std::holds_alternative<InvalidState>(
state_)) {
310 if (
trie_->getRoot() ==
nullptr) {
311 return outcome::success();
314 if (
key().has_value()) {
315 SL_TRACE(
log_,
"Searching next key, current is {}",
key().
value());
317 SL_TRACE(
log_,
"Searching next key, no current key");
320 if (std::holds_alternative<UninitializedState>(
state_)) {
322 if (
trie_->getRoot()->value) {
323 return outcome::success();
326 BOOST_ASSERT(std::holds_alternative<SearchState>(
state_));
327 auto &search_state = std::get<SearchState>(
state_);
330 if (search_state.getCurrent().isBranch()) {
331 SL_TRACE(
log_,
"We're in a branch and looking for next value in subtree");
333 BOOST_ASSERT_MSG(child !=
nullptr,
334 "Since parent is branch, there must be a child");
335 SL_TRACE(
log_,
"Go to child {}", search_state.getPath().back().child_idx);
338 return outcome::success();
341 SL_TRACE(
log_,
"We're in a leaf and looking for next value in outer tree");
344 SL_TRACE(
log_,
"Not found anything");
349 return outcome::success();
353 BOOST_ASSERT(std::holds_alternative<SearchState>(
state_));
354 const auto &search_state = std::get<SearchState>(
state_);
356 for (
const auto &node_idx : search_state.getPath()) {
357 const auto &node = node_idx.parent;
358 auto idx = node_idx.child_idx;
359 std::copy(node.key_nibbles.begin(),
360 node.key_nibbles.end(),
361 std::back_inserter<Buffer>(key_nibbles));
362 key_nibbles.putUint8(idx);
364 key_nibbles.put(search_state.getCurrent().key_nibbles);
365 return key_nibbles.toByteBuffer();
369 if (
const auto *search_state = std::get_if<SearchState>(&
state_);
370 search_state !=
nullptr) {
377 if (
const auto *search_state = std::get_if<SearchState>(&
state_);
378 search_state !=
nullptr) {
379 const auto &value_opt = search_state->getCurrent().value;
381 return std::make_optional<common::BufferConstRef>(
382 std::cref(value_opt.value()));
390 -> outcome::result<SearchState> {
391 if (
trie_->getRoot() ==
nullptr) {
396 auto add_visited_child = [&search_state,
this](
398 auto idx)
mutable -> outcome::result<void> {
399 OUTCOME_TRY(child,
trie_->retrieveChild(branch, idx));
401 search_state.visitChild(idx, *child));
402 return outcome::success();
404 auto res =
trie_->forNodeInPath(
406 if (res.has_error()) {
Class represents arbitrary (including empty) byte buffer.
std::shared_ptr< const PolkadotTrie > trie_
outcome::result< void > seekLowerBoundInternal(const TrieNode ¤t, gsl::span< const uint8_t > left_nibbles)
outcome::result< bool > seekFirst() override
Same as std::begin(...);.
auto makeSearchStateAt(const common::BufferView &key) -> outcome::result< SearchState >
#define SAFE_CALL(res, expr)
std::string hex_lower(const gsl::span< const uint8_t > bytes) noexcept
Converts bytes to hex representation.
outcome::result< void > nextNodeWithValueInSubTree(const TrieNode &subtree_root)
outcome::result< void > seekLowerBound(const common::BufferView &key) override
static constexpr uint8_t kMaxChildren
#define SAFE_VOID_CALL(expr)
outcome::result< bool > seek(const common::BufferView &key) override
Find given key and seek iterator to this key.
outcome::result< bool > nextNodeWithValueInOuterTree()
outcome::result< void > seekUpperBound(const common::BufferView &key) override
outcome::result< bool > seekLast() override
Same as std::rbegin(...);, e.g. points to the last valid element.
outcome::result< void > visitChild(uint8_t index, const TrieNode &child)
PolkadotTrieCursorImpl(std::shared_ptr< const PolkadotTrie > trie)
bool isValid() const override
Is the cursor in a valid state?
Type getTrieType() const noexcept
outcome::result< const TrieNode * > visitChildWithMinIdx(const TrieNode &node, uint8_t min_idx=0)
std::vector< TriePathEntry > path_
const TrieNode * current_
OUTCOME_CPP_DEFINE_CATEGORY(kagome::storage::trie, PolkadotTrieCursorImpl::Error, e)
Logger createLogger(const std::string &tag)
common::Buffer collectKey() const
std::optional< common::BufferConstRef > value() const override
Getter for value of the element currently pointed at.
static KeyNibbles fromByteBuffer(const common::BufferView &key)
static outcome::result< std::unique_ptr< PolkadotTrieCursorImpl > > createAt(const common::BufferView &key, const std::shared_ptr< const PolkadotTrie > &trie)
outcome::result< void > next() override
Make step forward.
std::optional< common::Buffer > key() const override
Getter for the key of the element currently pointed at.