Skip to content

Commit

Permalink
[FOLD] Write a collection to a stream with delimiters
Browse files Browse the repository at this point in the history
  • Loading branch information
ximinez committed Feb 16, 2022
1 parent dcee594 commit 2be56a2
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 15 deletions.
1 change: 1 addition & 0 deletions Builds/CMake/RippledCore.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,7 @@ if (tests)
src/test/basics/contract_test.cpp
src/test/basics/FeeUnits_test.cpp
src/test/basics/hardened_hash_test.cpp
src/test/basics/join_test.cpp
src/test/basics/mulDiv_test.cpp
src/test/basics/tagged_integer_test.cpp
#[===============================[
Expand Down
18 changes: 3 additions & 15 deletions src/ripple/app/paths/Pathfinder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <ripple/app/paths/RippleLineCache.h>
#include <ripple/app/paths/impl/PathfinderUtils.h>
#include <ripple/basics/Log.h>
#include <ripple/basics/join.h>
#include <ripple/core/Config.h>
#include <ripple/core/JobQueue.h>
#include <ripple/json/to_string.h>
Expand Down Expand Up @@ -157,20 +158,6 @@ smallestUsefulAmount(STAmount const& amount, int maxPaths)
}
} // namespace

template <class Stream>
Stream&
operator<<(Stream& s, Pathfinder::PathType const& v)
{
auto iter = v.begin();
if (iter != v.end())
{
s << *iter;
for (++iter; iter != v.end(); ++iter)
s << ", " << *iter;
}
return s;
}

Pathfinder::Pathfinder(
std::shared_ptr<RippleLineCache> const& cache,
AccountID const& uSrcAccount,
Expand Down Expand Up @@ -804,7 +791,8 @@ Pathfinder::addPathsForType(
PathType const& pathType,
std::function<bool(void)> const& continueCallback)
{
JLOG(j_.warn()) << "addPathsForType " << pathType;
JLOG(j_.warn()) << "addPathsForType "
<< CollectionAndDelimiter(pathType, ", ");
// See if the set of paths for this type already exists.
auto it = mPaths.find(pathType);
if (it != mPaths.end())
Expand Down
106 changes: 106 additions & 0 deletions src/ripple/basics/join.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2022 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef JOIN_H_INCLUDED
#define JOIN_H_INCLUDED

namespace ripple {

template <class Stream, class Iter, class Str>
Stream&
join(Stream& s, Iter iter, Iter end, Str const& delimiter)
{
if (iter == end)
return s;
s << *iter;
for (++iter; iter != end; ++iter)
s << delimiter << *iter;
return s;
}

template <class Collection, class Str>
class CollectionAndDelimiter
{
public:
Collection const& collection;
Str const& delimiter;

explicit CollectionAndDelimiter(Collection const& c, Str const& delim)
: collection(c), delimiter(delim)
{
}

template <class Stream>
friend Stream&
operator<<(Stream& s, CollectionAndDelimiter const& cd)
{
return join(
s,
std::begin(cd.collection),
std::end(cd.collection),
cd.delimiter);
}
};

template <class Collection, size_t N, class Str>
class CollectionAndDelimiter<Collection[N], Str>
{
public:
Collection const* collection;
Str const& delimiter;

explicit CollectionAndDelimiter(Collection const c[N], Str const& delim)
: collection(c), delimiter(delim)
{
}

template <class Stream>
friend Stream&
operator<<(Stream& s, CollectionAndDelimiter const& cd)
{
return join(s, cd.collection, cd.collection + N, cd.delimiter);
}
};

// Specialization for const char* strings
template <size_t N, class Str>
class CollectionAndDelimiter<char[N], Str>
{
public:
char const* collection;
Str const& delimiter;

explicit CollectionAndDelimiter(char const c[N], Str const& delim)
: collection(c), delimiter(delim)
{
}

template <class Stream>
friend Stream&
operator<<(Stream& s, CollectionAndDelimiter const& cd)
{
auto end = cd.collection + N;
if (N > 0 && *(end - 1) == '\0')
--end;
return join(s, cd.collection, end, cd.delimiter);
}
};

} // namespace ripple

#endif
88 changes: 88 additions & 0 deletions src/test/basics/join_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2022 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================

#include <ripple/basics/join.h>

#include <ripple/beast/unit_test.h>
#include <test/jtx/Account.h>

namespace ripple {
namespace test {

struct join_test : beast::unit_test::suite
{
void
run() override
{
auto test = [this](auto collectionanddelimiter, std::string expected) {
std::stringstream ss;
// Put something else in the buffer before and after to ensure that
// the << operator returns the stream correctly.
ss << "(" << collectionanddelimiter << ")";
auto const str = ss.str();
BEAST_EXPECT(str.substr(1, str.length() - 2) == expected);
BEAST_EXPECT(str.front() == '(');
BEAST_EXPECT(str.back() == ')');
};

// C++ array
test(
CollectionAndDelimiter(std::array<int, 4>{2, -1, 5, 10}, '/'),
"2/-1/5/10");
// Empty C++ array edge case
test(CollectionAndDelimiter(std::array<int, 0>{}, uint256{5}), "");
{
// C-style array
char letters[4]{'w', 'a', 's', 'd'};
test(CollectionAndDelimiter(letters, 0), "w0a0s0d");
}
{
// Auto sized C-style array
std::string words[]{"one", "two", "three", "four"};
test(CollectionAndDelimiter(words, "\n"), "one\ntwo\nthree\nfour");
}
// Initializer list
test(
CollectionAndDelimiter(std::initializer_list<size_t>{19, 25}, "+"),
"19+25");
{
using namespace jtx;
// vector
test(CollectionAndDelimiter(std::vector<int>{0, 42}, 99), "09942");
// vector with one item edge case
test(
CollectionAndDelimiter(
std::vector<Account>{Account::master}, "xxx"),
Account::master.human());
}
// C-style string
test(CollectionAndDelimiter("string", " "), "s t r i n g");
// Empty C-style string edge case
test(CollectionAndDelimiter("", "*"), "");
// C-style string
test(CollectionAndDelimiter(std::string{"string"}, " "), "s t r i n g");
// Empty C-style string edge case
test(CollectionAndDelimiter(std::string{""}, "*"), "");
}
}; // namespace test

BEAST_DEFINE_TESTSUITE(join, ripple_basics, ripple);

} // namespace test
} // namespace ripple

0 comments on commit 2be56a2

Please sign in to comment.