mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 19:15:54 +00:00
363 lines
10 KiB
C++
363 lines
10 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
This file is part of rippled: https://github.com/ripple/rippled
|
|
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
//==============================================================================
|
|
|
|
#include <xrpl/basics/contract.h>
|
|
#include <xrpl/beast/utility/instrumentation.h>
|
|
#include <xrpl/ledger/ApplyView.h>
|
|
#include <xrpl/protocol/Protocol.h>
|
|
|
|
namespace ripple {
|
|
|
|
std::optional<std::uint64_t>
|
|
ApplyView::dirAdd(
|
|
bool preserveOrder,
|
|
Keylet const& directory,
|
|
uint256 const& key,
|
|
std::function<void(std::shared_ptr<SLE> const&)> const& describe)
|
|
{
|
|
auto root = peek(directory);
|
|
|
|
if (!root)
|
|
{
|
|
// No root, make it.
|
|
root = std::make_shared<SLE>(directory);
|
|
root->setFieldH256(sfRootIndex, directory.key);
|
|
describe(root);
|
|
|
|
STVector256 v;
|
|
v.push_back(key);
|
|
root->setFieldV256(sfIndexes, v);
|
|
|
|
insert(root);
|
|
return std::uint64_t{0};
|
|
}
|
|
|
|
std::uint64_t page = root->getFieldU64(sfIndexPrevious);
|
|
|
|
auto node = root;
|
|
|
|
if (page)
|
|
{
|
|
node = peek(keylet::page(directory, page));
|
|
if (!node)
|
|
LogicError("Directory chain: root back-pointer broken.");
|
|
}
|
|
|
|
auto indexes = node->getFieldV256(sfIndexes);
|
|
|
|
// If there's space, we use it:
|
|
if (indexes.size() < dirNodeMaxEntries)
|
|
{
|
|
if (preserveOrder)
|
|
{
|
|
if (std::find(indexes.begin(), indexes.end(), key) != indexes.end())
|
|
LogicError("dirInsert: double insertion");
|
|
|
|
indexes.push_back(key);
|
|
}
|
|
else
|
|
{
|
|
// We can't be sure if this page is already sorted because
|
|
// it may be a legacy page we haven't yet touched. Take
|
|
// the time to sort it.
|
|
std::sort(indexes.begin(), indexes.end());
|
|
|
|
auto pos = std::lower_bound(indexes.begin(), indexes.end(), key);
|
|
|
|
if (pos != indexes.end() && key == *pos)
|
|
LogicError("dirInsert: double insertion");
|
|
|
|
indexes.insert(pos, key);
|
|
}
|
|
|
|
node->setFieldV256(sfIndexes, indexes);
|
|
update(node);
|
|
return page;
|
|
}
|
|
|
|
// Check whether we're out of pages.
|
|
if (++page >= dirNodeMaxPages)
|
|
return std::nullopt;
|
|
|
|
// We are about to create a new node; we'll link it to
|
|
// the chain first:
|
|
node->setFieldU64(sfIndexNext, page);
|
|
update(node);
|
|
|
|
root->setFieldU64(sfIndexPrevious, page);
|
|
update(root);
|
|
|
|
// Insert the new key:
|
|
indexes.clear();
|
|
indexes.push_back(key);
|
|
|
|
node = std::make_shared<SLE>(keylet::page(directory, page));
|
|
node->setFieldH256(sfRootIndex, directory.key);
|
|
node->setFieldV256(sfIndexes, indexes);
|
|
|
|
// Save some space by not specifying the value 0 since
|
|
// it's the default.
|
|
if (page != 1)
|
|
node->setFieldU64(sfIndexPrevious, page - 1);
|
|
describe(node);
|
|
insert(node);
|
|
|
|
return page;
|
|
}
|
|
|
|
bool
|
|
ApplyView::emptyDirDelete(Keylet const& directory)
|
|
{
|
|
auto node = peek(directory);
|
|
|
|
if (!node)
|
|
return false;
|
|
|
|
// Verify that the passed directory node is the directory root.
|
|
if (directory.type != ltDIR_NODE ||
|
|
node->getFieldH256(sfRootIndex) != directory.key)
|
|
{
|
|
UNREACHABLE("ripple::ApplyView::emptyDirDelete : invalid node type");
|
|
return false;
|
|
}
|
|
|
|
// The directory still contains entries and so it cannot be removed
|
|
if (!node->getFieldV256(sfIndexes).empty())
|
|
return false;
|
|
|
|
std::uint64_t constexpr rootPage = 0;
|
|
auto prevPage = node->getFieldU64(sfIndexPrevious);
|
|
auto nextPage = node->getFieldU64(sfIndexNext);
|
|
|
|
if (nextPage == rootPage && prevPage != rootPage)
|
|
LogicError("Directory chain: fwd link broken");
|
|
|
|
if (prevPage == rootPage && nextPage != rootPage)
|
|
LogicError("Directory chain: rev link broken");
|
|
|
|
// Older versions of the code would, in some cases, allow the last
|
|
// page to be empty. Remove such pages:
|
|
if (nextPage == prevPage && nextPage != rootPage)
|
|
{
|
|
auto last = peek(keylet::page(directory, nextPage));
|
|
|
|
if (!last)
|
|
LogicError("Directory chain: fwd link broken.");
|
|
|
|
if (!last->getFieldV256(sfIndexes).empty())
|
|
return false;
|
|
|
|
// Update the first page's linked list and
|
|
// mark it as updated.
|
|
node->setFieldU64(sfIndexNext, rootPage);
|
|
node->setFieldU64(sfIndexPrevious, rootPage);
|
|
update(node);
|
|
|
|
// And erase the empty last page:
|
|
erase(last);
|
|
|
|
// Make sure our local values reflect the
|
|
// updated information:
|
|
nextPage = rootPage;
|
|
prevPage = rootPage;
|
|
}
|
|
|
|
// If there are no other pages, erase the root:
|
|
if (nextPage == rootPage && prevPage == rootPage)
|
|
erase(node);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
ApplyView::dirRemove(
|
|
Keylet const& directory,
|
|
std::uint64_t page,
|
|
uint256 const& key,
|
|
bool keepRoot)
|
|
{
|
|
auto node = peek(keylet::page(directory, page));
|
|
|
|
if (!node)
|
|
return false;
|
|
|
|
std::uint64_t constexpr rootPage = 0;
|
|
|
|
{
|
|
auto entries = node->getFieldV256(sfIndexes);
|
|
|
|
auto it = std::find(entries.begin(), entries.end(), key);
|
|
|
|
if (entries.end() == it)
|
|
return false;
|
|
|
|
// We always preserve the relative order when we remove.
|
|
entries.erase(it);
|
|
|
|
node->setFieldV256(sfIndexes, entries);
|
|
update(node);
|
|
|
|
if (!entries.empty())
|
|
return true;
|
|
}
|
|
|
|
// The current page is now empty; check if it can be
|
|
// deleted, and, if so, whether the entire directory
|
|
// can now be removed.
|
|
auto prevPage = node->getFieldU64(sfIndexPrevious);
|
|
auto nextPage = node->getFieldU64(sfIndexNext);
|
|
|
|
// The first page is the directory's root node and is
|
|
// treated specially: it can never be deleted even if
|
|
// it is empty, unless we plan on removing the entire
|
|
// directory.
|
|
if (page == rootPage)
|
|
{
|
|
if (nextPage == page && prevPage != page)
|
|
LogicError("Directory chain: fwd link broken");
|
|
|
|
if (prevPage == page && nextPage != page)
|
|
LogicError("Directory chain: rev link broken");
|
|
|
|
// Older versions of the code would, in some cases,
|
|
// allow the last page to be empty. Remove such
|
|
// pages if we stumble on them:
|
|
if (nextPage == prevPage && nextPage != page)
|
|
{
|
|
auto last = peek(keylet::page(directory, nextPage));
|
|
if (!last)
|
|
LogicError("Directory chain: fwd link broken.");
|
|
|
|
if (last->getFieldV256(sfIndexes).empty())
|
|
{
|
|
// Update the first page's linked list and
|
|
// mark it as updated.
|
|
node->setFieldU64(sfIndexNext, page);
|
|
node->setFieldU64(sfIndexPrevious, page);
|
|
update(node);
|
|
|
|
// And erase the empty last page:
|
|
erase(last);
|
|
|
|
// Make sure our local values reflect the
|
|
// updated information:
|
|
nextPage = page;
|
|
prevPage = page;
|
|
}
|
|
}
|
|
|
|
if (keepRoot)
|
|
return true;
|
|
|
|
// If there's no other pages, erase the root:
|
|
if (nextPage == page && prevPage == page)
|
|
erase(node);
|
|
|
|
return true;
|
|
}
|
|
|
|
// This can never happen for nodes other than the root:
|
|
if (nextPage == page)
|
|
LogicError("Directory chain: fwd link broken");
|
|
|
|
if (prevPage == page)
|
|
LogicError("Directory chain: rev link broken");
|
|
|
|
// This node isn't the root, so it can either be in the
|
|
// middle of the list, or at the end. Unlink it first
|
|
// and then check if that leaves the list with only a
|
|
// root:
|
|
auto prev = peek(keylet::page(directory, prevPage));
|
|
if (!prev)
|
|
LogicError("Directory chain: fwd link broken.");
|
|
// Fix previous to point to its new next.
|
|
prev->setFieldU64(sfIndexNext, nextPage);
|
|
update(prev);
|
|
|
|
auto next = peek(keylet::page(directory, nextPage));
|
|
if (!next)
|
|
LogicError("Directory chain: rev link broken.");
|
|
// Fix next to point to its new previous.
|
|
next->setFieldU64(sfIndexPrevious, prevPage);
|
|
update(next);
|
|
|
|
// The page is no longer linked. Delete it.
|
|
erase(node);
|
|
|
|
// Check whether the next page is the last page and, if
|
|
// so, whether it's empty. If it is, delete it.
|
|
if (nextPage != rootPage && next->getFieldU64(sfIndexNext) == rootPage &&
|
|
next->getFieldV256(sfIndexes).empty())
|
|
{
|
|
// Since next doesn't point to the root, it
|
|
// can't be pointing to prev.
|
|
erase(next);
|
|
|
|
// The previous page is now the last page:
|
|
prev->setFieldU64(sfIndexNext, rootPage);
|
|
update(prev);
|
|
|
|
// And the root points to the last page:
|
|
auto root = peek(keylet::page(directory, rootPage));
|
|
if (!root)
|
|
LogicError("Directory chain: root link broken.");
|
|
root->setFieldU64(sfIndexPrevious, prevPage);
|
|
update(root);
|
|
|
|
nextPage = rootPage;
|
|
}
|
|
|
|
// If we're not keeping the root, then check to see if
|
|
// it's left empty. If so, delete it as well.
|
|
if (!keepRoot && nextPage == rootPage && prevPage == rootPage)
|
|
{
|
|
if (prev->getFieldV256(sfIndexes).empty())
|
|
erase(prev);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
ApplyView::dirDelete(
|
|
Keylet const& directory,
|
|
std::function<void(uint256 const&)> const& callback)
|
|
{
|
|
std::optional<std::uint64_t> pi;
|
|
|
|
do
|
|
{
|
|
auto const page = peek(keylet::page(directory, pi.value_or(0)));
|
|
|
|
if (!page)
|
|
return false;
|
|
|
|
for (auto const& item : page->getFieldV256(sfIndexes))
|
|
callback(item);
|
|
|
|
pi = (*page)[~sfIndexNext];
|
|
|
|
erase(page);
|
|
} while (pi);
|
|
|
|
return true;
|
|
}
|
|
|
|
} // namespace ripple
|