Merge remote-tracking branch 'origin/develop' into FN-36-credential_pins_pseudo_account

# Conflicts:
#	src/libxrpl/ledger/helpers/AMMHelpers.cpp
This commit is contained in:
Timur Ialymov
2026-08-18 18:04:41 +01:00
91 changed files with 2215 additions and 1240 deletions

View File

@@ -2,7 +2,6 @@
#include <xrpl/basics/Blob.h>
#include <boost/format.hpp>
#include <boost/utility/string_view.hpp>
#include <array>

View File

@@ -11,6 +11,7 @@
#include <cstddef>
#include <iterator>
#include <string>
#include <string_view>
#include <vector>
namespace beast::rfc2616 {
@@ -186,7 +187,7 @@ splitCommas(FwdIt first, FwdIt last)
template <class Result = std::vector<std::string>>
Result
splitCommas(boost::beast::string_view const& s)
splitCommas(std::string_view s)
{
return splitCommas(s.begin(), s.end());
}

View File

@@ -2,7 +2,6 @@
#include <xrpl/basics/contract.h>
#include <boost/beast/core/string.hpp>
#include <boost/lexical_cast.hpp>
#include <algorithm>

View File

@@ -1,20 +1,19 @@
#pragma once
#include <boost/beast/core/string.hpp>
#include <functional>
#include <string>
#include <string_view>
namespace json {
class Value;
using Output = std::function<void(boost::beast::string_view const&)>;
using Output = std::function<void(std::string_view)>;
inline Output
stringOutput(std::string& s)
{
return [&](boost::beast::string_view const& b) { s.append(b.data(), b.size()); };
return [&](std::string_view b) { s.append(b.data(), b.size()); };
}
/**

View File

@@ -78,6 +78,13 @@ isVaultPseudoAccountFrozen(
MPTIssue const& mptShare,
std::uint8_t depth);
[[nodiscard]] bool
isVaultPseudoAccountFrozen(
ReadView const& view,
AccountID const& account,
SLE const& issuanceSle,
std::uint8_t depth);
[[nodiscard]] bool
isLPTokenFrozen(
ReadView const& view,
@@ -85,6 +92,26 @@ isLPTokenFrozen(
Asset const& asset,
Asset const& asset2);
/**
* Check whether an AMM LPToken may be transferred between @p from and @p to.
*
* @p lpTokenIssuer is the issuer of the LPToken being moved. If it is not an
* AMM account the token is not an LPToken and the transfer is unconditionally
* permitted. Otherwise, for each MPT pool asset of that AMM, canTransfer() must
* permit the transfer (which exempts the MPT issuer). Non-MPT pool assets are
* always transferable by this check, so it is implicitly gated by
* featureMPTokensV2 (MPTs can only be AMM pool assets once V2 is enabled).
*
* @return tesSUCCESS if permitted, otherwise the canTransfer() failure code
* (e.g. tecNO_AUTH) of the first MPT pool asset that disallows it.
*/
[[nodiscard]] TER
canTransferLPToken(
ReadView const& view,
AccountID const& from,
AccountID const& to,
AccountID const& lpTokenIssuer);
// Return the list of enabled amendments
[[nodiscard]] std::set<uint256>
getEnabledAmendments(ReadView const& view);

View File

@@ -29,6 +29,9 @@ namespace xrpl {
[[nodiscard]] bool
isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue);
[[nodiscard]] bool
isGlobalFrozen(SLE const& issuanceSle);
/**
* Returns true if @p account's MPToken for @p mptIssue carries the
* individual-lock flag (lsfMPTLocked).
@@ -40,9 +43,29 @@ isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue);
* receive tokens — it combines isIndividualFrozen, isGlobalFrozen, and
* isVaultPseudoAccountFrozen into a single complete check.
*/
[[nodiscard]] bool
isIndividualFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue);
[[nodiscard]] bool
isIndividualFrozen(SLE const& mptSle);
/**
* Returns true if @p account cannot send or receive tokens of @p mptIssue
* because a freeze applies. This is the complete check callers should use
* before moving MPT value: it combines @ref isGlobalFrozen (issuance-level
* lock), @ref isIndividualFrozen (per-holder lock bit), and the transitive
* vault pseudo-account check (if @p mptIssue is a vault share, the underlying
* asset is checked, and so on recursively up to @c maxAssetCheckDepth).
*
* The @c SLE overload takes an already-loaded ltMPTOKEN or ltMPTOKEN_ISSUANCE
* ledger entry; for ltMPTOKEN it can skip the per-holder individual-lock lookup.
* @ref isAnyFrozen answers the same question for a set of accounts and returns true
* if the freeze applies to any of them.
*
* @param depth Current recursion depth for the vault-share walk. Callers
* outside this module should leave it at the default.
*/
[[nodiscard]] bool
isFrozen(
ReadView const& view,
@@ -50,6 +73,18 @@ isFrozen(
MPTIssue const& mptIssue,
std::uint8_t depth = 0);
/**
* SLE overload: pass an already-loaded ltMPTOKEN (holder row) or
* ltMPTOKEN_ISSUANCE to reuse it for the freeze checks and avoid re-reading
* the same object. For an ltMPTOKEN, @p sle is used directly for the
* individual-lock check and the issuance is read once for global-freeze and
* vault-pseudo-account. For an ltMPTOKEN_ISSUANCE, @p sle is used directly
* for global-freeze and vault-pseudo-account, and the caller's holder row is
* read for the individual-lock check.
*/
[[nodiscard]] bool
isFrozen(ReadView const& view, AccountID const& account, SLE const& sle, std::uint8_t depth = 0);
[[nodiscard]] bool
isAnyFrozen(
ReadView const& view,

View File

@@ -8,11 +8,11 @@
#include <boost/asio.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/format.hpp>
#include <openssl/err.h>
#include <openssl/tls1.h>
#include <format>
#include <stdexcept>
#include <string>
#include <type_traits>
@@ -38,8 +38,8 @@ public:
if (ec && sslVerifyDir.empty())
{
Throw<std::runtime_error>(boost::str(
boost::format("Failed to set_default_verify_paths: %s") % ec.message()));
Throw<std::runtime_error>(
std::format("Failed to set_default_verify_paths: {}", ec.message()));
}
}
else
@@ -54,7 +54,7 @@ public:
if (ec)
{
Throw<std::runtime_error>(
boost::str(boost::format("Failed to add verify path: %s") % ec.message()));
std::format("Failed to add verify path: {}", ec.message()));
}
}
}

View File

@@ -1,10 +1,10 @@
syntax = "proto2";
package protocol;
// Unused numbers in the list below may have been used previously. Please don't
// reassign them for reuse unless you are 100% certain that there won't be a
// conflict. Even if you're sure, it's probably best to assign a new type.
enum MessageType {
// Previously used - don't reuse.
reserved 0 to 1, 4, 6 to 14, 16 to 29, 36 to 40, 43 to 54, 61 to 62;
mtMANIFESTS = 2;
mtPING = 3;
mtCLUSTER = 5;
@@ -17,7 +17,6 @@ enum MessageType {
mtHAVE_SET = 35;
mtVALIDATION = 41;
mtGET_OBJECTS = 42;
mtVALIDATOR_LIST = 54;
mtSQUELCH = 55;
mtVALIDATOR_LIST_COLLECTION = 56;
mtPROOF_PATH_REQ = 57;
@@ -162,14 +161,6 @@ message TMHaveTransactionSet {
required bytes hash = 2;
}
// Validator list (UNL)
message TMValidatorList {
required bytes manifest = 1;
required bytes blob = 2;
required bytes signature = 3;
required uint32 version = 4;
}
// Validator List v2
message ValidatorBlobInfo {
optional bytes manifest = 1;

View File

@@ -2,6 +2,9 @@
#include <array>
#include <cstdint>
#include <format>
#include <string>
#include <string_view>
namespace xrpl {
@@ -9,9 +12,29 @@ namespace xrpl {
// These pragmas are built at startup and applied to all database
// connections, unless otherwise noted.
inline constexpr char const* kCommonDbPragmaJournal{"PRAGMA journal_mode=%s;"};
inline constexpr char const* kCommonDbPragmaSync{"PRAGMA synchronous=%s;"};
inline constexpr char const* kCommonDbPragmaTemp{"PRAGMA temp_store=%s;"};
//
// They are exposed as functions rather than as format-string constants so
// that the un-substituted template can never reach sqlite: an unrecognized
// pragma value is silently ignored, so forgetting to interpolate would
// leave the setting at its default instead of failing loudly.
[[nodiscard]] inline std::string
commonDbPragmaJournal(std::string_view journalMode)
{
return std::format("PRAGMA journal_mode={};", journalMode);
}
[[nodiscard]] inline std::string
commonDbPragmaSync(std::string_view synchronous)
{
return std::format("PRAGMA synchronous={};", synchronous);
}
[[nodiscard]] inline std::string
commonDbPragmaTemp(std::string_view tempStore)
{
return std::format("PRAGMA temp_store={};", tempStore);
}
// A warning will be logged if any lower-safety sqlite tuning settings
// are used and at least this much ledger history is configured. This
// includes full history nodes. This is because such a large amount of

View File

@@ -10,6 +10,10 @@
#include <xrpl/rdb/DatabaseCon.h>
#include <xrpl/server/Manifest.h>
// boost::optional (not std::optional) appears in the declarations below,
// because SOCI's into()/use() bindings only support boost::optional.
#include <boost/optional/optional.hpp>
#include <functional>
#include <memory>
#include <string>

View File

@@ -25,6 +25,7 @@
#include <iterator>
#include <list>
#include <memory>
#include <string_view>
#include <utility>
#include <vector>
@@ -62,8 +63,7 @@ private:
bool pingActive_ = false;
boost::beast::websocket::ping_data payload_;
error_code ec_;
std::function<void(boost::beast::websocket::frame_type, boost::beast::string_view)>
controlCallback_;
std::function<void(boost::beast::websocket::frame_type, std::string_view)> controlCallback_;
public:
template <class Body, class Headers>
@@ -151,7 +151,7 @@ protected:
onPing(error_code const& ec);
void
onPingPong(boost::beast::websocket::frame_type kind, boost::beast::string_view payload);
onPingPong(boost::beast::websocket::frame_type kind, std::string_view payload);
void
onTimer(error_code ec);
@@ -189,9 +189,9 @@ BaseWSPeer<Handler, Impl>::run()
impl().ws_.set_option(port().pmdOptions);
// Must manage the control callback memory outside of the `control_callback`
// function
controlCallback_ = [this](
boost::beast::websocket::frame_type kind,
boost::beast::string_view payload) { onPingPong(kind, payload); };
controlCallback_ = [this](boost::beast::websocket::frame_type kind, std::string_view payload) {
onPingPong(kind, payload);
};
impl().ws_.control_callback(controlCallback_);
startTimer();
closeOnTimer_ = true;
@@ -430,11 +430,11 @@ template <class Handler, class Impl>
void
BaseWSPeer<Handler, Impl>::onPingPong(
boost::beast::websocket::frame_type kind,
boost::beast::string_view payload)
std::string_view payload)
{
if (kind == boost::beast::websocket::frame_type::pong)
{
boost::beast::string_view const p(payload_.begin());
std::string_view const p(payload_.begin(), payload_.size());
if (payload == p)
{
closeOnTimer_ = false;

View File

@@ -484,31 +484,36 @@ private:
// returns the first item at or below this node
SHAMapLeafNode*
firstBelow(SHAMapTreeNodePtr node, SharedPtrNodeStack& stack, int branch = 0) const;
firstBelow(SHAMapTreeNodePtr node, SharedPtrNodeStack& stack, unsigned int branch = 0u) const;
// returns the last item at or below this node
SHAMapLeafNode*
lastBelow(SHAMapTreeNodePtr node, SharedPtrNodeStack& stack, int branch = kBranchFactor) const;
lastBelow(
SHAMapTreeNodePtr node,
SharedPtrNodeStack& stack,
unsigned int branch = kBranchFactor) const;
// direction in which belowHelper scans an inner node's branches
enum class BelowDirection { First, Last };
// helper function for firstBelow and lastBelow
SHAMapLeafNode*
belowHelper(
SHAMapTreeNodePtr node,
SharedPtrNodeStack& stack,
int branch,
std::tuple<int, std::function<bool(int)>, std::function<void(int&)>> const& loopParams)
const;
unsigned int branch,
BelowDirection direction) const;
// Simple descent
// Get a child of the specified node
SHAMapTreeNode*
descend(SHAMapInnerNode*, int branch) const;
descend(SHAMapInnerNode*, unsigned int branch) const;
SHAMapTreeNode*
descendThrow(SHAMapInnerNode*, int branch) const;
descendThrow(SHAMapInnerNode*, unsigned int branch) const;
SHAMapTreeNodePtr
descend(SHAMapInnerNode&, int branch) const;
descend(SHAMapInnerNode&, unsigned int branch) const;
SHAMapTreeNodePtr
descendThrow(SHAMapInnerNode&, int branch) const;
descendThrow(SHAMapInnerNode&, unsigned int branch) const;
// Descend with filter
// If pending, callback is called as if it called fetchNodeNT
@@ -516,7 +521,7 @@ private:
SHAMapTreeNode*
descendAsync(
SHAMapInnerNode* parent,
int branch,
unsigned int branch,
SHAMapSyncFilter const* filter,
bool& pending,
descendCallback&&) const;
@@ -525,13 +530,13 @@ private:
descend(
SHAMapInnerNode* parent,
SHAMapNodeID const& parentID,
int branch,
unsigned int branch,
SHAMapSyncFilter const* filter) const;
// Non-storing
// Does not hook the returned node to its parent
SHAMapTreeNodePtr
descendNoStore(SHAMapInnerNode&, int branch) const;
descendNoStore(SHAMapInnerNode&, unsigned int branch) const;
/**
* If there is only one leaf below this node, get its contents
@@ -581,8 +586,8 @@ private:
using StackEntry = std::tuple<
SHAMapInnerNode*, // pointer to the node
SHAMapNodeID, // the node's ID
int, // while child we check first
int, // which child we check next
unsigned int, // which child we check first
unsigned int, // which child we check next
bool>; // whether we've found any missing children yet
// We explicitly choose to specify the use of std::deque here, because
@@ -596,7 +601,7 @@ private:
using DeferredNode = std::tuple<
SHAMapInnerNode*, // parent node
SHAMapNodeID, // parent node ID
int, // branch
unsigned int, // branch
SHAMapTreeNodePtr>; // node
int deferred;

View File

@@ -62,8 +62,8 @@ private:
*
* @param i index of the requested child
*/
std::optional<int>
getChildIndex(int i) const;
std::optional<unsigned int>
getChildIndex(unsigned int i) const;
/**
* Call the `f` callback for all 16 (branchFactor) branches - even if
@@ -125,28 +125,28 @@ public:
isEmpty() const;
bool
isEmptyBranch(int m) const;
isEmptyBranch(unsigned int branch) const;
int
unsigned int
getBranchCount() const;
SHAMapHash const&
getChildHash(int m) const;
getChildHash(unsigned int branch) const;
void
setChild(int m, SHAMapTreeNodePtr child);
setChild(unsigned int branch, SHAMapTreeNodePtr child);
void
shareChild(int m, SHAMapTreeNodePtr const& child);
shareChild(unsigned int branch, SHAMapTreeNodePtr const& child);
SHAMapTreeNode*
getChildPointer(int branch);
getChildPointer(unsigned int branch);
SHAMapTreeNodePtr
getChild(int branch);
getChild(unsigned int branch);
SHAMapTreeNodePtr
canonicalizeChild(int branch, SHAMapTreeNodePtr node);
canonicalizeChild(unsigned int branch, SHAMapTreeNodePtr node);
// sync functions
bool
@@ -190,12 +190,12 @@ SHAMapInnerNode::isEmpty() const
}
inline bool
SHAMapInnerNode::isEmptyBranch(int m) const
SHAMapInnerNode::isEmptyBranch(unsigned int branch) const
{
return (isBranch_ & (1 << m)) == 0;
return (isBranch_ & (1u << branch)) == 0u;
}
inline int
inline unsigned int
SHAMapInnerNode::getBranchCount() const
{
return popcnt16(isBranch_);

View File

@@ -53,7 +53,21 @@ public:
}
[[nodiscard]] SHAMapNodeID
getChildNodeID(unsigned int m) const;
getChildNodeID(unsigned int branch) const;
/**
* Test whether this node ID lies on the path to the given leaf key
*
* A node at depth d identifies the tree path spelled by the first d
* nibbles of its key, so any leaf beneath it must agree on that prefix.
* A node ID that fails this test names a different subtree than the one
* it was built for.
*
* @param key the key of a leaf below this node
* @return whether this node ID is a prefix of the leaf key
*/
[[nodiscard]] bool
isPrefixOf(uint256 const& key) const;
/**
* Create a SHAMapNodeID of a node with the depth of the node and
@@ -64,7 +78,7 @@ public:
* @return SHAMapNodeID of the node
*/
static SHAMapNodeID
createID(int depth, uint256 const& key);
createID(unsigned int depth, uint256 const& key);
/**
* Comparison operators

View File

@@ -219,11 +219,11 @@ public:
*
* @param i index of the requested child
*/
[[nodiscard]] std::optional<int>
getChildIndex(std::uint16_t isBranch, int i) const;
[[nodiscard]] std::optional<unsigned int>
getChildIndex(std::uint16_t isBranch, unsigned int i) const;
};
[[nodiscard]] inline int
[[nodiscard]] inline unsigned int
popcnt16(std::uint16_t a)
{
#if __cpp_lib_bitops
@@ -234,11 +234,11 @@ popcnt16(std::uint16_t a)
// fallback to table lookup
static constexpr auto tbl = []() {
std::array<std::uint8_t, 256> ret{};
for (int i = 0; i != 256; ++i)
for (auto i = 0u; i != 256u; ++i)
{
for (int j = 0; j != 8; ++j)
for (auto j = 0u; j != 8u; ++j)
{
if (i & (1 << j))
if (i & (1u << j))
ret[i]++;
}
}

View File

@@ -22,6 +22,11 @@ static_assert(
static_assert(
kBoundaries.back() == SHAMapInnerNode::kBranchFactor,
"Last element of boundaries must be number of children in a dense array");
static_assert(
kBoundaries.front() >= 1,
"TaggedPointer.ipp subtracts 1 from a numAllocated value derived from "
"kBoundaries, as an unsigned quantity, in several places; the smallest "
"boundary must stay non-zero or those subtractions underflow.");
// Terminology: A chunk is the memory being allocated from a block. A block
// contains multiple chunks. This is the terminology the boost documentation
@@ -148,16 +153,16 @@ TaggedPointer::iterChildren(std::uint16_t isBranch, F&& f) const
if (numAllocated == SHAMapInnerNode::kBranchFactor)
{
// dense case
for (int i = 0; i < SHAMapInnerNode::kBranchFactor; ++i)
for (auto i = 0u; i < SHAMapInnerNode::kBranchFactor; ++i)
f(hashes[i]);
}
else
{
// sparse case
int curHashI = 0;
for (int i = 0; i < SHAMapInnerNode::kBranchFactor; ++i)
auto curHashI = 0u;
for (auto i = 0u; i < SHAMapInnerNode::kBranchFactor; ++i)
{
if ((1 << i) & isBranch)
if ((1u << i) & isBranch)
{
f(hashes[curHashI++]);
}
@@ -176,9 +181,9 @@ TaggedPointer::iterNonEmptyChildIndexes(std::uint16_t isBranch, F&& f) const
if (capacity() == SHAMapInnerNode::kBranchFactor)
{
// dense case
for (int i = 0; i < SHAMapInnerNode::kBranchFactor; ++i)
for (auto i = 0u; i < SHAMapInnerNode::kBranchFactor; ++i)
{
if ((1 << i) & isBranch)
if ((1u << i) & isBranch)
{
f(i, i);
}
@@ -187,10 +192,10 @@ TaggedPointer::iterNonEmptyChildIndexes(std::uint16_t isBranch, F&& f) const
else
{
// sparse case
int curHashI = 0;
for (int i = 0; i < SHAMapInnerNode::kBranchFactor; ++i)
auto curHashI = 0u;
for (auto i = 0u; i < SHAMapInnerNode::kBranchFactor; ++i)
{
if ((1 << i) & isBranch)
if ((1u << i) & isBranch)
{
f(i, curHashI++);
}
@@ -216,14 +221,14 @@ TaggedPointer::destroyHashesAndChildren()
deallocateArrays(tag, ptr);
}
inline std::optional<int>
TaggedPointer::getChildIndex(std::uint16_t isBranch, int i) const
inline std::optional<unsigned int>
TaggedPointer::getChildIndex(std::uint16_t isBranch, unsigned int i) const
{
if (isDense())
return i;
// Sparse case
if ((isBranch & (1 << i)) == 0)
if ((isBranch & (1u << i)) == 0u)
{
// Empty branch. Sparse children do not store empty branches
return {};
@@ -273,10 +278,10 @@ inline TaggedPointer::TaggedPointer(
*this = std::move(other);
auto [srcDstNumAllocated, srcDstHashes, srcDstChildren] = getHashesAndChildren();
bool const srcDstIsDense = isDense();
int srcDstIndex = 0;
for (int i = 0; i < SHAMapInnerNode::kBranchFactor; ++i)
auto srcDstIndex = 0u;
for (auto i = 0u; i < SHAMapInnerNode::kBranchFactor; ++i)
{
auto const mask = (1 << i);
auto const mask = (1u << i);
bool const inSrc = (srcBranches & mask) != 0;
bool const inDst = (dstBranches & mask) != 0;
if (inSrc && inDst)
@@ -298,13 +303,13 @@ inline TaggedPointer::TaggedPointer(
// sparse
// need to shift all the elements to the left by
// one
for (int c = srcDstIndex; c < srcDstNumAllocated - 1; ++c)
for (auto c = srcDstIndex; c + 1 < srcDstNumAllocated; ++c)
{
srcDstHashes[c] = srcDstHashes[c + 1];
srcDstChildren[c] = std::move(srcDstChildren[c + 1]);
}
srcDstHashes[srcDstNumAllocated - 1].zero();
srcDstChildren[srcDstNumAllocated - 1].reset();
srcDstHashes[srcDstNumAllocated - 1u].zero();
srcDstChildren[srcDstNumAllocated - 1u].reset();
// do not increment the index
}
}
@@ -321,7 +326,7 @@ inline TaggedPointer::TaggedPointer(
// sparse
// need to create a hole by shifting all the elements to the
// right by one
for (int c = srcDstNumAllocated - 1; c > srcDstIndex; --c)
for (auto c = srcDstNumAllocated - 1u; c > srcDstIndex; --c)
{
srcDstHashes[c] = srcDstHashes[c - 1];
srcDstChildren[c] = std::move(srcDstChildren[c - 1]);
@@ -352,10 +357,10 @@ inline TaggedPointer::TaggedPointer(
auto [srcNumAllocated, srcHashes, srcChildren] = src.getHashesAndChildren();
bool const srcIsDense = src.isDense();
bool const dstIsDense = dst.isDense();
int srcIndex = 0, dstIndex = 0;
for (int i = 0; i < SHAMapInnerNode::kBranchFactor; ++i)
auto srcIndex = 0u, dstIndex = 0u;
for (auto i = 0u; i < SHAMapInnerNode::kBranchFactor; ++i)
{
auto const mask = (1 << i);
auto const mask = (1u << i);
bool const inSrc = (srcBranches & mask) != 0;
bool const inDst = (dstBranches & mask) != 0;
if (inSrc && inDst)
@@ -409,7 +414,7 @@ inline TaggedPointer::TaggedPointer(
!dstIsDense || dstIndex == dstNumAllocated,
"xrpl::TaggedPointer::TaggedPointer(TaggedPointer&& ...) : "
"non-sparse or valid sparse");
for (int i = dstIndex; i < dstNumAllocated; ++i)
for (auto i = dstIndex; i < dstNumAllocated; ++i)
{
new (&dstHashes[i]) SHAMapHash{};
new (&dstChildren[i]) SHAMapTreeNodePtr{};
@@ -448,9 +453,9 @@ inline TaggedPointer::TaggedPointer(
new (&newChildren[branchNum]) SHAMapTreeNodePtr{std::move(oldChildren[indexNum])};
});
// Run the constructors for the remaining elements
for (int i = 0; i < SHAMapInnerNode::kBranchFactor; ++i)
for (auto i = 0u; i < SHAMapInnerNode::kBranchFactor; ++i)
{
if (((1 << i) & isBranch) != 0)
if (((1u << i) & isBranch) != 0u)
continue;
new (&newHashes[i]) SHAMapHash{};
new (&newChildren[i]) SHAMapTreeNodePtr{};
@@ -459,7 +464,7 @@ inline TaggedPointer::TaggedPointer(
else
{
// new arrays are sparse, old arrays may be sparse or dense
int curCompressedIndex = 0;
auto curCompressedIndex = 0u;
iterNonEmptyChildIndexes(isBranch, [&](auto branchNum, auto indexNum) {
new (&newHashes[curCompressedIndex]) SHAMapHash{oldHashes[indexNum]};
new (&newChildren[curCompressedIndex])
@@ -467,7 +472,7 @@ inline TaggedPointer::TaggedPointer(
++curCompressedIndex;
});
// Run the constructors for the remaining elements
for (int i = curCompressedIndex; i < newNumAllocated; ++i)
for (auto i = curCompressedIndex; i < newNumAllocated; ++i)
{
new (&newHashes[i]) SHAMapHash{};
new (&newChildren[i]) SHAMapTreeNodePtr{};