mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-04 17:27:00 +00:00
Compare commits
3 Commits
pratik/ran
...
vlntb/reve
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc6e54b5a6 | ||
|
|
96b2c0964f | ||
|
|
1441d4690d |
6
flake.lock
generated
6
flake.lock
generated
@@ -2,11 +2,11 @@
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1777954456,
|
||||
"narHash": "sha256-hGdgeU2Nk87RAuZyYjyDjFL6LK7dAZN5RE9+hrDTkDU=",
|
||||
"lastModified": 1780243769,
|
||||
"narHash": "sha256-x5UQuRsH3MqI0U9afaXSNqzTPSeZlRLvFAav2Ux1pNw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "549bd84d6279f9852cae6225e372cc67fb91a4c1",
|
||||
"rev": "331800de5053fcebacf6813adb5db9c9dca22a0c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -534,62 +534,7 @@ public:
|
||||
std::pair<T, int>
|
||||
normalizeToRange() const;
|
||||
|
||||
/** Normalize raw (mantissa, exponent) integers directly to a target range.
|
||||
*
|
||||
* This is the construction-time counterpart of the member overload above.
|
||||
* Callers that hold raw integers (e.g. IOUAmount) and want them in a
|
||||
* narrow range would otherwise build a Number (one normalize pass to the
|
||||
* default kRange) and then call the member normalizeToRange (a second pass
|
||||
* down to the narrow range). This overload does a single pass: it converts
|
||||
* the signed mantissa to its internal magnitude and normalizes straight to
|
||||
* [MinMantissa, MaxMantissa], building no intermediate Number.
|
||||
*
|
||||
* Data flow (single pass), contrasted with the old two-pass path:
|
||||
*
|
||||
* two-pass: (m,e) --build Number--> [kRange/Large] --member--> [Min,Max]
|
||||
* one-pass: (m,e) -------------- normalize --------------> [Min,Max]
|
||||
*
|
||||
* @tparam MinMantissa Lower bound of the target mantissa range; must be a
|
||||
* positive power of ten.
|
||||
* @tparam MaxMantissa Upper bound; must equal MinMantissa * 10 - 1.
|
||||
* @tparam T Result mantissa type, int64_t or uint64_t. Defaults
|
||||
* to the type of MinMantissa.
|
||||
* @param mantissa Raw signed mantissa (sign is extracted internally).
|
||||
* @param exponent Raw exponent.
|
||||
* @return The normalized (mantissa, exponent) pair in the target range.
|
||||
* A zero mantissa is returned unchanged.
|
||||
* @note The result is bit-identical to the two-pass path: an intermediate
|
||||
* pass to a strictly wider range cannot change the final
|
||||
* narrower-range result.
|
||||
* @note Thread-safety: reads the thread-local rounding mode only; holds no
|
||||
* shared state of its own. Safe to call concurrently.
|
||||
*
|
||||
* Example (IOU range, 10^15 .. 10^16-1):
|
||||
* @code
|
||||
* auto [m, e] = Number::normalizeToRange<1'000'000'000'000'000,
|
||||
* 9'999'999'999'999'999>(1, 0);
|
||||
* // m == 1'000'000'000'000'000, e == -15
|
||||
* @endcode
|
||||
*/
|
||||
template <
|
||||
auto MinMantissa,
|
||||
auto MaxMantissa,
|
||||
Integral64 T = std::decay_t<decltype(MinMantissa)>>
|
||||
[[nodiscard]]
|
||||
static std::pair<T, int>
|
||||
normalizeToRange(rep mantissa, int exponent);
|
||||
|
||||
private:
|
||||
// Shared implementation for both normalizeToRange overloads. Takes the sign
|
||||
// and internal (uint64) magnitude already separated, normalizes in place to
|
||||
// [MinMantissa, MaxMantissa], and returns the signed (mantissa, exponent).
|
||||
template <
|
||||
auto MinMantissa,
|
||||
auto MaxMantissa,
|
||||
Integral64 T = std::decay_t<decltype(MinMantissa)>>
|
||||
static std::pair<T, int>
|
||||
normalizeToRangeImpl(bool negative, internalrep mantissa, int exponent);
|
||||
|
||||
static thread_local RoundingMode mode;
|
||||
// The available ranges for mantissa
|
||||
|
||||
@@ -834,7 +779,7 @@ Number::isnormal() const noexcept
|
||||
|
||||
template <auto MinMantissa, auto MaxMantissa, Integral64 T>
|
||||
std::pair<T, int>
|
||||
Number::normalizeToRangeImpl(bool negative, internalrep mantissa, int exponent)
|
||||
Number::normalizeToRange() const
|
||||
{
|
||||
static_assert(std::is_same_v<T, std::uint64_t> || std::is_same_v<T, std::int64_t>);
|
||||
static_assert(std::is_same_v<T, std::decay_t<decltype(MinMantissa)>>);
|
||||
@@ -847,6 +792,10 @@ Number::normalizeToRangeImpl(bool negative, internalrep mantissa, int exponent)
|
||||
static_assert(kMAX % 10 == 9);
|
||||
static_assert((kMAX + 1) / 10 == kMIN);
|
||||
|
||||
bool negative = negative_;
|
||||
internalrep mantissa = mantissa_;
|
||||
int exponent = exponent_;
|
||||
|
||||
if constexpr (std::is_unsigned_v<T>)
|
||||
{
|
||||
XRPL_ASSERT_PARTS(
|
||||
@@ -863,26 +812,6 @@ Number::normalizeToRangeImpl(bool negative, internalrep mantissa, int exponent)
|
||||
return std::make_pair(static_cast<T>(sign * mantissa), exponent);
|
||||
}
|
||||
|
||||
template <auto MinMantissa, auto MaxMantissa, Integral64 T>
|
||||
std::pair<T, int>
|
||||
Number::normalizeToRange() const
|
||||
{
|
||||
// Forward this Number's already-separated internal components to the shared
|
||||
// implementation. Passing mantissa_ (which may exceed kMaxRep in the Large
|
||||
// range) through unchanged keeps the result byte-identical to before.
|
||||
return normalizeToRangeImpl<MinMantissa, MaxMantissa, T>(negative_, mantissa_, exponent_);
|
||||
}
|
||||
|
||||
template <auto MinMantissa, auto MaxMantissa, Integral64 T>
|
||||
std::pair<T, int>
|
||||
Number::normalizeToRange(rep mantissa, int exponent)
|
||||
{
|
||||
// Separate sign and magnitude from the raw signed mantissa, then normalize
|
||||
// straight to the target range in a single pass (no intermediate Number).
|
||||
return normalizeToRangeImpl<MinMantissa, MaxMantissa, T>(
|
||||
mantissa < 0, externalToInternal(mantissa), exponent);
|
||||
}
|
||||
|
||||
constexpr Number
|
||||
abs(Number x) noexcept
|
||||
{
|
||||
|
||||
@@ -85,7 +85,7 @@ private:
|
||||
/** The sequence of the ledger that this map references, if any. */
|
||||
std::uint32_t ledgerSeq_ = 0;
|
||||
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode> root_;
|
||||
SHAMapTreeNodePtr root_;
|
||||
mutable SHAMapState state_;
|
||||
SHAMapType const type_;
|
||||
bool backed_ = true; // Map is backed by the database
|
||||
@@ -326,36 +326,32 @@ public:
|
||||
invariants() const;
|
||||
|
||||
private:
|
||||
using SharedPtrNodeStack =
|
||||
std::stack<std::pair<intr_ptr::SharedPtr<SHAMapTreeNode>, SHAMapNodeID>>;
|
||||
using SharedPtrNodeStack = std::stack<std::pair<SHAMapTreeNodePtr, SHAMapNodeID>>;
|
||||
using DeltaRef =
|
||||
std::pair<boost::intrusive_ptr<SHAMapItem const>, boost::intrusive_ptr<SHAMapItem const>>;
|
||||
|
||||
// tree node cache operations
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
cacheLookup(SHAMapHash const& hash) const;
|
||||
|
||||
void
|
||||
canonicalize(SHAMapHash const& hash, intr_ptr::SharedPtr<SHAMapTreeNode>&) const;
|
||||
canonicalize(SHAMapHash const& hash, SHAMapTreeNodePtr&) const;
|
||||
|
||||
// database operations
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
fetchNodeFromDB(SHAMapHash const& hash) const;
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
fetchNodeNT(SHAMapHash const& hash) const;
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
fetchNodeNT(SHAMapHash const& hash, SHAMapSyncFilter* filter) const;
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
fetchNode(SHAMapHash const& hash) const;
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
checkFilter(SHAMapHash const& hash, SHAMapSyncFilter* filter) const;
|
||||
|
||||
/** Update hashes up to the root */
|
||||
void
|
||||
dirtyUp(
|
||||
SharedPtrNodeStack& stack,
|
||||
uint256 const& target,
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode> terminal);
|
||||
dirtyUp(SharedPtrNodeStack& stack, uint256 const& target, SHAMapTreeNodePtr terminal);
|
||||
|
||||
/** Walk towards the specified id, returning the node. Caller must check
|
||||
if the return is nullptr, and if not, if the node->peekItem()->key() ==
|
||||
@@ -377,25 +373,21 @@ private:
|
||||
preFlushNode(intr_ptr::SharedPtr<Node> node) const;
|
||||
|
||||
/** write and canonicalize modified node */
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
writeNode(NodeObjectType t, intr_ptr::SharedPtr<SHAMapTreeNode> node) const;
|
||||
SHAMapTreeNodePtr
|
||||
writeNode(NodeObjectType t, SHAMapTreeNodePtr node) const;
|
||||
|
||||
// returns the first item at or below this node
|
||||
SHAMapLeafNode*
|
||||
firstBelow(intr_ptr::SharedPtr<SHAMapTreeNode>, SharedPtrNodeStack& stack, int branch = 0)
|
||||
const;
|
||||
firstBelow(SHAMapTreeNodePtr node, SharedPtrNodeStack& stack, int branch = 0) const;
|
||||
|
||||
// returns the last item at or below this node
|
||||
SHAMapLeafNode*
|
||||
lastBelow(
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode> node,
|
||||
SharedPtrNodeStack& stack,
|
||||
int branch = kBranchFactor) const;
|
||||
lastBelow(SHAMapTreeNodePtr node, SharedPtrNodeStack& stack, int branch = kBranchFactor) const;
|
||||
|
||||
// helper function for firstBelow and lastBelow
|
||||
SHAMapLeafNode*
|
||||
belowHelper(
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode> node,
|
||||
SHAMapTreeNodePtr node,
|
||||
SharedPtrNodeStack& stack,
|
||||
int branch,
|
||||
std::tuple<int, std::function<bool(int)>, std::function<void(int&)>> const& loopParams)
|
||||
@@ -407,15 +399,14 @@ private:
|
||||
descend(SHAMapInnerNode*, int branch) const;
|
||||
SHAMapTreeNode*
|
||||
descendThrow(SHAMapInnerNode*, int branch) const;
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
descend(SHAMapInnerNode&, int branch) const;
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
descendThrow(SHAMapInnerNode&, int branch) const;
|
||||
|
||||
// Descend with filter
|
||||
// If pending, callback is called as if it called fetchNodeNT
|
||||
using descendCallback =
|
||||
std::function<void(intr_ptr::SharedPtr<SHAMapTreeNode>, SHAMapHash const&)>;
|
||||
using descendCallback = std::function<void(SHAMapTreeNodePtr, SHAMapHash const&)>;
|
||||
SHAMapTreeNode*
|
||||
descendAsync(
|
||||
SHAMapInnerNode* parent,
|
||||
@@ -433,7 +424,7 @@ private:
|
||||
|
||||
// Non-storing
|
||||
// Does not hook the returned node to its parent
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
descendNoStore(SHAMapInnerNode&, int branch) const;
|
||||
|
||||
/** If there is only one leaf below this node, get its contents */
|
||||
@@ -495,10 +486,10 @@ private:
|
||||
|
||||
// nodes we may have acquired from deferred reads
|
||||
using DeferredNode = std::tuple<
|
||||
SHAMapInnerNode*, // parent node
|
||||
SHAMapNodeID, // parent node ID
|
||||
int, // branch
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>>; // node
|
||||
SHAMapInnerNode*, // parent node
|
||||
SHAMapNodeID, // parent node ID
|
||||
int, // branch
|
||||
SHAMapTreeNodePtr>; // node
|
||||
|
||||
int deferred;
|
||||
std::mutex deferLock;
|
||||
@@ -524,7 +515,7 @@ private:
|
||||
gmnProcessDeferredReads(MissingNodes&);
|
||||
|
||||
// fetch from DB helper function
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
finishFetch(SHAMapHash const& hash, std::shared_ptr<NodeObject> const& object) const;
|
||||
};
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
clone(std::uint32_t cowid) const final
|
||||
{
|
||||
return intr_ptr::makeShared<SHAMapAccountStateLeafNode>(item_, cowid, hash_);
|
||||
|
||||
@@ -87,7 +87,7 @@ public:
|
||||
void
|
||||
partialDestructor() override;
|
||||
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
clone(std::uint32_t cowid) const override;
|
||||
|
||||
SHAMapNodeType
|
||||
@@ -121,19 +121,19 @@ public:
|
||||
getChildHash(int m) const;
|
||||
|
||||
void
|
||||
setChild(int m, intr_ptr::SharedPtr<SHAMapTreeNode> child);
|
||||
setChild(int m, SHAMapTreeNodePtr child);
|
||||
|
||||
void
|
||||
shareChild(int m, intr_ptr::SharedPtr<SHAMapTreeNode> const& child);
|
||||
shareChild(int m, SHAMapTreeNodePtr const& child);
|
||||
|
||||
SHAMapTreeNode*
|
||||
getChildPointer(int branch);
|
||||
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
getChild(int branch);
|
||||
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
canonicalizeChild(int branch, intr_ptr::SharedPtr<SHAMapTreeNode> node);
|
||||
SHAMapTreeNodePtr
|
||||
canonicalizeChild(int branch, SHAMapTreeNodePtr node);
|
||||
|
||||
// sync functions
|
||||
bool
|
||||
@@ -161,10 +161,10 @@ public:
|
||||
void
|
||||
invariants(bool isRoot = false) const override;
|
||||
|
||||
static intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
static SHAMapTreeNodePtr
|
||||
makeFullInner(Slice data, SHAMapHash const& hash, bool hashValid);
|
||||
|
||||
static intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
static SHAMapTreeNodePtr
|
||||
makeCompressedInner(Slice data);
|
||||
};
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class SHAMapTreeNode;
|
||||
using SHAMapTreeNodePtr = intr_ptr::SharedPtr<SHAMapTreeNode>;
|
||||
|
||||
// These are wire-protocol identifiers used during serialization to encode the
|
||||
// type of a node. They should not be arbitrarily be changed.
|
||||
static constexpr unsigned char const kWireTypeTransaction = 0;
|
||||
@@ -112,7 +115,7 @@ public:
|
||||
}
|
||||
|
||||
/** Make a copy of this node, setting the owner. */
|
||||
virtual intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
virtual SHAMapTreeNodePtr
|
||||
clone(std::uint32_t cowid) const = 0;
|
||||
/** @} */
|
||||
|
||||
@@ -153,20 +156,20 @@ public:
|
||||
virtual void
|
||||
invariants(bool isRoot = false) const = 0;
|
||||
|
||||
static intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
static SHAMapTreeNodePtr
|
||||
makeFromPrefix(Slice rawNode, SHAMapHash const& hash);
|
||||
|
||||
static intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
static SHAMapTreeNodePtr
|
||||
makeFromWire(Slice rawNode);
|
||||
|
||||
private:
|
||||
static intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
static SHAMapTreeNodePtr
|
||||
makeTransaction(Slice data, SHAMapHash const& hash, bool hashValid);
|
||||
|
||||
static intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
static SHAMapTreeNodePtr
|
||||
makeAccountState(Slice data, SHAMapHash const& hash, bool hashValid);
|
||||
|
||||
static intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
static SHAMapTreeNodePtr
|
||||
makeTransactionWithMeta(Slice data, SHAMapHash const& hash, bool hashValid);
|
||||
};
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
clone(std::uint32_t cowid) const final
|
||||
{
|
||||
return intr_ptr::makeShared<SHAMapTxLeafNode>(item_, cowid, hash_);
|
||||
|
||||
@@ -27,7 +27,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
clone(std::uint32_t cowid) const override
|
||||
{
|
||||
return intr_ptr::makeShared<SHAMapTxPlusMetaLeafNode>(item_, cowid, hash_);
|
||||
|
||||
@@ -11,5 +11,5 @@ using TreeNodeCache = TaggedCache<
|
||||
SHAMapTreeNode,
|
||||
/*IsKeyCache*/ false,
|
||||
intr_ptr::SharedWeakUnionPtr<SHAMapTreeNode>,
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>>;
|
||||
SHAMapTreeNodePtr>;
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -148,7 +148,7 @@ public:
|
||||
/** Get the number of elements in each array and a pointer to the start
|
||||
of each array.
|
||||
*/
|
||||
[[nodiscard]] std::tuple<std::uint8_t, SHAMapHash*, intr_ptr::SharedPtr<SHAMapTreeNode>*>
|
||||
[[nodiscard]] std::tuple<std::uint8_t, SHAMapHash*, SHAMapTreeNodePtr*>
|
||||
getHashesAndChildren() const;
|
||||
|
||||
/** Get the `hashes` array */
|
||||
@@ -156,7 +156,7 @@ public:
|
||||
getHashes() const;
|
||||
|
||||
/** Get the `children` array */
|
||||
[[nodiscard]] intr_ptr::SharedPtr<SHAMapTreeNode>*
|
||||
[[nodiscard]] SHAMapTreeNodePtr*
|
||||
getChildren() const;
|
||||
|
||||
/** Call the `f` callback for all 16 (branchFactor) branches - even if
|
||||
|
||||
@@ -26,8 +26,7 @@ static_assert(
|
||||
// Terminology: A chunk is the memory being allocated from a block. A block
|
||||
// contains multiple chunks. This is the terminology the boost documentation
|
||||
// uses. Pools use "Simple Segregated Storage" as their storage format.
|
||||
constexpr size_t kElementSizeBytes =
|
||||
(sizeof(SHAMapHash) + sizeof(intr_ptr::SharedPtr<SHAMapTreeNode>));
|
||||
constexpr size_t kElementSizeBytes = sizeof(SHAMapHash) + sizeof(SHAMapTreeNodePtr);
|
||||
|
||||
constexpr size_t kBlockSizeBytes = kilobytes(512);
|
||||
|
||||
@@ -364,8 +363,7 @@ inline TaggedPointer::TaggedPointer(
|
||||
// keep
|
||||
new (&dstHashes[dstIndex]) SHAMapHash{srcHashes[srcIndex]};
|
||||
|
||||
new (&dstChildren[dstIndex])
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>{std::move(srcChildren[srcIndex])};
|
||||
new (&dstChildren[dstIndex]) SHAMapTreeNodePtr{std::move(srcChildren[srcIndex])};
|
||||
++dstIndex;
|
||||
++srcIndex;
|
||||
}
|
||||
@@ -376,7 +374,7 @@ inline TaggedPointer::TaggedPointer(
|
||||
if (dstIsDense)
|
||||
{
|
||||
new (&dstHashes[dstIndex]) SHAMapHash{};
|
||||
new (&dstChildren[dstIndex]) intr_ptr::SharedPtr<SHAMapTreeNode>{};
|
||||
new (&dstChildren[dstIndex]) SHAMapTreeNodePtr{};
|
||||
++dstIndex;
|
||||
}
|
||||
}
|
||||
@@ -384,7 +382,7 @@ inline TaggedPointer::TaggedPointer(
|
||||
{
|
||||
// add
|
||||
new (&dstHashes[dstIndex]) SHAMapHash{};
|
||||
new (&dstChildren[dstIndex]) intr_ptr::SharedPtr<SHAMapTreeNode>{};
|
||||
new (&dstChildren[dstIndex]) SHAMapTreeNodePtr{};
|
||||
++dstIndex;
|
||||
if (srcIsDense)
|
||||
{
|
||||
@@ -397,7 +395,7 @@ inline TaggedPointer::TaggedPointer(
|
||||
if (dstIsDense)
|
||||
{
|
||||
new (&dstHashes[dstIndex]) SHAMapHash{};
|
||||
new (&dstChildren[dstIndex]) intr_ptr::SharedPtr<SHAMapTreeNode>{};
|
||||
new (&dstChildren[dstIndex]) SHAMapTreeNodePtr{};
|
||||
++dstIndex;
|
||||
}
|
||||
if (srcIsDense)
|
||||
@@ -414,7 +412,7 @@ inline TaggedPointer::TaggedPointer(
|
||||
for (int i = dstIndex; i < dstNumAllocated; ++i)
|
||||
{
|
||||
new (&dstHashes[i]) SHAMapHash{};
|
||||
new (&dstChildren[i]) intr_ptr::SharedPtr<SHAMapTreeNode>{};
|
||||
new (&dstChildren[i]) SHAMapTreeNodePtr{};
|
||||
}
|
||||
*this = std::move(dst);
|
||||
}
|
||||
@@ -433,8 +431,10 @@ inline TaggedPointer::TaggedPointer(
|
||||
|
||||
// allocate hashes and children, but do not run constructors
|
||||
TaggedPointer newHashesAndChildren{RawAllocateTag{}, toAllocate};
|
||||
SHAMapHash *newHashes = nullptr, *oldHashes = nullptr;
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>*newChildren = nullptr, *oldChildren = nullptr;
|
||||
SHAMapHash* newHashes = nullptr;
|
||||
SHAMapHash* oldHashes = nullptr;
|
||||
SHAMapTreeNodePtr* newChildren = nullptr;
|
||||
SHAMapTreeNodePtr* oldChildren = nullptr;
|
||||
std::uint8_t newNumAllocated = 0;
|
||||
// structured bindings can't be captured in c++ 17; use tie instead
|
||||
std::tie(newNumAllocated, newHashes, newChildren) = newHashesAndChildren.getHashesAndChildren();
|
||||
@@ -445,8 +445,7 @@ inline TaggedPointer::TaggedPointer(
|
||||
// new arrays are dense, old arrays are sparse
|
||||
iterNonEmptyChildIndexes(isBranch, [&](auto branchNum, auto indexNum) {
|
||||
new (&newHashes[branchNum]) SHAMapHash{oldHashes[indexNum]};
|
||||
new (&newChildren[branchNum])
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>{std::move(oldChildren[indexNum])};
|
||||
new (&newChildren[branchNum]) SHAMapTreeNodePtr{std::move(oldChildren[indexNum])};
|
||||
});
|
||||
// Run the constructors for the remaining elements
|
||||
for (int i = 0; i < SHAMapInnerNode::kBranchFactor; ++i)
|
||||
@@ -454,7 +453,7 @@ inline TaggedPointer::TaggedPointer(
|
||||
if (((1 << i) & isBranch) != 0)
|
||||
continue;
|
||||
new (&newHashes[i]) SHAMapHash{};
|
||||
new (&newChildren[i]) intr_ptr::SharedPtr<SHAMapTreeNode>{};
|
||||
new (&newChildren[i]) SHAMapTreeNodePtr{};
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -464,14 +463,14 @@ inline TaggedPointer::TaggedPointer(
|
||||
iterNonEmptyChildIndexes(isBranch, [&](auto branchNum, auto indexNum) {
|
||||
new (&newHashes[curCompressedIndex]) SHAMapHash{oldHashes[indexNum]};
|
||||
new (&newChildren[curCompressedIndex])
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>{std::move(oldChildren[indexNum])};
|
||||
SHAMapTreeNodePtr{std::move(oldChildren[indexNum])};
|
||||
++curCompressedIndex;
|
||||
});
|
||||
// Run the constructors for the remaining elements
|
||||
for (int i = curCompressedIndex; i < newNumAllocated; ++i)
|
||||
{
|
||||
new (&newHashes[i]) SHAMapHash{};
|
||||
new (&newChildren[i]) intr_ptr::SharedPtr<SHAMapTreeNode>{};
|
||||
new (&newChildren[i]) SHAMapTreeNodePtr{};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -485,7 +484,7 @@ inline TaggedPointer::TaggedPointer(std::uint8_t numChildren)
|
||||
for (std::size_t i = 0; i < numAllocated; ++i)
|
||||
{
|
||||
new (&hashes[i]) SHAMapHash{};
|
||||
new (&children[i]) intr_ptr::SharedPtr<SHAMapTreeNode>{};
|
||||
new (&children[i]) SHAMapTreeNodePtr{};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -523,14 +522,13 @@ TaggedPointer::isDense() const
|
||||
return (tp_ & kTagMask) == kBoundaries.size() - 1;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline std::tuple<std::uint8_t, SHAMapHash*, intr_ptr::SharedPtr<SHAMapTreeNode>*>
|
||||
[[nodiscard]] inline std::tuple<std::uint8_t, SHAMapHash*, SHAMapTreeNodePtr*>
|
||||
TaggedPointer::getHashesAndChildren() const
|
||||
{
|
||||
auto const [tag, ptr] = decode();
|
||||
auto const hashes = reinterpret_cast<SHAMapHash*>(ptr);
|
||||
std::uint8_t const numAllocated = kBoundaries[tag];
|
||||
auto const children =
|
||||
reinterpret_cast<intr_ptr::SharedPtr<SHAMapTreeNode>*>(hashes + numAllocated);
|
||||
auto const children = reinterpret_cast<SHAMapTreeNodePtr*>(hashes + numAllocated);
|
||||
return {numAllocated, hashes, children};
|
||||
};
|
||||
|
||||
@@ -540,7 +538,7 @@ TaggedPointer::getHashes() const
|
||||
return reinterpret_cast<SHAMapHash*>(tp_ & kPtrMask);
|
||||
};
|
||||
|
||||
[[nodiscard]] inline intr_ptr::SharedPtr<SHAMapTreeNode>*
|
||||
[[nodiscard]] inline SHAMapTreeNodePtr*
|
||||
TaggedPointer::getChildren() const
|
||||
{
|
||||
auto [unused1, unused2, result] = getHashesAndChildren();
|
||||
|
||||
@@ -77,12 +77,8 @@ IOUAmount::normalize()
|
||||
|
||||
if (getSTNumberSwitchover())
|
||||
{
|
||||
// Normalize the raw mantissa/exponent straight to the IOU range in a
|
||||
// single pass. Previously this built a Number (one pass to the default
|
||||
// range) and then re-normalized to the IOU range via fromNumber (a
|
||||
// second pass); the static primitive collapses both into one.
|
||||
std::tie(mantissa_, exponent_) =
|
||||
Number::normalizeToRange<kMinMantissa, kMaxMantissa>(mantissa_, exponent_);
|
||||
Number const v{mantissa_, exponent_};
|
||||
*this = fromNumber(v);
|
||||
if (exponent_ > kMaxExponent)
|
||||
Throw<std::overflow_error>("value overflow");
|
||||
if (exponent_ < kMinExponent)
|
||||
|
||||
@@ -97,10 +97,7 @@ SHAMap::snapShot(bool isMutable) const
|
||||
}
|
||||
|
||||
void
|
||||
SHAMap::dirtyUp(
|
||||
SharedPtrNodeStack& stack,
|
||||
uint256 const& target,
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode> child)
|
||||
SHAMap::dirtyUp(SharedPtrNodeStack& stack, uint256 const& target, SHAMapTreeNodePtr child)
|
||||
{
|
||||
// walk the tree up from through the inner nodes to the root_
|
||||
// update hashes and links
|
||||
@@ -165,7 +162,7 @@ SHAMap::findKey(uint256 const& id) const
|
||||
return leaf;
|
||||
}
|
||||
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
SHAMap::fetchNodeFromDB(SHAMapHash const& hash) const
|
||||
{
|
||||
XRPL_ASSERT(backed_, "xrpl::SHAMap::fetchNodeFromDB : is backed");
|
||||
@@ -173,7 +170,7 @@ SHAMap::fetchNodeFromDB(SHAMapHash const& hash) const
|
||||
return finishFetch(hash, obj);
|
||||
}
|
||||
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
SHAMap::finishFetch(SHAMapHash const& hash, std::shared_ptr<NodeObject> const& object) const
|
||||
{
|
||||
XRPL_ASSERT(backed_, "xrpl::SHAMap::finishFetch : is backed");
|
||||
@@ -208,7 +205,7 @@ SHAMap::finishFetch(SHAMapHash const& hash, std::shared_ptr<NodeObject> const& o
|
||||
}
|
||||
|
||||
// See if a sync filter has a node
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
SHAMap::checkFilter(SHAMapHash const& hash, SHAMapSyncFilter* filter) const
|
||||
{
|
||||
if (auto nodeData = filter->getNode(hash))
|
||||
@@ -234,7 +231,7 @@ SHAMap::checkFilter(SHAMapHash const& hash, SHAMapSyncFilter* filter) const
|
||||
|
||||
// Get a node without throwing
|
||||
// Used on maps where missing nodes are expected
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
SHAMap::fetchNodeNT(SHAMapHash const& hash, SHAMapSyncFilter* filter) const
|
||||
{
|
||||
auto node = cacheLookup(hash);
|
||||
@@ -257,7 +254,7 @@ SHAMap::fetchNodeNT(SHAMapHash const& hash, SHAMapSyncFilter* filter) const
|
||||
return node;
|
||||
}
|
||||
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
SHAMap::fetchNodeNT(SHAMapHash const& hash) const
|
||||
{
|
||||
auto node = cacheLookup(hash);
|
||||
@@ -269,7 +266,7 @@ SHAMap::fetchNodeNT(SHAMapHash const& hash) const
|
||||
}
|
||||
|
||||
// Throw if the node is missing
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
SHAMap::fetchNode(SHAMapHash const& hash) const
|
||||
{
|
||||
auto node = fetchNodeNT(hash);
|
||||
@@ -291,10 +288,10 @@ SHAMap::descendThrow(SHAMapInnerNode* parent, int branch) const
|
||||
return ret;
|
||||
}
|
||||
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
SHAMap::descendThrow(SHAMapInnerNode& parent, int branch) const
|
||||
{
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode> ret = descend(parent, branch);
|
||||
SHAMapTreeNodePtr ret = descend(parent, branch);
|
||||
|
||||
if (!ret && !parent.isEmptyBranch(branch))
|
||||
Throw<SHAMapMissingNode>(type_, parent.getChildHash(branch));
|
||||
@@ -309,7 +306,7 @@ SHAMap::descend(SHAMapInnerNode* parent, int branch) const
|
||||
if ((ret != nullptr) || !backed_)
|
||||
return ret;
|
||||
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode> node = fetchNodeNT(parent->getChildHash(branch));
|
||||
SHAMapTreeNodePtr node = fetchNodeNT(parent->getChildHash(branch));
|
||||
if (!node)
|
||||
return nullptr;
|
||||
|
||||
@@ -317,10 +314,10 @@ SHAMap::descend(SHAMapInnerNode* parent, int branch) const
|
||||
return node.get();
|
||||
}
|
||||
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
SHAMap::descend(SHAMapInnerNode& parent, int branch) const
|
||||
{
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode> node = parent.getChild(branch);
|
||||
SHAMapTreeNodePtr node = parent.getChild(branch);
|
||||
if (node || !backed_)
|
||||
return node;
|
||||
|
||||
@@ -334,10 +331,10 @@ SHAMap::descend(SHAMapInnerNode& parent, int branch) const
|
||||
|
||||
// Gets the node that would be hooked to this branch,
|
||||
// but doesn't hook it up.
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
SHAMap::descendNoStore(SHAMapInnerNode& parent, int branch) const
|
||||
{
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode> ret = parent.getChild(branch);
|
||||
SHAMapTreeNodePtr ret = parent.getChild(branch);
|
||||
if (!ret && backed_)
|
||||
ret = fetchNode(parent.getChildHash(branch));
|
||||
return ret;
|
||||
@@ -361,7 +358,7 @@ SHAMap::descend(
|
||||
if (child == nullptr)
|
||||
{
|
||||
auto const& childHash = parent->getChildHash(branch);
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode> childNode = fetchNodeNT(childHash, filter);
|
||||
SHAMapTreeNodePtr childNode = fetchNodeNT(childHash, filter);
|
||||
|
||||
if (childNode)
|
||||
{
|
||||
@@ -434,7 +431,7 @@ SHAMap::unshareNode(intr_ptr::SharedPtr<Node> node, SHAMapNodeID const& nodeID)
|
||||
|
||||
SHAMapLeafNode*
|
||||
SHAMap::belowHelper(
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode> node,
|
||||
SHAMapTreeNodePtr node,
|
||||
SharedPtrNodeStack& stack,
|
||||
int branch,
|
||||
std::tuple<int, std::function<bool(int)>, std::function<void(int&)>> const& loopParams) const
|
||||
@@ -479,8 +476,7 @@ SHAMap::belowHelper(
|
||||
return nullptr;
|
||||
}
|
||||
SHAMapLeafNode*
|
||||
SHAMap::lastBelow(intr_ptr::SharedPtr<SHAMapTreeNode> node, SharedPtrNodeStack& stack, int branch)
|
||||
const
|
||||
SHAMap::lastBelow(SHAMapTreeNodePtr node, SharedPtrNodeStack& stack, int branch) const
|
||||
{
|
||||
auto init = kBranchFactor - 1;
|
||||
auto cmp = [](int i) { return i >= 0; };
|
||||
@@ -489,8 +485,7 @@ SHAMap::lastBelow(intr_ptr::SharedPtr<SHAMapTreeNode> node, SharedPtrNodeStack&
|
||||
return belowHelper(node, stack, branch, {init, cmp, incr});
|
||||
}
|
||||
SHAMapLeafNode*
|
||||
SHAMap::firstBelow(intr_ptr::SharedPtr<SHAMapTreeNode> node, SharedPtrNodeStack& stack, int branch)
|
||||
const
|
||||
SHAMap::firstBelow(SHAMapTreeNodePtr node, SharedPtrNodeStack& stack, int branch) const
|
||||
{
|
||||
auto init = 0;
|
||||
auto cmp = [](int i) { return i <= kBranchFactor; };
|
||||
@@ -699,10 +694,8 @@ SHAMap::delItem(uint256 const& id)
|
||||
|
||||
SHAMapNodeType const type = leaf->getType();
|
||||
|
||||
using TreeNodeType = intr_ptr::SharedPtr<SHAMapTreeNode>;
|
||||
|
||||
// What gets attached to the end of the chain (For now, nothing, since we deleted the leaf)
|
||||
TreeNodeType prevNode;
|
||||
SHAMapTreeNodePtr prevNode;
|
||||
|
||||
while (!stack.empty())
|
||||
{
|
||||
@@ -728,7 +721,7 @@ SHAMap::delItem(uint256 const& id)
|
||||
// no children below this branch
|
||||
//
|
||||
// Note: This is unnecessary due to the std::move above but left here for safety
|
||||
prevNode = TreeNodeType{};
|
||||
prevNode = SHAMapTreeNodePtr{};
|
||||
}
|
||||
else if (bc == 1)
|
||||
{
|
||||
@@ -741,7 +734,7 @@ SHAMap::delItem(uint256 const& id)
|
||||
{
|
||||
if (!node->isEmptyBranch(i))
|
||||
{
|
||||
node->setChild(i, TreeNodeType{});
|
||||
node->setChild(i, SHAMapTreeNodePtr{});
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -937,8 +930,8 @@ SHAMap::fetchRoot(SHAMapHash const& hash, SHAMapSyncFilter* filter)
|
||||
@note The node must have already been unshared by having the caller
|
||||
first call SHAMapTreeNode::unshare().
|
||||
*/
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMap::writeNode(NodeObjectType t, intr_ptr::SharedPtr<SHAMapTreeNode> node) const
|
||||
SHAMapTreeNodePtr
|
||||
SHAMap::writeNode(NodeObjectType t, SHAMapTreeNodePtr node) const
|
||||
{
|
||||
XRPL_ASSERT(node->cowid() == 0, "xrpl::SHAMap::writeNode : valid input node");
|
||||
XRPL_ASSERT(backed_, "xrpl::SHAMap::writeNode : is backed");
|
||||
@@ -1155,7 +1148,7 @@ SHAMap::dump(bool hash) const
|
||||
JLOG(journal_.info()) << leafCount << " resident leaves";
|
||||
}
|
||||
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
SHAMap::cacheLookup(SHAMapHash const& hash) const
|
||||
{
|
||||
auto ret = f_.getTreeNodeCache()->fetch(hash.asUInt256());
|
||||
@@ -1164,7 +1157,7 @@ SHAMap::cacheLookup(SHAMapHash const& hash) const
|
||||
}
|
||||
|
||||
void
|
||||
SHAMap::canonicalize(SHAMapHash const& hash, intr_ptr::SharedPtr<SHAMapTreeNode>& node) const
|
||||
SHAMap::canonicalize(SHAMapHash const& hash, SHAMapTreeNodePtr& node) const
|
||||
{
|
||||
XRPL_ASSERT(backed_, "xrpl::SHAMap::canonicalize : is backed");
|
||||
XRPL_ASSERT(node->cowid() == 0, "xrpl::SHAMap::canonicalize : valid node input");
|
||||
|
||||
@@ -261,7 +261,7 @@ SHAMap::walkMap(std::vector<SHAMapMissingNode>& missingNodes, int maxMissing) co
|
||||
{
|
||||
if (!node->isEmptyBranch(i))
|
||||
{
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode> const nextNode = descendNoStore(*node, i);
|
||||
SHAMapTreeNodePtr const nextNode = descendNoStore(*node, i);
|
||||
|
||||
if (nextNode)
|
||||
{
|
||||
@@ -286,7 +286,7 @@ SHAMap::walkMapParallel(std::vector<SHAMapMissingNode>& missingNodes, int maxMis
|
||||
return false;
|
||||
|
||||
using StackEntry = intr_ptr::SharedPtr<SHAMapInnerNode>;
|
||||
std::array<intr_ptr::SharedPtr<SHAMapTreeNode>, 16> topChildren;
|
||||
std::array<SHAMapTreeNodePtr, 16> topChildren;
|
||||
{
|
||||
auto const& innerRoot = intr_ptr::staticPointerCast<SHAMapInnerNode>(root_);
|
||||
for (int i = 0; i < 16; ++i)
|
||||
@@ -331,8 +331,7 @@ SHAMap::walkMapParallel(std::vector<SHAMapMissingNode>& missingNodes, int maxMis
|
||||
{
|
||||
if (node->isEmptyBranch(i))
|
||||
continue;
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode> const nextNode =
|
||||
descendNoStore(*node, i);
|
||||
SHAMapTreeNodePtr const nextNode = descendNoStore(*node, i);
|
||||
|
||||
if (nextNode)
|
||||
{
|
||||
|
||||
@@ -37,7 +37,7 @@ SHAMapInnerNode::~SHAMapInnerNode() = default;
|
||||
void
|
||||
SHAMapInnerNode::partialDestructor()
|
||||
{
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>* children = nullptr;
|
||||
SHAMapTreeNodePtr* children = nullptr;
|
||||
// structured bindings can't be captured in c++ 17; use tie instead
|
||||
std::tie(std::ignore, std::ignore, children) = hashesAndChildren_.getHashesAndChildren();
|
||||
iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) { children[indexNum].reset(); });
|
||||
@@ -69,7 +69,7 @@ SHAMapInnerNode::getChildIndex(int i) const
|
||||
return hashesAndChildren_.getChildIndex(isBranch_, i);
|
||||
}
|
||||
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
SHAMapInnerNode::clone(std::uint32_t cowid) const
|
||||
{
|
||||
auto const branchCount = getBranchCount();
|
||||
@@ -78,8 +78,10 @@ SHAMapInnerNode::clone(std::uint32_t cowid) const
|
||||
p->hash_ = hash_;
|
||||
p->isBranch_ = isBranch_;
|
||||
p->fullBelowGen_ = fullBelowGen_;
|
||||
SHAMapHash *cloneHashes = nullptr, *thisHashes = nullptr;
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>*cloneChildren = nullptr, *thisChildren = nullptr;
|
||||
SHAMapHash* cloneHashes = nullptr;
|
||||
SHAMapHash* thisHashes = nullptr;
|
||||
SHAMapTreeNodePtr* cloneChildren = nullptr;
|
||||
SHAMapTreeNodePtr* thisChildren = nullptr;
|
||||
// structured bindings can't be captured in c++ 17; use tie instead
|
||||
std::tie(std::ignore, cloneHashes, cloneChildren) =
|
||||
p->hashesAndChildren_.getHashesAndChildren();
|
||||
@@ -118,7 +120,7 @@ SHAMapInnerNode::clone(std::uint32_t cowid) const
|
||||
return p;
|
||||
}
|
||||
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
SHAMapInnerNode::makeFullInner(Slice data, SHAMapHash const& hash, bool hashValid)
|
||||
{
|
||||
// A full inner node is serialized as 16 256-bit hashes, back to back:
|
||||
@@ -153,7 +155,7 @@ SHAMapInnerNode::makeFullInner(Slice data, SHAMapHash const& hash, bool hashVali
|
||||
return ret;
|
||||
}
|
||||
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
SHAMapInnerNode::makeCompressedInner(Slice data)
|
||||
{
|
||||
// A compressed inner node is serialized as a series of 33 byte chunks,
|
||||
@@ -207,7 +209,7 @@ void
|
||||
SHAMapInnerNode::updateHashDeep()
|
||||
{
|
||||
SHAMapHash* hashes = nullptr;
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>* children = nullptr;
|
||||
SHAMapTreeNodePtr* children = nullptr;
|
||||
// structured bindings can't be captured in c++ 17; use tie instead
|
||||
std::tie(std::ignore, hashes, children) = hashesAndChildren_.getHashesAndChildren();
|
||||
iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
|
||||
@@ -265,7 +267,7 @@ SHAMapInnerNode::getString(SHAMapNodeID const& id) const
|
||||
|
||||
// We are modifying an inner node
|
||||
void
|
||||
SHAMapInnerNode::setChild(int m, intr_ptr::SharedPtr<SHAMapTreeNode> child)
|
||||
SHAMapInnerNode::setChild(int m, SHAMapTreeNodePtr child)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
(m >= 0) && (m < kBranchFactor), "xrpl::SHAMapInnerNode::setChild : valid branch input");
|
||||
@@ -307,7 +309,7 @@ SHAMapInnerNode::setChild(int m, intr_ptr::SharedPtr<SHAMapTreeNode> child)
|
||||
|
||||
// finished modifying, now make shareable
|
||||
void
|
||||
SHAMapInnerNode::shareChild(int m, intr_ptr::SharedPtr<SHAMapTreeNode> const& child)
|
||||
SHAMapInnerNode::shareChild(int m, SHAMapTreeNodePtr const& child)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
(m >= 0) && (m < kBranchFactor), "xrpl::SHAMapInnerNode::shareChild : valid branch input");
|
||||
@@ -337,7 +339,7 @@ SHAMapInnerNode::getChildPointer(int branch)
|
||||
return hashesAndChildren_.getChildren()[index].get();
|
||||
}
|
||||
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
SHAMapInnerNode::getChild(int branch)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
@@ -365,8 +367,8 @@ SHAMapInnerNode::getChildHash(int m) const
|
||||
return kZeroShaMapHash;
|
||||
}
|
||||
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapInnerNode::canonicalizeChild(int branch, intr_ptr::SharedPtr<SHAMapTreeNode> node)
|
||||
SHAMapTreeNodePtr
|
||||
SHAMapInnerNode::canonicalizeChild(int branch, SHAMapTreeNodePtr node)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
branch >= 0 && branch < kBranchFactor,
|
||||
|
||||
@@ -66,7 +66,7 @@ SHAMap::visitNodes(std::function<bool(SHAMapTreeNode&)> const& function) const
|
||||
{
|
||||
if (!node->isEmptyBranch(pos))
|
||||
{
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode> const child = descendNoStore(*node, pos);
|
||||
SHAMapTreeNodePtr const child = descendNoStore(*node, pos);
|
||||
if (!function(*child))
|
||||
return;
|
||||
|
||||
@@ -204,8 +204,7 @@ SHAMap::gmnProcessNodes(MissingNodes& mn, MissingNodes::StackEntry& se)
|
||||
branch,
|
||||
mn.filter,
|
||||
pending,
|
||||
[node, nodeID, branch, &mn](
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode> found, SHAMapHash const&) {
|
||||
[node, nodeID, branch, &mn](SHAMapTreeNodePtr found, SHAMapHash const&) {
|
||||
// a read completed asynchronously
|
||||
std::unique_lock<std::mutex> const lock{mn.deferLock};
|
||||
mn.finishedReads.emplace_back(node, nodeID, branch, std::move(found));
|
||||
@@ -266,8 +265,7 @@ SHAMap::gmnProcessDeferredReads(MissingNodes& mn)
|
||||
int complete = 0;
|
||||
while (complete != mn.deferred)
|
||||
{
|
||||
std::tuple<SHAMapInnerNode*, SHAMapNodeID, int, intr_ptr::SharedPtr<SHAMapTreeNode>>
|
||||
deferredNode;
|
||||
std::tuple<SHAMapInnerNode*, SHAMapNodeID, int, SHAMapTreeNodePtr> deferredNode;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock{mn.deferLock};
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
SHAMapTreeNode::makeTransaction(Slice data, SHAMapHash const& hash, bool hashValid)
|
||||
{
|
||||
if (data.size() < kMinShaMapItemBytes)
|
||||
@@ -43,7 +43,7 @@ SHAMapTreeNode::makeTransaction(Slice data, SHAMapHash const& hash, bool hashVal
|
||||
return intr_ptr::makeShared<SHAMapTxLeafNode>(std::move(item), 0);
|
||||
}
|
||||
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
SHAMapTreeNode::makeTransactionWithMeta(Slice data, SHAMapHash const& hash, bool hashValid)
|
||||
{
|
||||
Serializer s(data.data(), data.size());
|
||||
@@ -83,7 +83,7 @@ SHAMapTreeNode::makeTransactionWithMeta(Slice data, SHAMapHash const& hash, bool
|
||||
return intr_ptr::makeShared<SHAMapTxPlusMetaLeafNode>(std::move(item), 0);
|
||||
}
|
||||
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
SHAMapTreeNode::makeAccountState(Slice data, SHAMapHash const& hash, bool hashValid)
|
||||
{
|
||||
Serializer s(data.data(), data.size());
|
||||
@@ -124,7 +124,7 @@ SHAMapTreeNode::makeAccountState(Slice data, SHAMapHash const& hash, bool hashVa
|
||||
return intr_ptr::makeShared<SHAMapAccountStateLeafNode>(std::move(item), 0);
|
||||
}
|
||||
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
SHAMapTreeNode::makeFromWire(Slice rawNode)
|
||||
{
|
||||
if (rawNode.empty())
|
||||
@@ -155,7 +155,7 @@ SHAMapTreeNode::makeFromWire(Slice rawNode)
|
||||
Throw<std::runtime_error>("wire: Unknown type (" + std::to_string(type) + ")");
|
||||
}
|
||||
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>
|
||||
SHAMapTreeNodePtr
|
||||
SHAMapTreeNode::makeFromPrefix(Slice rawNode, SHAMapHash const& hash)
|
||||
{
|
||||
if (rawNode.size() < 4)
|
||||
|
||||
@@ -1,198 +0,0 @@
|
||||
#include <xrpl/basics/Number.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
|
||||
using namespace xrpl;
|
||||
|
||||
namespace {
|
||||
|
||||
// The IOUAmount mantissa range: [10^15, 10^16 - 1]. Kept here as signed
|
||||
// constants so the default template parameter T resolves to std::int64_t,
|
||||
// matching IOUAmount's own use of Number::normalizeToRange.
|
||||
constexpr std::int64_t kMin = 1'000'000'000'000'000;
|
||||
constexpr std::int64_t kMax = (kMin * 10) - 1;
|
||||
|
||||
// The two-pass path that the static primitive replaces: build a Number (one
|
||||
// normalize pass to the default range) and then re-normalize to the narrow IOU
|
||||
// range via the const member overload (a second pass).
|
||||
std::pair<std::int64_t, int>
|
||||
twoPass(std::int64_t mantissa, int exponent)
|
||||
{
|
||||
Number const v{mantissa, exponent};
|
||||
return v.normalizeToRange<kMin, kMax>();
|
||||
}
|
||||
|
||||
// The single-pass static primitive under test.
|
||||
std::pair<std::int64_t, int>
|
||||
onePass(std::int64_t mantissa, int exponent)
|
||||
{
|
||||
return Number::normalizeToRange<kMin, kMax>(mantissa, exponent);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// The static primitive must produce bit-identical (mantissa, exponent) to the
|
||||
// old two-pass path across a broad sweep of inputs: values needing scale-up,
|
||||
// scale-down, rounding cusps, negatives, and exponent extremes.
|
||||
TEST(Number, normalizeToRangeEquivalence)
|
||||
{
|
||||
// A spread of mantissa magnitudes: tiny (heavy scale-up), mid, at the IOU
|
||||
// floor/ceiling, beyond it (scale-down), and int64 extremes.
|
||||
std::int64_t const mantissas[] = {
|
||||
1,
|
||||
2,
|
||||
7,
|
||||
9,
|
||||
99,
|
||||
100,
|
||||
12345,
|
||||
999'999'999'999'999,
|
||||
kMin,
|
||||
kMin + 1,
|
||||
kMax,
|
||||
kMax + 1,
|
||||
1'234'567'890'123'456,
|
||||
12'345'678'901'234'567,
|
||||
std::numeric_limits<std::int64_t>::max(),
|
||||
std::numeric_limits<std::int64_t>::max() - 1,
|
||||
};
|
||||
|
||||
for (std::int64_t const absM : mantissas)
|
||||
{
|
||||
for (std::int64_t const m : {absM, -absM})
|
||||
{
|
||||
for (int const e : {-90, -32, -1, 0, 1, 5, 32, 70})
|
||||
{
|
||||
auto const expected = twoPass(m, e);
|
||||
auto const actual = onePass(m, e);
|
||||
EXPECT_EQ(actual.first, expected.first)
|
||||
<< "mantissa mismatch for m=" << m << " e=" << e;
|
||||
EXPECT_EQ(actual.second, expected.second)
|
||||
<< "exponent mismatch for m=" << m << " e=" << e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// int64::min cannot be negated naively; externalToInternal handles it. Make
|
||||
// sure the static path agrees with the two-pass path on it too.
|
||||
{
|
||||
std::int64_t const m = std::numeric_limits<std::int64_t>::min();
|
||||
auto const expected = twoPass(m, 0);
|
||||
auto const actual = onePass(m, 0);
|
||||
EXPECT_EQ(actual.first, expected.first);
|
||||
EXPECT_EQ(actual.second, expected.second);
|
||||
}
|
||||
}
|
||||
|
||||
// Exact, hand-computed results (state + cause), not just "equals the old path".
|
||||
TEST(Number, normalizeToRangeExactValues)
|
||||
{
|
||||
// A single digit scales up by 15 powers of ten to reach the floor 10^15,
|
||||
// with the exponent dropping by the same 15.
|
||||
{
|
||||
auto const [m, e] = onePass(1, 0);
|
||||
EXPECT_EQ(m, kMin); // 1'000'000'000'000'000
|
||||
EXPECT_EQ(e, -15);
|
||||
}
|
||||
// Already exactly at the floor: unchanged.
|
||||
{
|
||||
auto const [m, e] = onePass(kMin, 4);
|
||||
EXPECT_EQ(m, kMin);
|
||||
EXPECT_EQ(e, 4);
|
||||
}
|
||||
// Already exactly at the ceiling: unchanged.
|
||||
{
|
||||
auto const [m, e] = onePass(kMax, -7);
|
||||
EXPECT_EQ(m, kMax); // 9'999'999'999'999'999
|
||||
EXPECT_EQ(e, -7);
|
||||
}
|
||||
// One past the ceiling scales down by one power of ten; the dropped ones
|
||||
// digit (0) truncates cleanly and the exponent rises by one.
|
||||
{
|
||||
auto const [m, e] = onePass(kMax + 1, 0); // 10'000'000'000'000'000
|
||||
EXPECT_EQ(m, kMin); // 1'000'000'000'000'000
|
||||
EXPECT_EQ(e, 1);
|
||||
}
|
||||
// Negative values keep their sign through normalization.
|
||||
{
|
||||
auto const [m, e] = onePass(-5, 0);
|
||||
EXPECT_EQ(m, -5 * kMin); // -5'000'000'000'000'000
|
||||
EXPECT_EQ(e, -15);
|
||||
}
|
||||
// Zero mantissa: the workhorse leaves it as zero (callers special-case it).
|
||||
{
|
||||
auto const [m, e] = onePass(0, 0);
|
||||
EXPECT_EQ(m, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Equivalence must hold under every rounding mode, not just the default
|
||||
// ToNearest. This is the subtlest risk: the single-pass impl hardcodes
|
||||
// CuspRoundingFix::Disabled, whereas the old two-pass path ran an intermediate
|
||||
// normalize to the wider range first. Sweep all four modes, including inputs
|
||||
// that round at a tie (a trailing digit of exactly 5 when scaling down).
|
||||
TEST(Number, normalizeToRangeAllRoundingModes)
|
||||
{
|
||||
// Inputs chosen so scale-down drops a non-zero (and tie) trailing digit.
|
||||
std::int64_t const mantissas[] = {
|
||||
15,
|
||||
25,
|
||||
12'345'678'901'234'565, // 17 digits, trailing 5 -> tie on the drop
|
||||
99'999'999'999'999'995,
|
||||
kMax + 5,
|
||||
std::numeric_limits<std::int64_t>::max(),
|
||||
};
|
||||
|
||||
for (auto mode :
|
||||
{Number::RoundingMode::ToNearest,
|
||||
Number::RoundingMode::TowardsZero,
|
||||
Number::RoundingMode::Downward,
|
||||
Number::RoundingMode::Upward})
|
||||
{
|
||||
for (std::int64_t const absM : mantissas)
|
||||
{
|
||||
for (std::int64_t const m : {absM, -absM})
|
||||
{
|
||||
for (int const e : {-20, 0, 13})
|
||||
{
|
||||
NumberRoundModeGuard const g(mode);
|
||||
auto const expected = twoPass(m, e);
|
||||
auto const actual = onePass(m, e);
|
||||
EXPECT_EQ(actual.first, expected.first)
|
||||
<< "mantissa mismatch: mode=" << static_cast<int>(mode) << " m=" << m
|
||||
<< " e=" << e;
|
||||
EXPECT_EQ(actual.second, expected.second)
|
||||
<< "exponent mismatch: mode=" << static_cast<int>(mode) << " m=" << m
|
||||
<< " e=" << e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The refactored const member overload must forward to the static primitive
|
||||
// and yield identical results for the same Number.
|
||||
TEST(Number, normalizeToRangeMemberStaticConsistency)
|
||||
{
|
||||
std::int64_t const mantissas[] = {3, 42, kMin, kMin + 7, kMax, kMax + 1, 1'234'567'890'123'456};
|
||||
|
||||
for (std::int64_t const absM : mantissas)
|
||||
{
|
||||
for (std::int64_t const m : {absM, -absM})
|
||||
{
|
||||
for (int const e : {-50, -3, 0, 11, 60})
|
||||
{
|
||||
Number const v{m, e};
|
||||
auto const viaMember = v.normalizeToRange<kMin, kMax>();
|
||||
// Feed the static the raw inputs that built the Number.
|
||||
auto const viaStatic = Number::normalizeToRange<kMin, kMax>(m, e);
|
||||
EXPECT_EQ(viaMember.first, viaStatic.first) << "m=" << m << " e=" << e;
|
||||
EXPECT_EQ(viaMember.second, viaStatic.second) << "m=" << m << " e=" << e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
#include <xrpl/basics/Number.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
|
||||
using namespace xrpl;
|
||||
|
||||
namespace NumberNormalizeBenchNs {
|
||||
|
||||
constexpr std::int64_t kBenchMin = 1'000'000'000'000'000;
|
||||
constexpr std::int64_t kBenchMax = (kBenchMin * 10) - 1;
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
doNotOptimize(T const& val)
|
||||
{
|
||||
asm volatile("" : : "r,m"(val) : "memory");
|
||||
}
|
||||
|
||||
std::array<std::pair<std::int64_t, int>, 12> const kTestInputs = {{
|
||||
{1, 0},
|
||||
{7, 3},
|
||||
{12345, -2},
|
||||
{999'999'999, 0},
|
||||
{999'999'999'999, 5},
|
||||
{kBenchMin, 0},
|
||||
{kBenchMax, -3},
|
||||
{kBenchMax + 1, 0},
|
||||
{1'234'567'890'123'456, 0},
|
||||
{99'999'999'999'999'999, 0},
|
||||
{1'234'567'890'123'456'789, 0},
|
||||
{static_cast<std::int64_t>(9'000'000'000'000'000'000ull), 0},
|
||||
}};
|
||||
|
||||
inline std::pair<std::int64_t, int>
|
||||
twoPassNormalize(std::int64_t mantissa, int exponent)
|
||||
{
|
||||
Number const v{mantissa, exponent};
|
||||
return v.normalizeToRange<kBenchMin, kBenchMax>();
|
||||
}
|
||||
|
||||
inline std::pair<std::int64_t, int>
|
||||
singlePassNormalize(std::int64_t mantissa, int exponent)
|
||||
{
|
||||
return Number::normalizeToRange<kBenchMin, kBenchMax>(mantissa, exponent);
|
||||
}
|
||||
|
||||
constexpr int kWarmupIterations = 100'000;
|
||||
constexpr int kBenchIterations = 5'000'000;
|
||||
|
||||
} // namespace NumberNormalizeBenchNs
|
||||
|
||||
using namespace NumberNormalizeBenchNs;
|
||||
|
||||
TEST(NumberNormalizeBench, SinglePassVsTwoPassPerformance)
|
||||
{
|
||||
for (int i = 0; i < kWarmupIterations; ++i)
|
||||
{
|
||||
for (auto const& [m, e] : kTestInputs)
|
||||
{
|
||||
doNotOptimize(twoPassNormalize(m, e));
|
||||
doNotOptimize(singlePassNormalize(m, e));
|
||||
}
|
||||
}
|
||||
|
||||
auto const twoStart = std::chrono::steady_clock::now();
|
||||
for (int i = 0; i < kBenchIterations; ++i)
|
||||
{
|
||||
for (auto const& [m, e] : kTestInputs)
|
||||
{
|
||||
doNotOptimize(twoPassNormalize(m, e));
|
||||
}
|
||||
}
|
||||
auto const twoEnd = std::chrono::steady_clock::now();
|
||||
|
||||
auto const oneStart = std::chrono::steady_clock::now();
|
||||
for (int i = 0; i < kBenchIterations; ++i)
|
||||
{
|
||||
for (auto const& [m, e] : kTestInputs)
|
||||
{
|
||||
doNotOptimize(singlePassNormalize(m, e));
|
||||
}
|
||||
}
|
||||
auto const oneEnd = std::chrono::steady_clock::now();
|
||||
|
||||
auto const twoNs =
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(twoEnd - twoStart).count();
|
||||
auto const oneNs =
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(oneEnd - oneStart).count();
|
||||
|
||||
double const twoPerCall = static_cast<double>(twoNs) / (kBenchIterations * kTestInputs.size());
|
||||
double const onePerCall = static_cast<double>(oneNs) / (kBenchIterations * kTestInputs.size());
|
||||
double const speedup = twoPerCall / onePerCall;
|
||||
|
||||
std::cout << "\n=== Single-Pass vs Two-Pass Normalize ===\n";
|
||||
std::cout << "Iterations: " << kBenchIterations << " x " << kTestInputs.size()
|
||||
<< " inputs = " << (kBenchIterations * kTestInputs.size()) << " calls\n";
|
||||
std::cout << "Two-pass (old): " << twoPerCall << " ns/call (" << twoNs << " ns total)\n";
|
||||
std::cout << "Single-pass (new): " << onePerCall << " ns/call (" << oneNs << " ns total)\n";
|
||||
std::cout << "Speedup: " << speedup << "x\n";
|
||||
std::cout << "==========================================\n\n";
|
||||
|
||||
if (speedup > 1.0)
|
||||
{
|
||||
std::cout << "Single-pass is FASTER by " << ((speedup - 1.0) * 100.0) << "%\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Two-pass is faster by " << ((1.0 / speedup - 1.0) * 100.0) << "%\n";
|
||||
}
|
||||
}
|
||||
|
||||
TEST(NumberNormalizeBench, SingleVsTwoPassBreakdown)
|
||||
{
|
||||
struct InputCategory
|
||||
{
|
||||
char const* name;
|
||||
std::int64_t mantissa;
|
||||
int exponent;
|
||||
};
|
||||
|
||||
std::array<InputCategory, 6> const categories = {{
|
||||
{.name = "1 (far from range)", .mantissa = 1, .exponent = 0},
|
||||
{.name = "12345 (moderate)", .mantissa = 12345, .exponent = 0},
|
||||
{.name = "10^12 (close)", .mantissa = 1'000'000'000'000, .exponent = 0},
|
||||
{.name = "10^15 (in range)", .mantissa = kBenchMin, .exponent = 0},
|
||||
{.name = "10^16 (1 over)", .mantissa = kBenchMax + 1, .exponent = 0},
|
||||
{.name = "10^18 (far over)", .mantissa = 1'234'567'890'123'456'789, .exponent = 0},
|
||||
}};
|
||||
|
||||
constexpr int kIters = 10'000'000;
|
||||
|
||||
std::cout << "\n=== Single vs Two Pass: Per-Input Breakdown ===\n";
|
||||
std::cout << "Input | 2-pass ns | 1-pass ns | Speedup\n";
|
||||
std::cout << "-------------------------|-----------|-----------|--------\n";
|
||||
|
||||
for (auto const& cat : categories)
|
||||
{
|
||||
for (int i = 0; i < 100'000; ++i)
|
||||
{
|
||||
doNotOptimize(twoPassNormalize(cat.mantissa, cat.exponent));
|
||||
doNotOptimize(singlePassNormalize(cat.mantissa, cat.exponent));
|
||||
}
|
||||
|
||||
auto const ts = std::chrono::steady_clock::now();
|
||||
for (int i = 0; i < kIters; ++i)
|
||||
{
|
||||
doNotOptimize(twoPassNormalize(cat.mantissa, cat.exponent));
|
||||
}
|
||||
auto const te = std::chrono::steady_clock::now();
|
||||
|
||||
auto const os = std::chrono::steady_clock::now();
|
||||
for (int i = 0; i < kIters; ++i)
|
||||
{
|
||||
doNotOptimize(singlePassNormalize(cat.mantissa, cat.exponent));
|
||||
}
|
||||
auto const oe = std::chrono::steady_clock::now();
|
||||
|
||||
double const twoNs =
|
||||
static_cast<double>(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(te - ts).count()) /
|
||||
kIters;
|
||||
double const oneNs =
|
||||
static_cast<double>(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(oe - os).count()) /
|
||||
kIters;
|
||||
double const speedup = twoNs / oneNs;
|
||||
|
||||
printf("%-25s| %9.2f | %9.2f | %.2fx\n", cat.name, twoNs, oneNs, speedup);
|
||||
}
|
||||
std::cout << "================================================\n\n";
|
||||
}
|
||||
@@ -1088,7 +1088,7 @@ public:
|
||||
<< "; size after: " << cachedSLEs_.size();
|
||||
}
|
||||
|
||||
mallocTrim("doSweep", journal_);
|
||||
// mallocTrim("doSweep", journal_);
|
||||
|
||||
// Set timer to do another sweep later.
|
||||
setSweepTimer();
|
||||
|
||||
Reference in New Issue
Block a user