mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-26 16:40:20 +00:00
Merge branch 'develop' into tapanito/lending-fix-amendment
This commit is contained in:
2
.github/scripts/strategy-matrix/macos.json
vendored
2
.github/scripts/strategy-matrix/macos.json
vendored
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"platform": "macos/arm64",
|
||||
"runner": ["self-hosted", "macOS", "ARM64", "mac-runner-m1"],
|
||||
"runner": ["self-hosted", "macOS", "ARM64", "macos-26-apple-clang-21"],
|
||||
"configs": [
|
||||
{
|
||||
"build_type": "Release",
|
||||
|
||||
2
.github/workflows/publish-docs.yml
vendored
2
.github/workflows/publish-docs.yml
vendored
@@ -47,7 +47,7 @@ jobs:
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Prepare runner
|
||||
uses: XRPLF/actions/prepare-runner@c47daebb2f9db64ffbac71b47d68a661498d5ce8
|
||||
uses: XRPLF/actions/prepare-runner@9355d190fd7d4de80fadfd161e6edddc9702cd9f
|
||||
with:
|
||||
enable_ccache: false
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ jobs:
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Prepare runner
|
||||
uses: XRPLF/actions/prepare-runner@c47daebb2f9db64ffbac71b47d68a661498d5ce8
|
||||
uses: XRPLF/actions/prepare-runner@9355d190fd7d4de80fadfd161e6edddc9702cd9f
|
||||
with:
|
||||
enable_ccache: ${{ inputs.ccache_enabled }}
|
||||
|
||||
|
||||
2
.github/workflows/reusable-clang-tidy.yml
vendored
2
.github/workflows/reusable-clang-tidy.yml
vendored
@@ -43,7 +43,7 @@ jobs:
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Prepare runner
|
||||
uses: XRPLF/actions/prepare-runner@c47daebb2f9db64ffbac71b47d68a661498d5ce8
|
||||
uses: XRPLF/actions/prepare-runner@9355d190fd7d4de80fadfd161e6edddc9702cd9f
|
||||
with:
|
||||
enable_ccache: false
|
||||
|
||||
|
||||
2
.github/workflows/upload-conan-deps.yml
vendored
2
.github/workflows/upload-conan-deps.yml
vendored
@@ -68,7 +68,7 @@ jobs:
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Prepare runner
|
||||
uses: XRPLF/actions/prepare-runner@c47daebb2f9db64ffbac71b47d68a661498d5ce8
|
||||
uses: XRPLF/actions/prepare-runner@9355d190fd7d4de80fadfd161e6edddc9702cd9f
|
||||
with:
|
||||
enable_ccache: false
|
||||
|
||||
|
||||
10
conanfile.py
10
conanfile.py
@@ -30,10 +30,8 @@ class Xrpl(ConanFile):
|
||||
"ed25519/2015.03",
|
||||
"grpc/1.81.1",
|
||||
"libarchive/3.8.7",
|
||||
"mpt-crypto/0.4.0-rc2",
|
||||
"nudb/2.0.9",
|
||||
"openssl/3.6.3",
|
||||
"secp256k1/0.7.1",
|
||||
"soci/4.0.3",
|
||||
"zlib/1.3.2",
|
||||
]
|
||||
@@ -133,13 +131,15 @@ class Xrpl(ConanFile):
|
||||
def requirements(self):
|
||||
self.requires("boost/1.91.0", force=True, transitive_headers=True)
|
||||
self.requires("date/3.0.4", transitive_headers=True)
|
||||
self.requires("lz4/1.10.0", force=True)
|
||||
self.requires("protobuf/6.33.5", force=True)
|
||||
self.requires("sqlite3/3.53.0", force=True)
|
||||
if self.options.jemalloc:
|
||||
self.requires("jemalloc/5.3.1")
|
||||
self.requires("lz4/1.10.0", force=True)
|
||||
self.requires("mpt-crypto/0.4.0-rc2", transitive_headers=True)
|
||||
self.requires("protobuf/6.33.5", force=True)
|
||||
if self.options.rocksdb:
|
||||
self.requires("rocksdb/10.5.1")
|
||||
self.requires("secp256k1/0.7.1", transitive_headers=True)
|
||||
self.requires("sqlite3/3.53.0", force=True)
|
||||
self.requires("xxhash/0.8.3", transitive_headers=True)
|
||||
|
||||
exports_sources = (
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/invariants/InvariantCheck.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <exception>
|
||||
@@ -114,7 +115,7 @@ ApplyContext::checkInvariantsHelper(
|
||||
tx, result, fee, *view_, journal)...}}; // NOLINT(bugprone-unchecked-optional-access)
|
||||
|
||||
// call each check's finalizer to see that it passes
|
||||
if (!std::all_of(finalizers.cbegin(), finalizers.cend(), [](auto const& b) { return b; }))
|
||||
if (!std::ranges::all_of(finalizers, [](auto const& b) { return b; }))
|
||||
{
|
||||
JLOG(journal.fatal()) << "Transaction has failed one or more global invariants: "
|
||||
<< to_string(tx.getJson(JsonOptions::Values::None));
|
||||
|
||||
@@ -876,10 +876,9 @@ ValidPseudoAccounts::visitEntry(bool isDelete, SLE::const_ref before, SLE::const
|
||||
{
|
||||
std::vector<SField const*> const& fields = getPseudoAccountFields();
|
||||
|
||||
auto const numFields =
|
||||
std::count_if(fields.begin(), fields.end(), [&after](SField const* sf) -> bool {
|
||||
return after->isFieldPresent(*sf);
|
||||
});
|
||||
auto const numFields = std::ranges::count_if(
|
||||
fields,
|
||||
[&after](SField const* sf) -> bool { return after->isFieldPresent(*sf); });
|
||||
if (numFields != 1)
|
||||
{
|
||||
std::stringstream error;
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include <xrpl/tx/paths/Flow.h>
|
||||
#include <xrpl/tx/paths/detail/Steps.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <limits>
|
||||
@@ -301,10 +302,8 @@ onNewAttestations(
|
||||
}
|
||||
|
||||
auto const& claimSigningAccount = att->attestationSignerAccount;
|
||||
if (auto i = std::find_if(
|
||||
attestations.begin(),
|
||||
attestations.end(),
|
||||
[&](auto const& a) { return a.keyAccount == claimSigningAccount; });
|
||||
if (auto i = std::ranges::find_if(
|
||||
attestations, [&](auto const& a) { return a.keyAccount == claimSigningAccount; });
|
||||
i != attestations.end())
|
||||
{
|
||||
// existing attestation
|
||||
|
||||
@@ -140,7 +140,7 @@ private:
|
||||
combineArg(std::vector<Arg>& dest, std::vector<Arg> const& src, Args const&... args)
|
||||
{
|
||||
assert(dest.capacity() >= dest.size() + src.size());
|
||||
std::copy(src.begin(), src.end(), std::back_inserter(dest));
|
||||
std::ranges::copy(src, std::back_inserter(dest));
|
||||
if constexpr (sizeof...(args) > 0)
|
||||
combineArg(dest, args...);
|
||||
}
|
||||
|
||||
@@ -290,10 +290,9 @@ public:
|
||||
if (inManifests.size() == loadedManifests.size())
|
||||
{
|
||||
BEAST_EXPECT(
|
||||
std::equal(
|
||||
inManifests.begin(),
|
||||
inManifests.end(),
|
||||
loadedManifests.begin(),
|
||||
std::ranges::equal(
|
||||
inManifests,
|
||||
loadedManifests,
|
||||
[](Manifest const* lhs, Manifest const* rhs) { return *lhs == *rhs; }));
|
||||
}
|
||||
else
|
||||
|
||||
@@ -3728,10 +3728,9 @@ public:
|
||||
auto actorOffers = offersOnAccount(env, actor.acct);
|
||||
auto const offerCount = std::distance(
|
||||
actorOffers.begin(),
|
||||
std::remove_if(
|
||||
actorOffers.begin(), actorOffers.end(), [](SLE::const_pointer& offer) {
|
||||
return (*offer)[sfTakerGets].signum() == 0;
|
||||
}));
|
||||
std::ranges::remove_if(actorOffers, [](SLE::const_pointer& offer) {
|
||||
return (*offer)[sfTakerGets].signum() == 0;
|
||||
}).begin());
|
||||
BEAST_EXPECT(offerCount == actor.offers);
|
||||
|
||||
env.require(Balance(actor.acct, actor.xrp));
|
||||
@@ -3898,10 +3897,9 @@ public:
|
||||
auto actorOffers = offersOnAccount(env, actor.acct);
|
||||
auto const offerCount = std::distance(
|
||||
actorOffers.begin(),
|
||||
std::remove_if(
|
||||
actorOffers.begin(), actorOffers.end(), [](SLE::const_pointer& offer) {
|
||||
return (*offer)[sfTakerGets].signum() == 0;
|
||||
}));
|
||||
std::ranges::remove_if(actorOffers, [](SLE::const_pointer& offer) {
|
||||
return (*offer)[sfTakerGets].signum() == 0;
|
||||
}).begin());
|
||||
BEAST_EXPECT(offerCount == actor.offers);
|
||||
|
||||
env.require(Balance(actor.acct, actor.xrp));
|
||||
|
||||
@@ -306,9 +306,8 @@ struct BalanceTransfer
|
||||
[[nodiscard]] bool
|
||||
payeesReceived(STAmount const& reward) const
|
||||
{
|
||||
return std::all_of(rewardAccounts.begin(), rewardAccounts.end(), [&](balance const& b) {
|
||||
return b.diff() == reward;
|
||||
});
|
||||
return std::ranges::all_of(
|
||||
rewardAccounts, [&](balance const& b) { return b.diff() == reward; });
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
@@ -1296,13 +1296,7 @@ AgedAssociativeContainerTestBase::testChronological()
|
||||
|
||||
typename Traits::template Cont<> c(v.begin(), v.end(), clock);
|
||||
|
||||
BEAST_EXPECT(
|
||||
std::equal(
|
||||
c.chronological.cbegin(),
|
||||
c.chronological.cend(),
|
||||
v.begin(),
|
||||
v.end(),
|
||||
EqualValue<Traits>()));
|
||||
BEAST_EXPECT(std::ranges::equal(c.chronological, v, EqualValue<Traits>()));
|
||||
|
||||
// Test touch() with a non-const iterator.
|
||||
for (auto iter(v.crbegin()); iter != v.crend(); ++iter)
|
||||
@@ -1336,13 +1330,7 @@ AgedAssociativeContainerTestBase::testChronological()
|
||||
c.touch(found);
|
||||
}
|
||||
|
||||
BEAST_EXPECT(
|
||||
std::equal(
|
||||
c.chronological.cbegin(),
|
||||
c.chronological.cend(),
|
||||
v.cbegin(),
|
||||
v.cend(),
|
||||
EqualValue<Traits>()));
|
||||
BEAST_EXPECT(std::ranges::equal(c.chronological, v, EqualValue<Traits>()));
|
||||
|
||||
{
|
||||
// Because touch (reverse_iterator pos) is not allowed, the following
|
||||
@@ -1407,8 +1395,8 @@ AgedAssociativeContainerTestBase::reverseFillAgedContainer(Container& c, Values
|
||||
clk.set(0);
|
||||
|
||||
Values rev(values);
|
||||
std::sort(rev.begin(), rev.end());
|
||||
std::reverse(rev.begin(), rev.end());
|
||||
std::ranges::sort(rev);
|
||||
std::ranges::reverse(rev);
|
||||
for (auto& v : rev)
|
||||
{
|
||||
// Add values in reverse order so they are reversed chronologically.
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <boost/asio/spawn.hpp>
|
||||
#include <boost/system/detail/error_code.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <condition_variable> // IWYU pragma: keep
|
||||
#include <cstddef>
|
||||
@@ -94,18 +95,14 @@ class io_latency_probe_test : public beast::unit_test::Suite, public beast::test
|
||||
auto
|
||||
getMax()
|
||||
{
|
||||
return std::chrono::duration_cast<D>(
|
||||
*std::max_element(elapsedTimes.begin(), elapsedTimes.end()))
|
||||
.count();
|
||||
return std::chrono::duration_cast<D>(*std::ranges::max_element(elapsedTimes)).count();
|
||||
}
|
||||
|
||||
template <class D>
|
||||
auto
|
||||
getMin()
|
||||
{
|
||||
return std::chrono::duration_cast<D>(
|
||||
*std::min_element(elapsedTimes.begin(), elapsedTimes.end()))
|
||||
.count();
|
||||
return std::chrono::duration_cast<D>(*std::ranges::min_element(elapsedTimes)).count();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -31,12 +31,12 @@ class RCLCensorshipDetector_test : public beast::unit_test::Suite
|
||||
cdet.check(std::move(accepted), [&remove, &remain](auto id, auto seq) {
|
||||
// If the item is supposed to be removed from the censorship
|
||||
// detector internal tracker manually, do it now:
|
||||
if (std::find(remove.begin(), remove.end(), id) != remove.end())
|
||||
if (std::ranges::find(remove, id) != remove.end())
|
||||
return true;
|
||||
|
||||
// If the item is supposed to still remain in the censorship
|
||||
// detector internal tracker; remove it from the vector.
|
||||
auto it = std::find(remain.begin(), remain.end(), id);
|
||||
auto it = std::ranges::find(remain, id);
|
||||
if (it != remain.end())
|
||||
remain.erase(it);
|
||||
return false;
|
||||
|
||||
@@ -44,7 +44,7 @@ std::vector<typename RandomNumberDistribution::result_type>
|
||||
sample(std::size_t size, RandomNumberDistribution dist, Generator& g)
|
||||
{
|
||||
std::vector<typename RandomNumberDistribution::result_type> res(size);
|
||||
std::generate(res.begin(), res.end(), [&dist, &g]() { return dist(g); });
|
||||
std::ranges::generate(res, [&dist, &g]() { return dist(g); });
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@@ -167,10 +167,9 @@ public:
|
||||
for (auto i = std::make_pair(0, c.hops.begin()); i.second != c.hops.end();
|
||||
++i.first, ++i.second)
|
||||
{
|
||||
std::copy((*i.second).begin(), (*i.second).end(), std::back_inserter(before[i.first]));
|
||||
std::copy(
|
||||
(*i.second).begin(), (*i.second).end(), std::back_inserter(beforeSorted[i.first]));
|
||||
std::sort(beforeSorted[i.first].begin(), beforeSorted[i.first].end(), cmpEp);
|
||||
std::ranges::copy(*i.second, std::back_inserter(before[i.first]));
|
||||
std::ranges::copy(*i.second, std::back_inserter(beforeSorted[i.first]));
|
||||
std::ranges::sort(beforeSorted[i.first], cmpEp);
|
||||
}
|
||||
|
||||
c.hops.shuffle();
|
||||
@@ -180,10 +179,9 @@ public:
|
||||
for (auto i = std::make_pair(0, c.hops.begin()); i.second != c.hops.end();
|
||||
++i.first, ++i.second)
|
||||
{
|
||||
std::copy((*i.second).begin(), (*i.second).end(), std::back_inserter(after[i.first]));
|
||||
std::copy(
|
||||
(*i.second).begin(), (*i.second).end(), std::back_inserter(afterSorted[i.first]));
|
||||
std::sort(afterSorted[i.first].begin(), afterSorted[i.first].end(), cmpEp);
|
||||
std::ranges::copy(*i.second, std::back_inserter(after[i.first]));
|
||||
std::ranges::copy(*i.second, std::back_inserter(afterSorted[i.first]));
|
||||
std::ranges::sort(afterSorted[i.first], cmpEp);
|
||||
}
|
||||
|
||||
// each hop bucket should contain the same items
|
||||
|
||||
@@ -127,9 +127,8 @@ std::size_t
|
||||
InboundLedger::getPeerCount() const
|
||||
{
|
||||
auto const& peerIds = peerSet_->getPeerIds();
|
||||
return std::count_if(peerIds.begin(), peerIds.end(), [this](auto id) {
|
||||
return (app_.getOverlay().findPeerByShortID(id) != nullptr);
|
||||
});
|
||||
return std::ranges::count_if(
|
||||
peerIds, [this](auto id) { return (app_.getOverlay().findPeerByShortID(id) != nullptr); });
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -204,10 +204,8 @@ struct Node
|
||||
void
|
||||
erase(Node const* child)
|
||||
{
|
||||
auto it = std::find_if(
|
||||
children.begin(), children.end(), [child](std::unique_ptr<Node> const& curr) {
|
||||
return curr.get() == child;
|
||||
});
|
||||
auto it = std::ranges::find_if(
|
||||
children, [child](std::unique_ptr<Node> const& curr) { return curr.get() == child; });
|
||||
XRPL_ASSERT(it != children.end(), "xrpl::Node::erase : valid input");
|
||||
std::swap(*it, children.back());
|
||||
children.pop_back();
|
||||
|
||||
@@ -817,16 +817,15 @@ public:
|
||||
if (!preferred)
|
||||
{
|
||||
// fall back to majority over acquiring ledgers
|
||||
auto it = std::max_element(
|
||||
acquiring_.begin(), acquiring_.end(), [](auto const& a, auto const& b) {
|
||||
std::pair<Seq, ID> const& aKey = a.first;
|
||||
typename hash_set<NodeID>::size_type const& aSize = a.second.size();
|
||||
std::pair<Seq, ID> const& bKey = b.first;
|
||||
typename hash_set<NodeID>::size_type const& bSize = b.second.size();
|
||||
// order by number of trusted peers validating that ledger
|
||||
// break ties with ledger ID
|
||||
return std::tie(aSize, aKey.second) < std::tie(bSize, bKey.second);
|
||||
});
|
||||
auto it = std::ranges::max_element(acquiring_, [](auto const& a, auto const& b) {
|
||||
std::pair<Seq, ID> const& aKey = a.first;
|
||||
typename hash_set<NodeID>::size_type const& aSize = a.second.size();
|
||||
std::pair<Seq, ID> const& bKey = b.first;
|
||||
typename hash_set<NodeID>::size_type const& bSize = b.second.size();
|
||||
// order by number of trusted peers validating that ledger
|
||||
// break ties with ledger ID
|
||||
return std::tie(aSize, aKey.second) < std::tie(bSize, bKey.second);
|
||||
});
|
||||
if (it != acquiring_.end())
|
||||
return it->first;
|
||||
return std::nullopt;
|
||||
@@ -896,7 +895,7 @@ public:
|
||||
return (preferred->first >= minSeq) ? preferred->second : lcl.id();
|
||||
|
||||
// Otherwise, rely on peer ledgers
|
||||
auto it = std::max_element(peerCounts.begin(), peerCounts.end(), [](auto& a, auto& b) {
|
||||
auto it = std::ranges::max_element(peerCounts, [](auto const& a, auto const& b) {
|
||||
// Prefer larger counts, then larger ids on ties
|
||||
// (max_element expects this to return true if a < b)
|
||||
return std::tie(a.second, a.first) < std::tie(b.second, b.first);
|
||||
@@ -932,7 +931,7 @@ public:
|
||||
}
|
||||
|
||||
// Count parent ledgers as fallback
|
||||
return std::count_if(lastLedger_.begin(), lastLedger_.end(), [&ledgerID](auto const& it) {
|
||||
return std::ranges::count_if(lastLedger_, [&ledgerID](auto const& it) {
|
||||
auto const& curr = it.second;
|
||||
return curr.seq() > Seq{0} && curr[curr.seq() - Seq{1}] == ledgerID;
|
||||
});
|
||||
|
||||
@@ -143,7 +143,7 @@ RedirectHandouts::tryInsert(Endpoint const& ep)
|
||||
return false;
|
||||
|
||||
// Make sure the address isn't already in our list
|
||||
if (std::any_of(list_.begin(), list_.end(), [&ep](Endpoint const& other) {
|
||||
if (std::ranges::any_of(list_, [&ep](Endpoint const& other) {
|
||||
// Ignore port for security reasons
|
||||
return other.address.address() == ep.address.address();
|
||||
}))
|
||||
@@ -222,7 +222,7 @@ SlotHandouts::tryInsert(Endpoint const& ep)
|
||||
return false;
|
||||
|
||||
// Make sure the address isn't already in our list
|
||||
if (std::any_of(list_.begin(), list_.end(), [&ep](Endpoint const& other) {
|
||||
if (std::ranges::any_of(list_, [&ep](Endpoint const& other) {
|
||||
// Ignore port for security reasons
|
||||
return other.address.address() == ep.address.address();
|
||||
}))
|
||||
@@ -311,7 +311,7 @@ ConnectHandouts::tryInsert(beast::IP::Endpoint const& endpoint)
|
||||
return false;
|
||||
|
||||
// Make sure the address isn't already in our list
|
||||
if (std::any_of(list_.begin(), list_.end(), [&endpoint](beast::IP::Endpoint const& other) {
|
||||
if (std::ranges::any_of(list_, [&endpoint](beast::IP::Endpoint const& other) {
|
||||
// Ignore port for security reasons
|
||||
return other.address() == endpoint.address();
|
||||
}))
|
||||
|
||||
@@ -573,19 +573,17 @@ public:
|
||||
// build list of active slots
|
||||
std::vector<SlotImp::ptr> activeSlots;
|
||||
activeSlots.reserve(slots.size());
|
||||
std::for_each(
|
||||
slots.cbegin(), slots.cend(), [&activeSlots](Slots::value_type const& value) {
|
||||
if (value.second->state() == Slot::State::Active)
|
||||
activeSlots.emplace_back(value.second);
|
||||
});
|
||||
std::ranges::for_each(slots, [&activeSlots](Slots::value_type const& value) {
|
||||
if (value.second->state() == Slot::State::Active)
|
||||
activeSlots.emplace_back(value.second);
|
||||
});
|
||||
std::shuffle(activeSlots.begin(), activeSlots.end(), defaultPrng());
|
||||
|
||||
// build target vector
|
||||
targets.reserve(activeSlots.size());
|
||||
std::for_each(
|
||||
activeSlots.cbegin(), activeSlots.cend(), [&targets](SlotImp::ptr const& slot) {
|
||||
targets.emplace_back(slot);
|
||||
});
|
||||
std::ranges::for_each(activeSlots, [&targets](SlotImp::ptr const& slot) {
|
||||
targets.emplace_back(slot);
|
||||
});
|
||||
}
|
||||
|
||||
/* VFALCO NOTE
|
||||
@@ -987,7 +985,7 @@ public:
|
||||
{
|
||||
auto const& address(iter->first.address());
|
||||
if (iter->second.when() <= now && squelches.find(address) == squelches.end() &&
|
||||
std::none_of(slots.cbegin(), slots.cend(), [address](Slots::value_type const& v) {
|
||||
std::ranges::none_of(slots, [address](Slots::value_type const& v) {
|
||||
return address == v.first.address();
|
||||
}))
|
||||
{
|
||||
|
||||
@@ -1179,9 +1179,8 @@ parsePorts(Config const& config, std::ostream& log)
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const count = std::count_if(result.cbegin(), result.cend(), [](Port const& p) {
|
||||
return p.protocol.contains("peer");
|
||||
});
|
||||
auto const count = std::ranges::count_if(
|
||||
result, [](Port const& p) { return p.protocol.contains("peer"); });
|
||||
|
||||
if (count > 1)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user