19 case E::INVALID_NODE_TYPE:
20 return "The trie node type is invalid";
22 return "Unknown error";
30 std::shared_ptr<TrieNode> root) noexcept
33 static outcome::result<std::unique_ptr<OpaqueNodeStorage>>
createAt(
34 std::shared_ptr<OpaqueTrieNode> root,
36 OUTCOME_TRY(root_node, node_retriever(root));
37 return std::unique_ptr<OpaqueNodeStorage>{
41 [[nodiscard]]
const std::shared_ptr<TrieNode> &
getRoot() {
45 [[nodiscard]] std::shared_ptr<const TrieNode>
getRoot()
const {
49 void setRoot(
const std::shared_ptr<TrieNode> &root) {
53 [[nodiscard]] outcome::result<std::shared_ptr<const TrieNode>>
getChild(
58 auto &mut_parent =
const_cast<BranchNode &
>(parent);
59 auto &opaque_child = parent.
children.at(idx);
61 mut_parent.children.at(idx) = child;
62 return std::move(child);
65 [[nodiscard]] outcome::result<std::shared_ptr<TrieNode>>
getChild(
69 OUTCOME_TRY(const_child, const_this->getChild(parent, idx));
71 auto child = std::const_pointer_cast<
TrieNode>(const_child);
77 std::shared_ptr<TrieNode>
root_;
84 uint32_t getCommonPrefixLength(
const NibblesView &first,
87 std::mismatch(first.begin(), first.end(), second.begin(), second.end());
88 return it - first.begin();
99 [[nodiscard]] outcome::result<void> handleDeletion(
103 if (!parent->isBranch())
return outcome::success();
104 auto &branch =
dynamic_cast<BranchNode &
>(*parent);
109 parent = std::make_shared<LeafNode>(parent->key_nibbles, parent->value);
110 SL_TRACE(logger,
"handleDeletion: turn childless branch into a leaf");
114 SL_TRACE(logger,
"handleDeletion: nullify valueless branch parent");
116 }
else if (branch.childrenNum() == 1 && !branch.value) {
118 while (bitmap >>= 1u) ++idx;
119 OUTCOME_TRY(child, node_storage.
getChild(branch, idx));
122 if (child->getTrieType() == T::Leaf) {
123 parent = std::make_shared<LeafNode>(parent->key_nibbles, child->value);
125 "handleDeletion: turn a branch with single leaf child into " 127 }
else if (child->isBranch()) {
129 parent->value = child->value;
131 "handleDeletion: turn a branch with single branch child into " 134 parent->key_nibbles.putUint8(idx).put(child->key_nibbles);
136 return outcome::success();
142 [[nodiscard]] outcome::result<void> deleteNode(
147 if (node ==
nullptr) {
148 return outcome::success();
151 "deleteNode: currently in {}, sought key is {}",
152 node->key_nibbles.toHex(),
155 if (node->isBranch()) {
156 auto &branch =
dynamic_cast<BranchNode &
>(*node);
157 if (node->key_nibbles == sought_key) {
158 SL_TRACE(logger,
"deleteNode: deleting value in branch; stop");
159 node->value = std::nullopt;
161 auto length = getCommonPrefixLength(node->key_nibbles, sought_key);
162 OUTCOME_TRY(child, node_storage.
getChild(branch, sought_key[length]));
164 logger,
"deleteNode: go to child {:x}", (
int)sought_key[length]);
165 OUTCOME_TRY(deleteNode(
166 logger, child, sought_key.subspan(length + 1), node_storage));
167 branch.children[sought_key[length]] = child;
169 OUTCOME_TRY(handleDeletion(logger, node, node_storage));
170 }
else if (node->key_nibbles == sought_key) {
171 SL_TRACE(logger,
"deleteNode: nullifying leaf node; stop");
174 return outcome::success();
177 outcome::result<void> notifyOnDetached(
180 auto key = node->key_nibbles.toByteBuffer();
181 OUTCOME_TRY(callback(key, std::move(node->value)));
182 return outcome::success();
191 [[nodiscard]] outcome::result<void> detachNode(
193 std::shared_ptr<TrieNode> &parent,
195 std::optional<uint64_t> limit,
200 if (parent ==
nullptr) {
201 return outcome::success();
206 return outcome::success();
209 if (std::greater_equal<size_t>()(parent->key_nibbles.size(),
213 prefix.begin(), prefix.end(), parent->key_nibbles.begin())) {
215 if (parent->isBranch()) {
216 auto &branch =
dynamic_cast<BranchNode &
>(*parent);
217 for (uint8_t child_idx = 0; child_idx < branch.kMaxChildren;
219 if (branch.children[child_idx] !=
nullptr) {
220 OUTCOME_TRY(child_node, node_storage.
getChild(branch, child_idx));
221 OUTCOME_TRY(detachNode(logger,
229 branch.children[child_idx] = child_node;
233 if (not limit or count < limit.value()) {
235 OUTCOME_TRY(notifyOnDetached(parent, callback));
244 if (parent->isBranch()) {
246 OUTCOME_TRY(handleDeletion(logger, parent, node_storage));
249 return outcome::success();
255 if (not std::equal(parent->key_nibbles.begin(),
256 parent->key_nibbles.end(),
258 return outcome::success();
261 if (parent->isBranch()) {
262 const auto length = parent->key_nibbles.size();
263 auto &branch =
dynamic_cast<BranchNode &
>(*parent);
264 auto &child = branch.
children.at(prefix[length]);
265 if (child !=
nullptr) {
266 OUTCOME_TRY(child_node, node_storage.
getChild(branch, prefix[length]));
267 OUTCOME_TRY(detachNode(logger,
269 prefix.subspan(length + 1),
275 branch.children[prefix[length]] = child_node;
276 OUTCOME_TRY(handleDeletion(logger, parent, node_storage));
279 return outcome::success();
287 : nodes_{std::make_unique<OpaqueNodeStorage>(std::move(f),
nullptr)},
291 :
nodes_{std::make_unique<OpaqueNodeStorage>(std::move(f), root)},
298 auto value_copy = value;
299 return put(key, std::move(value_copy));
320 insert(root, k_enc, std::make_shared<LeafNode>(k_enc, value)));
323 return outcome::success();
328 std::optional<uint64_t> limit,
330 bool finished =
true;
333 auto root =
nodes_->getRoot();
334 OUTCOME_TRY(detachNode(
335 logger_, root, key_nibbles, limit, finished, count, callback, *
nodes_));
337 return {finished, count};
345 if (parent ==
nullptr) {
346 node->key_nibbles = key_nibbles;
350 const auto node_type = parent->getTrieType();
353 case T::BranchEmptyValue:
354 case T::BranchWithValue: {
355 auto parent_as_branch = std::dynamic_pointer_cast<
BranchNode>(parent);
356 return updateBranch(parent_as_branch, key_nibbles, node);
361 auto br = std::make_shared<BranchNode>();
362 auto length = getCommonPrefixLength(key_nibbles, parent->key_nibbles);
364 if (parent->key_nibbles == key_nibbles
365 && key_nibbles.size() == length) {
366 node->key_nibbles = key_nibbles;
370 br->key_nibbles =
KeyNibbles{key_nibbles.subspan(0, length)};
371 auto parentKey = parent->key_nibbles;
374 if (key_nibbles.size() == length) {
375 br->value = node->value;
379 if (static_cast<std::ptrdiff_t>(parent->key_nibbles.size())
380 > key_nibbles.size()) {
381 parent->key_nibbles = parent->key_nibbles.subbuffer(length + 1);
382 br->children.at(parentKey[length]) = parent;
388 node->key_nibbles =
KeyNibbles{key_nibbles.subspan(length + 1)};
390 if (length == parent->key_nibbles.size()) {
393 br->value = parent->value;
394 br->children.at(key_nibbles[length]) = node;
398 parent->key_nibbles = parent->key_nibbles.subbuffer(length + 1);
399 br->children.at(parentKey[length]) = parent;
400 br->children.at(key_nibbles[length]) = node;
406 case T::LeafContainingHashes:
409 case T::BranchContainingHashes:
415 case T::ReservedForCompactEncoding:
425 auto length = getCommonPrefixLength(key_nibbles, parent->key_nibbles);
427 if (length == parent->key_nibbles.size()) {
429 if (key_nibbles == parent->key_nibbles) {
430 parent->value = node->value;
433 OUTCOME_TRY(child,
retrieveChild(*parent, key_nibbles[length]));
435 OUTCOME_TRY(n,
insert(child, key_nibbles.subspan(length + 1), node));
436 parent->children.at(key_nibbles[length]) = n;
439 node->key_nibbles =
KeyNibbles{key_nibbles.subspan(length + 1)};
440 parent->children.at(key_nibbles[length]) = node;
443 auto br = std::make_shared<BranchNode>(
445 auto parentIdx = parent->key_nibbles[length];
448 insert(
nullptr, parent->key_nibbles.subspan(length + 1), parent));
449 br->children.at(parentIdx) = new_branch;
450 if (key_nibbles.size() <= length) {
451 br->value = node->value;
453 OUTCOME_TRY(new_child,
454 insert(
nullptr, key_nibbles.subspan(length + 1), node));
455 br->children.at(key_nibbles[length]) = new_child;
462 OUTCOME_TRY(opt_value,
tryGet(key));
463 if (opt_value.has_value()) {
464 return opt_value.value();
469 outcome::result<std::optional<common::BufferConstRef>>
471 if (not
nodes_->getRoot()) {
476 if (node && node->value) {
477 return node->value.value();
488 OUTCOME_TRY(const_node, const_this->getNode(parent, key_nibbles));
490 auto node = std::const_pointer_cast<
TrieNode>(const_node);
497 if (current ==
nullptr) {
501 const auto node_type = current->getTrieType();
503 case T::BranchEmptyValue:
504 case T::BranchWithValue: {
505 if (current->key_nibbles == nibbles or nibbles.empty()) {
508 if (nibbles.size() <
static_cast<long>(current->key_nibbles.size())) {
511 auto parent_as_branch =
512 std::dynamic_pointer_cast<
const BranchNode>(current);
513 auto length = getCommonPrefixLength(current->key_nibbles, nibbles);
514 OUTCOME_TRY(n,
retrieveChild(*parent_as_branch, nibbles[length]));
515 return getNode(n, nibbles.subspan(length + 1));
519 if (current->key_nibbles == nibbles) {
524 case T::LeafContainingHashes:
527 case T::BranchContainingHashes:
533 case T::ReservedForCompactEncoding:
545 const std::function<outcome::result<void>(
BranchNode const &,
546 uint8_t idx)> &callback)
const {
548 if (parent ==
nullptr) {
552 const auto node_type = parent->getTrieType();
554 case T::BranchEmptyValue:
555 case T::BranchWithValue: {
557 if (parent->key_nibbles == path or path.empty()) {
558 return outcome::success();
560 auto common_length = getCommonPrefixLength(parent->key_nibbles, path);
561 auto common_nibbles =
565 if (path == common_nibbles
567 <
static_cast<ssize_t
>(parent->key_nibbles.size())) {
568 return outcome::success();
570 auto parent_as_branch =
571 std::dynamic_pointer_cast<
const BranchNode>(parent);
574 OUTCOME_TRY(callback(*parent_as_branch, path[common_length]));
575 return forNodeInPath(child, path.subspan(common_length + 1), callback);
579 if (parent->key_nibbles == path) {
580 return outcome::success();
584 case T::LeafContainingHashes:
587 case T::BranchContainingHashes:
593 case T::ReservedForCompactEncoding:
603 return std::make_unique<PolkadotTrieCursorImpl>(shared_from_this());
608 if (not
nodes_->getRoot()) {
614 return node !=
nullptr && node->value;
618 return nodes_->getRoot() ==
nullptr;
626 auto root =
nodes_->getRoot();
628 logger_,
"Remove by key {:l} (nibbles {})", key, key_nibbles.
toHex());
629 OUTCOME_TRY(deleteNode(
logger_, root, key_nibbles, *
nodes_));
631 return outcome::success();
635 const BranchNode &parent, uint8_t idx)
const {
636 OUTCOME_TRY(node,
nodes_->getChild(parent, idx));
637 return std::move(node);
642 return nodes_->getChild(parent, idx);
outcome::result< std::tuple< bool, uint32_t > > clearPrefix(const common::BufferView &prefix, std::optional< uint64_t > limit, const OnDetachCallback &callback) override
outcome::result< void > remove(const common::BufferView &key) override
Remove value by key.
Class represents arbitrary (including empty) byte buffer.
uint16_t childrenBitmap() const
std::array< std::shared_ptr< OpaqueTrieNode >, kMaxChildren > children
outcome::result< ConstNodePtr > retrieveChild(const BranchNode &parent, uint8_t idx) const override
outcome::result< void > forNodeInPath(ConstNodePtr parent, const NibblesView &path, const std::function< outcome::result< void >(BranchNode const &, uint8_t idx)> &callback) const override
OUTCOME_CPP_DEFINE_CATEGORY(kagome::storage::trie, PolkadotTrieImpl::Error, e)
outcome::result< NodePtr > updateBranch(BranchPtr parent, const NibblesView &key_nibbles, const NodePtr &node)
OpaqueNodeStorage(PolkadotTrie::NodeRetrieveFunctor node_retriever, std::shared_ptr< TrieNode > root) noexcept
std::shared_ptr< TrieNode > NodePtr
PolkadotTrie::NodeRetrieveFunctor retrieve_node_
void setRoot(const std::shared_ptr< TrieNode > &root)
outcome::result< common::BufferConstRef > get(const common::BufferView &key) const override
Get value by key.
outcome::result< NodePtr > insert(const NodePtr &parent, const NibblesView &key_nibbles, NodePtr node)
outcome::result< std::shared_ptr< TrieNode > > getChild(BranchNode const &parent, uint8_t idx)
std::function< outcome::result< void >(const common::BufferView &key, std::optional< common::Buffer > &&value)> OnDetachCallback
gsl::span< const uint8_t > make_span(const rocksdb::Slice &s)
PolkadotTrieImpl(PolkadotTrie::NodeRetrieveFunctor f=PolkadotTrie::defaultNodeRetrieveFunctor)
SLBuffer< std::numeric_limits< size_t >::max()> Buffer
std::shared_ptr< BranchNode > BranchPtr
bool empty() const override
Returns true if the storage is empty.
std::shared_ptr< soralog::Logger > Logger
std::shared_ptr< TrieNode > root_
std::function< outcome::result< NodePtr >(std::shared_ptr< OpaqueTrieNode > const &)> NodeRetrieveFunctor
std::shared_ptr< const TrieNode > ConstNodePtr
outcome::result< std::shared_ptr< const TrieNode > > getChild(BranchNode const &parent, uint8_t idx) const
outcome::result< std::optional< common::BufferConstRef > > tryGet(const common::BufferView &key) const override
Get value by key.
static outcome::result< std::unique_ptr< OpaqueNodeStorage > > createAt(std::shared_ptr< OpaqueTrieNode > root, PolkadotTrie::NodeRetrieveFunctor node_retriever)
outcome::result< bool > contains(const common::BufferView &key) const override
Checks if given key-value binding exists in the storage.
std::unique_ptr< PolkadotTrieCursor > trieCursor() override
std::unique_ptr< OpaqueNodeStorage > nodes_
outcome::result< NodePtr > getNode(ConstNodePtr parent, const NibblesView &key_nibbles) override
std::shared_ptr< const TrieNode > getRoot() const
NodePtr getRoot() override
Logger createLogger(const std::string &tag)
static KeyNibbles fromByteBuffer(const common::BufferView &key)
outcome::result< void > put(const common::BufferView &key, const common::Buffer &value) override
Store value by key.
const std::shared_ptr< TrieNode > & getRoot()
std::string toHex() const