refactor: Remove dead headers (#5081)

This commit is contained in:
John Freeman
2024-08-28 14:23:38 -05:00
committed by GitHub
parent 4d7aed84ec
commit cad8970a57
24 changed files with 0 additions and 2557 deletions

View File

@@ -1,26 +0,0 @@
# Continuous Integration (CI) Scripts
In this directory are two scripts, `build.sh` and `test.sh` used for building
and testing rippled.
(For now, they assume Bash and Linux. Once I get Windows containers for
testing, I'll try them there, but if Bash is not available, then they will
soon be joined by PowerShell scripts `build.ps` and `test.ps`.)
We don't want these scripts to require arcane invocations that can only be
pieced together from within a CI configuration. We want something that humans
can easily invoke, read, and understand, for when we eventually have to test
and debug them interactively. That means:
(1) They should work with no arguments.
(2) They should document their arguments.
(3) They should expand short arguments into long arguments.
While we want to provide options for common use cases, we don't need to offer
the kitchen sink. We can rightfully expect users with esoteric, complicated
needs to write their own scripts.
To make argument-handling easy for us, the implementers, we can just take all
arguments from environment variables. They have the nice advantage that every
command-line uses named arguments. For the benefit of us and our users, we
document those variables at the top of each script.

View File

@@ -1,31 +0,0 @@
#!/usr/bin/env bash
set -o xtrace
set -o errexit
# The build system. Either 'Unix Makefiles' or 'Ninja'.
GENERATOR=${GENERATOR:-Unix Makefiles}
# The compiler. Either 'gcc' or 'clang'.
COMPILER=${COMPILER:-gcc}
# The build type. Either 'Debug' or 'Release'.
BUILD_TYPE=${BUILD_TYPE:-Debug}
# Additional arguments to CMake.
# We use the `-` substitution here instead of `:-` so that callers can erase
# the default by setting `$CMAKE_ARGS` to the empty string.
CMAKE_ARGS=${CMAKE_ARGS-'-Dwerr=ON'}
# https://gitlab.kitware.com/cmake/cmake/issues/18865
CMAKE_ARGS="-DBoost_NO_BOOST_CMAKE=ON ${CMAKE_ARGS}"
if [[ ${COMPILER} == 'gcc' ]]; then
export CC='gcc'
export CXX='g++'
elif [[ ${COMPILER} == 'clang' ]]; then
export CC='clang'
export CXX='clang++'
fi
mkdir build
cd build
cmake -G "${GENERATOR}" -DCMAKE_BUILD_TYPE=${BUILD_TYPE} ${CMAKE_ARGS} ..
cmake --build . -- -j $(nproc)

View File

@@ -1,41 +0,0 @@
#!/usr/bin/env bash
set -o xtrace
set -o errexit
# Set to 'true' to run the known "manual" tests in rippled.
MANUAL_TESTS=${MANUAL_TESTS:-false}
# The maximum number of concurrent tests.
CONCURRENT_TESTS=${CONCURRENT_TESTS:-$(nproc)}
# The path to rippled.
RIPPLED=${RIPPLED:-build/rippled}
# Additional arguments to rippled.
RIPPLED_ARGS=${RIPPLED_ARGS:-}
function join_by { local IFS="$1"; shift; echo "$*"; }
declare -a manual_tests=(
'beast.chrono.abstract_clock'
'beast.unit_test.print'
'ripple.NodeStore.Timing'
'ripple.app.Flow_manual'
'ripple.app.NoRippleCheckLimits'
'ripple.app.PayStrandAllPairs'
'ripple.consensus.ByzantineFailureSim'
'ripple.consensus.DistributedValidators'
'ripple.consensus.ScaleFreeSim'
'ripple.tx.CrossingLimits'
'ripple.tx.FindOversizeCross'
'ripple.tx.Offer_manual'
'ripple.tx.OversizeMeta'
'ripple.tx.PlumpBook'
)
if [[ ${MANUAL_TESTS} == 'true' ]]; then
RIPPLED_ARGS+=" --unittest=$(join_by , "${manual_tests[@]}")"
else
RIPPLED_ARGS+=" --unittest --quiet --unittest-log"
fi
RIPPLED_ARGS+=" --unittest-jobs ${CONCURRENT_TESTS}"
${RIPPLED} ${RIPPLED_ARGS}

View File

@@ -1,274 +0,0 @@
#!/usr/bin/env bash
set -ex
function version_ge() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; }
__dirname=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
echo "using CC: ${CC}"
"${CC}" --version
export CC
COMPNAME=$(basename $CC)
echo "using CXX: ${CXX:-notset}"
if [[ $CXX ]]; then
"${CXX}" --version
export CXX
fi
: ${BUILD_TYPE:=Debug}
echo "BUILD TYPE: ${BUILD_TYPE}"
: ${TARGET:=install}
echo "BUILD TARGET: ${TARGET}"
JOBS=${NUM_PROCESSORS:-2}
if [[ ${TRAVIS:-false} != "true" ]]; then
JOBS=$((JOBS+1))
fi
if [[ ! -z "${CMAKE_EXE:-}" ]] ; then
export PATH="$(dirname ${CMAKE_EXE}):$PATH"
fi
if [ -x /usr/bin/time ] ; then
: ${TIME:="Duration: %E"}
export TIME
time=/usr/bin/time
else
time=
fi
echo "Building rippled"
: ${CMAKE_EXTRA_ARGS:=""}
if [[ ${NINJA_BUILD:-} == true ]]; then
CMAKE_EXTRA_ARGS+=" -G Ninja"
fi
coverage=false
if [[ "${TARGET}" == "coverage" ]] ; then
echo "coverage option detected."
coverage=true
fi
cmake --version
CMAKE_VER=$(cmake --version | cut -d " " -f 3 | head -1)
#
# allow explicit setting of the name of the build
# dir, otherwise default to the compiler.build_type
#
: "${BUILD_DIR:=${COMPNAME}.${BUILD_TYPE}}"
BUILDARGS="--target ${TARGET}"
BUILDTOOLARGS=""
if version_ge $CMAKE_VER "3.12.0" ; then
BUILDARGS+=" --parallel"
fi
if [[ ${NINJA_BUILD:-} == false ]]; then
if version_ge $CMAKE_VER "3.12.0" ; then
BUILDARGS+=" ${JOBS}"
else
BUILDTOOLARGS+=" -j ${JOBS}"
fi
fi
if [[ ${VERBOSE_BUILD:-} == true ]]; then
CMAKE_EXTRA_ARGS+=" -DCMAKE_VERBOSE_MAKEFILE=ON"
if version_ge $CMAKE_VER "3.14.0" ; then
BUILDARGS+=" --verbose"
else
if [[ ${NINJA_BUILD:-} == false ]]; then
BUILDTOOLARGS+=" verbose=1"
else
BUILDTOOLARGS+=" -v"
fi
fi
fi
if [[ ${USE_CCACHE:-} == true ]]; then
echo "using ccache with basedir [${CCACHE_BASEDIR:-}]"
CMAKE_EXTRA_ARGS+=" -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache"
fi
if [ -d "build/${BUILD_DIR}" ]; then
rm -rf "build/${BUILD_DIR}"
fi
mkdir -p "build/${BUILD_DIR}"
pushd "build/${BUILD_DIR}"
# cleanup possible artifacts
rm -fv CMakeFiles/CMakeOutput.log CMakeFiles/CMakeError.log
# Clean up NIH directories which should be git repos, but aren't
for nih_path in ${NIH_CACHE_ROOT}/*/*/*/src ${NIH_CACHE_ROOT}/*/*/src
do
for dir in lz4 snappy rocksdb
do
if [ -e ${nih_path}/${dir} -a \! -e ${nih_path}/${dir}/.git ]
then
ls -la ${nih_path}/${dir}*
rm -rfv ${nih_path}/${dir}*
fi
done
done
# generate
${time} cmake ../.. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} ${CMAKE_EXTRA_ARGS}
# Display the cmake output, to help with debugging if something fails
for file in CMakeOutput.log CMakeError.log
do
if [ -f CMakeFiles/${file} ]
then
ls -l CMakeFiles/${file}
cat CMakeFiles/${file}
fi
done
# build
export DESTDIR=$(pwd)/_INSTALLED_
${time} eval cmake --build . ${BUILDARGS} -- ${BUILDTOOLARGS}
if [[ ${TARGET} == "docs" ]]; then
## mimic the standard test output for docs build
## to make controlling processes like jenkins happy
if [ -f docs/html/index.html ]; then
echo "1 case, 1 test total, 0 failures"
else
echo "1 case, 1 test total, 1 failures"
fi
exit
fi
popd
if [[ "${TARGET}" == "validator-keys" ]] ; then
export APP_PATH="$PWD/build/${BUILD_DIR}/validator-keys/validator-keys"
else
export APP_PATH="$PWD/build/${BUILD_DIR}/rippled"
fi
echo "using APP_PATH: ${APP_PATH}"
# See what we've actually built
ldd ${APP_PATH}
: ${APP_ARGS:=}
if [[ "${TARGET}" == "validator-keys" ]] ; then
APP_ARGS="--unittest"
else
function join_by { local IFS="$1"; shift; echo "$*"; }
# This is a list of manual tests
# in rippled that we want to run
# ORDER matters here...sorted in approximately
# descending execution time (longest running tests at top)
declare -a manual_tests=(
'ripple.ripple_data.reduce_relay_simulate'
'ripple.tx.Offer_manual'
'ripple.tx.CrossingLimits'
'ripple.tx.PlumpBook'
'ripple.app.Flow_manual'
'ripple.tx.OversizeMeta'
'ripple.consensus.DistributedValidators'
'ripple.app.NoRippleCheckLimits'
'ripple.ripple_data.compression'
'ripple.NodeStore.Timing'
'ripple.consensus.ByzantineFailureSim'
'beast.chrono.abstract_clock'
'beast.unit_test.print'
)
if [[ ${TRAVIS:-false} != "true" ]]; then
# these two tests cause travis CI to run out of memory.
# TODO: investigate possible workarounds.
manual_tests=(
'ripple.consensus.ScaleFreeSim'
'ripple.tx.FindOversizeCross'
"${manual_tests[@]}"
)
fi
if [[ ${MANUAL_TESTS:-} == true ]]; then
APP_ARGS+=" --unittest=$(join_by , "${manual_tests[@]}")"
else
APP_ARGS+=" --unittest --quiet --unittest-log"
fi
if [[ ${coverage} == false && ${PARALLEL_TESTS:-} == true ]]; then
APP_ARGS+=" --unittest-jobs ${JOBS}"
fi
if [[ ${IPV6_TESTS:-} == true ]]; then
APP_ARGS+=" --unittest-ipv6"
fi
fi
if [[ ${coverage} == true && $CC =~ ^gcc ]]; then
# Push the results (lcov.info) to codecov
codecov -X gcov # don't even try and look for .gcov files ;)
find . -name "*.gcda" | xargs rm -f
fi
if [[ ${SKIP_TESTS:-} == true ]]; then
echo "skipping tests."
exit
fi
ulimit -a
corepat=$(cat /proc/sys/kernel/core_pattern)
if [[ ${corepat} =~ ^[:space:]*\| ]] ; then
echo "WARNING: core pattern is piping - can't search for core files"
look_core=false
else
look_core=true
coredir=$(dirname ${corepat})
fi
if [[ ${look_core} == true ]]; then
before=$(ls -A1 ${coredir})
fi
set +e
echo "Running tests for ${APP_PATH}"
if [[ ${MANUAL_TESTS:-} == true && ${PARALLEL_TESTS:-} != true ]]; then
for t in "${manual_tests[@]}" ; do
${APP_PATH} --unittest=${t}
TEST_STAT=$?
if [[ $TEST_STAT -ne 0 ]] ; then
break
fi
done
else
${APP_PATH} ${APP_ARGS}
TEST_STAT=$?
fi
set -e
if [[ ${look_core} == true ]]; then
after=$(ls -A1 ${coredir})
oIFS="${IFS}"
IFS=$'\n\r'
found_core=false
for l in $(diff -w --suppress-common-lines <(echo "$before") <(echo "$after")) ; do
if [[ "$l" =~ ^[[:space:]]*\>[[:space:]]*(.+)$ ]] ; then
corefile="${BASH_REMATCH[1]}"
echo "FOUND core dump file at '${coredir}/${corefile}'"
gdb_output=$(/bin/mktemp /tmp/gdb_output_XXXXXXXXXX.txt)
found_core=true
gdb \
-ex "set height 0" \
-ex "set logging file ${gdb_output}" \
-ex "set logging on" \
-ex "print 'ripple::BuildInfo::versionString'" \
-ex "thread apply all backtrace full" \
-ex "info inferiors" \
-ex quit \
"$APP_PATH" \
"${coredir}/${corefile}" &> /dev/null
echo -e "CORE INFO: \n\n $(cat ${gdb_output}) \n\n)"
fi
done
IFS="${oIFS}"
fi
if [[ ${found_core} == true ]]; then
exit -1
else
exit $TEST_STAT
fi

View File

@@ -1,36 +0,0 @@
#!/usr/bin/env bash
# run our build script in a docker container
# using travis-ci hosts
set -eux
function join_by { local IFS="$1"; shift; echo "$*"; }
set +x
echo "VERBOSE_BUILD=true" > /tmp/co.env
matchers=(
'TRAVIS.*' 'CI' 'CC' 'CXX'
'BUILD_TYPE' 'TARGET' 'MAX_TIME'
'CODECOV.+' 'CMAKE.*' '.+_TESTS'
'.+_OPTIONS' 'NINJA.*' 'NUM_.+'
'NIH_.+' 'BOOST.*' '.*CCACHE.*')
matchstring=$(join_by '|' "${matchers[@]}")
echo "MATCHSTRING IS:: $matchstring"
env | grep -E "^(${matchstring})=" >> /tmp/co.env
set -x
# need to eliminate TRAVIS_CMD...don't want to pass it to the container
cat /tmp/co.env | grep -v TRAVIS_CMD > /tmp/co.env.2
mv /tmp/co.env.2 /tmp/co.env
cat /tmp/co.env
mkdir -p -m 0777 ${TRAVIS_BUILD_DIR}/cores
echo "${TRAVIS_BUILD_DIR}/cores/%e.%p" | sudo tee /proc/sys/kernel/core_pattern
docker run \
-t --env-file /tmp/co.env \
-v ${TRAVIS_HOME}:${TRAVIS_HOME} \
-w ${TRAVIS_BUILD_DIR} \
--cap-add SYS_PTRACE \
--ulimit "core=-1" \
$DOCKER_IMAGE \
/bin/bash -c 'if [[ $CC =~ ([[:alpha:]]+)-([[:digit:].]+) ]] ; then sudo update-alternatives --set ${BASH_REMATCH[1]} /usr/bin/$CC; fi; bin/ci/ubuntu/build-and-test.sh'

View File

@@ -1,44 +0,0 @@
#!/usr/bin/env bash
# some cached files create churn, so save them here for
# later restoration before packing the cache
set -eux
clean_cache="travis_clean_cache"
if [[ ! ( "${TRAVIS_JOB_NAME}" =~ "windows" || \
"${TRAVIS_JOB_NAME}" =~ "prereq-keep" ) ]] && \
( [[ "${TRAVIS_COMMIT_MESSAGE}" =~ "${clean_cache}" ]] || \
( [[ -v TRAVIS_PULL_REQUEST_SHA && \
"${TRAVIS_PULL_REQUEST_SHA}" != "" ]] && \
git log -1 "${TRAVIS_PULL_REQUEST_SHA}" | grep -cq "${clean_cache}" -
)
)
then
find ${TRAVIS_HOME}/_cache -maxdepth 2 -type d
rm -rf ${TRAVIS_HOME}/_cache
mkdir -p ${TRAVIS_HOME}/_cache
fi
pushd ${TRAVIS_HOME}
if [ -f cache_ignore.tar ] ; then
rm -f cache_ignore.tar
fi
if [ -d _cache/nih_c ] ; then
find _cache/nih_c -name "build.ninja" | tar rf cache_ignore.tar --files-from -
find _cache/nih_c -name ".ninja_deps" | tar rf cache_ignore.tar --files-from -
find _cache/nih_c -name ".ninja_log" | tar rf cache_ignore.tar --files-from -
find _cache/nih_c -name "*.log" | tar rf cache_ignore.tar --files-from -
find _cache/nih_c -name "*.tlog" | tar rf cache_ignore.tar --files-from -
# show .a files in the cache, for sanity checking
find _cache/nih_c -name "*.a" -ls
fi
if [ -d _cache/ccache ] ; then
find _cache/ccache -name "stats" | tar rf cache_ignore.tar --files-from -
fi
if [ -f cache_ignore.tar ] ; then
tar -tf cache_ignore.tar
fi
popd

View File

@@ -1,160 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_TEST_FAIL_COUNTER_HPP
#define BEAST_TEST_FAIL_COUNTER_HPP
#include <xrpl/beast/core/error.hpp>
#include <boost/throw_exception.hpp>
namespace beast {
namespace test {
enum class error { fail_error = 1 };
namespace detail {
class fail_error_category : public boost::system::error_category
{
public:
const char*
name() const noexcept override
{
return "test";
}
std::string
message(int ev) const override
{
switch (static_cast<error>(ev))
{
default:
case error::fail_error:
return "test error";
}
}
boost::system::error_condition
default_error_condition(int ev) const noexcept override
{
return boost::system::error_condition{ev, *this};
}
bool
equivalent(int ev, boost::system::error_condition const& condition)
const noexcept override
{
return condition.value() == ev && &condition.category() == this;
}
bool
equivalent(error_code const& error, int ev) const noexcept override
{
return error.value() == ev && &error.category() == this;
}
};
inline boost::system::error_category const&
get_error_category()
{
static fail_error_category const cat{};
return cat;
}
} // namespace detail
inline error_code
make_error_code(error ev)
{
return error_code{
static_cast<std::underlying_type<error>::type>(ev),
detail::get_error_category()};
}
/** An error code with an error set on default construction
Default constructed versions of this object will have
an error code set right away. This helps tests find code
which forgets to clear the error code on success.
*/
struct fail_error_code : error_code
{
fail_error_code() : error_code(make_error_code(error::fail_error))
{
}
template <class Arg0, class... ArgN>
fail_error_code(Arg0&& arg0, ArgN&&... argn)
: error_code(arg0, std::forward<ArgN>(argn)...)
{
}
};
/** A countdown to simulated failure.
On the Nth operation, the class will fail with the specified
error code, or the default error code of @ref error::fail_error.
*/
class fail_counter
{
std::size_t n_;
error_code ec_;
public:
fail_counter(fail_counter&&) = default;
/** Construct a counter.
@param The 0-based index of the operation to fail on or after.
*/
explicit fail_counter(
std::size_t n,
error_code ev = make_error_code(error::fail_error))
: n_(n), ec_(ev)
{
}
/// Throw an exception on the Nth failure
void
fail()
{
if (n_ > 0)
--n_;
if (!n_)
BOOST_THROW_EXCEPTION(system_error{ec_});
}
/// Set an error code on the Nth failure
bool
fail(error_code& ec)
{
if (n_ > 0)
--n_;
if (!n_)
{
ec = ec_;
return true;
}
ec.assign(0, ec.category());
return false;
}
};
} // namespace test
} // namespace beast
namespace boost {
namespace system {
template <>
struct is_error_code_enum<beast::test::error>
{
static bool const value = true;
};
} // namespace system
} // namespace boost
#endif

View File

@@ -1,182 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_TEST_FAIL_STREAM_HPP
#define BEAST_TEST_FAIL_STREAM_HPP
#include <xrpl/beast/core/async_result.hpp>
#include <xrpl/beast/core/bind_handler.hpp>
#include <xrpl/beast/core/detail/type_traits.hpp>
#include <xrpl/beast/core/error.hpp>
#include <xrpl/beast/test/fail_counter.hpp>
#include <xrpl/beast/websocket/teardown.hpp>
#include <boost/optional.hpp>
namespace beast {
namespace test {
/** A stream wrapper that fails.
On the Nth operation, the stream will fail with the specified
error code, or the default error code of invalid_argument.
*/
template <class NextLayer>
class fail_stream
{
boost::optional<fail_counter> fc_;
fail_counter* pfc_;
NextLayer next_layer_;
public:
using next_layer_type = typename std::remove_reference<NextLayer>::type;
using lowest_layer_type = typename get_lowest_layer<next_layer_type>::type;
fail_stream(fail_stream&&) = delete;
fail_stream(fail_stream const&) = delete;
fail_stream&
operator=(fail_stream&&) = delete;
fail_stream&
operator=(fail_stream const&) = delete;
template <class... Args>
explicit fail_stream(std::size_t n, Args&&... args)
: fc_(n), pfc_(&*fc_), next_layer_(std::forward<Args>(args)...)
{
}
template <class... Args>
explicit fail_stream(fail_counter& fc, Args&&... args)
: pfc_(&fc), next_layer_(std::forward<Args>(args)...)
{
}
next_layer_type&
next_layer()
{
return next_layer_;
}
lowest_layer_type&
lowest_layer()
{
return next_layer_.lowest_layer();
}
lowest_layer_type const&
lowest_layer() const
{
return next_layer_.lowest_layer();
}
boost::asio::io_service&
get_io_service()
{
return next_layer_.get_io_service();
}
template <class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers)
{
pfc_->fail();
return next_layer_.read_some(buffers);
}
template <class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers, error_code& ec)
{
if (pfc_->fail(ec))
return 0;
return next_layer_.read_some(buffers, ec);
}
template <class MutableBufferSequence, class ReadHandler>
async_return_type<ReadHandler, void(error_code, std::size_t)>
async_read_some(MutableBufferSequence const& buffers, ReadHandler&& handler)
{
error_code ec;
if (pfc_->fail(ec))
{
async_completion<ReadHandler, void(error_code, std::size_t)> init{
handler};
next_layer_.get_io_service().post(
bind_handler(init.completion_handler, ec, 0));
return init.result.get();
}
return next_layer_.async_read_some(
buffers, std::forward<ReadHandler>(handler));
}
template <class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers)
{
pfc_->fail();
return next_layer_.write_some(buffers);
}
template <class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers, error_code& ec)
{
if (pfc_->fail(ec))
return 0;
return next_layer_.write_some(buffers, ec);
}
template <class ConstBufferSequence, class WriteHandler>
async_return_type<WriteHandler, void(error_code, std::size_t)>
async_write_some(ConstBufferSequence const& buffers, WriteHandler&& handler)
{
error_code ec;
if (pfc_->fail(ec))
{
async_completion<WriteHandler, void(error_code, std::size_t)> init{
handler};
next_layer_.get_io_service().post(
bind_handler(init.completion_handler, ec, 0));
return init.result.get();
}
return next_layer_.async_write_some(
buffers, std::forward<WriteHandler>(handler));
}
friend void
teardown(
websocket::teardown_tag,
fail_stream<NextLayer>& stream,
boost::system::error_code& ec)
{
if (stream.pfc_->fail(ec))
return;
beast::websocket_helpers::call_teardown(stream.next_layer(), ec);
}
template <class TeardownHandler>
friend void
async_teardown(
websocket::teardown_tag,
fail_stream<NextLayer>& stream,
TeardownHandler&& handler)
{
error_code ec;
if (stream.pfc_->fail(ec))
{
stream.get_io_service().post(bind_handler(std::move(handler), ec));
return;
}
beast::websocket_helpers::call_async_teardown(
stream.next_layer(), std::forward<TeardownHandler>(handler));
}
};
} // namespace test
} // namespace beast
#endif

View File

@@ -1,482 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_TEST_PIPE_STREAM_HPP
#define BEAST_TEST_PIPE_STREAM_HPP
#include <xrpl/beast/core/async_result.hpp>
#include <xrpl/beast/core/bind_handler.hpp>
#include <xrpl/beast/core/flat_buffer.hpp>
#include <xrpl/beast/core/string.hpp>
#include <xrpl/beast/core/type_traits.hpp>
#include <xrpl/beast/test/fail_counter.hpp>
#include <xrpl/beast/websocket/teardown.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/assert.hpp>
#include <boost/throw_exception.hpp>
#include <condition_variable>
#include <limits>
#include <mutex>
#include <utility>
namespace beast {
namespace test {
/** A bidirectional in-memory communication channel
An instance of this class provides a client and server
endpoint that are automatically connected to each other
similarly to a connected socket.
Test pipes are used to facilitate writing unit tests
where the behavior of the transport is tightly controlled
to help illuminate all code paths (for code coverage)
*/
class pipe
{
public:
using buffer_type = flat_buffer;
private:
struct read_op
{
virtual ~read_op() = default;
virtual void
operator()() = 0;
};
struct state
{
std::mutex m;
buffer_type b;
std::condition_variable cv;
std::unique_ptr<read_op> op;
bool eof = false;
};
state s_[2];
public:
/** Represents an endpoint.
Each pipe has a client stream and a server stream.
*/
class stream
{
friend class pipe;
template <class Handler, class Buffers>
class read_op_impl;
state& in_;
state& out_;
boost::asio::io_service& ios_;
fail_counter* fc_ = nullptr;
std::size_t read_max_ = (std::numeric_limits<std::size_t>::max)();
std::size_t write_max_ = (std::numeric_limits<std::size_t>::max)();
stream(state& in, state& out, boost::asio::io_service& ios)
: in_(in), out_(out), ios_(ios), buffer(in_.b)
{
}
public:
using buffer_type = pipe::buffer_type;
/// Direct access to the underlying buffer
buffer_type& buffer;
/// Counts the number of read calls
std::size_t nread = 0;
/// Counts the number of write calls
std::size_t nwrite = 0;
~stream() = default;
stream(stream&&) = default;
/// Set the fail counter on the object
void
fail(fail_counter& fc)
{
fc_ = &fc;
}
/// Return the `io_service` associated with the object
boost::asio::io_service&
get_io_service()
{
return ios_;
}
/// Set the maximum number of bytes returned by read_some
void
read_size(std::size_t n)
{
read_max_ = n;
}
/// Set the maximum number of bytes returned by write_some
void
write_size(std::size_t n)
{
write_max_ = n;
}
/// Returns a string representing the pending input data
string_view
str() const
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
return {
buffer_cast<char const*>(*in_.b.data().begin()),
buffer_size(*in_.b.data().begin())};
}
/// Clear the buffer holding the input data
void
clear()
{
in_.b.consume((std::numeric_limits<std::size_t>::max)());
}
/** Close the stream.
The other end of the pipe will see
`boost::asio::error::eof` on read.
*/
template <class = void>
void
close();
template <class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers);
template <class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers, error_code& ec);
template <class MutableBufferSequence, class ReadHandler>
async_return_type<ReadHandler, void(error_code, std::size_t)>
async_read_some(
MutableBufferSequence const& buffers,
ReadHandler&& handler);
template <class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers);
template <class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers, error_code&);
template <class ConstBufferSequence, class WriteHandler>
async_return_type<WriteHandler, void(error_code, std::size_t)>
async_write_some(
ConstBufferSequence const& buffers,
WriteHandler&& handler);
friend void
teardown(
websocket::teardown_tag,
stream&,
boost::system::error_code& ec)
{
ec.assign(0, ec.category());
}
template <class TeardownHandler>
friend void
async_teardown(
websocket::teardown_tag,
stream& s,
TeardownHandler&& handler)
{
s.get_io_service().post(
bind_handler(std::move(handler), error_code{}));
}
};
/** Constructor.
The client and server endpoints will use the same `io_service`.
*/
explicit pipe(boost::asio::io_service& ios)
: client(s_[0], s_[1], ios), server(s_[1], s_[0], ios)
{
}
/** Constructor.
The client and server endpoints will different `io_service` objects.
*/
explicit pipe(boost::asio::io_service& ios1, boost::asio::io_service& ios2)
: client(s_[0], s_[1], ios1), server(s_[1], s_[0], ios2)
{
}
/// Represents the client endpoint
stream client;
/// Represents the server endpoint
stream server;
};
//------------------------------------------------------------------------------
template <class Handler, class Buffers>
class pipe::stream::read_op_impl : public pipe::read_op
{
stream& s_;
Buffers b_;
Handler h_;
public:
read_op_impl(stream& s, Buffers const& b, Handler&& h)
: s_(s), b_(b), h_(std::move(h))
{
}
read_op_impl(stream& s, Buffers const& b, Handler const& h)
: s_(s), b_(b), h_(h)
{
}
void
operator()() override;
};
//------------------------------------------------------------------------------
template <class Handler, class Buffers>
void
pipe::stream::read_op_impl<Handler, Buffers>::operator()()
{
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
s_.ios_.post([&]() {
BOOST_ASSERT(s_.in_.op);
std::unique_lock<std::mutex> lock{s_.in_.m};
if (s_.in_.b.size() > 0)
{
auto const bytes_transferred =
buffer_copy(b_, s_.in_.b.data(), s_.read_max_);
s_.in_.b.consume(bytes_transferred);
auto& s = s_;
Handler h{std::move(h_)};
lock.unlock();
s.in_.op.reset(nullptr);
++s.nread;
s.ios_.post(
bind_handler(std::move(h), error_code{}, bytes_transferred));
}
else
{
BOOST_ASSERT(s_.in_.eof);
auto& s = s_;
Handler h{std::move(h_)};
lock.unlock();
s.in_.op.reset(nullptr);
++s.nread;
s.ios_.post(bind_handler(std::move(h), boost::asio::error::eof, 0));
}
});
}
//------------------------------------------------------------------------------
template <class>
void
pipe::stream::close()
{
std::lock_guard lock{out_.m};
out_.eof = true;
if (out_.op)
out_.op.get()->operator()();
else
out_.cv.notify_all();
}
template <class MutableBufferSequence>
std::size_t
pipe::stream::read_some(MutableBufferSequence const& buffers)
{
static_assert(
is_mutable_buffer_sequence<MutableBufferSequence>::value,
"MutableBufferSequence requirements not met");
error_code ec;
auto const n = read_some(buffers, ec);
if (ec)
BOOST_THROW_EXCEPTION(system_error{ec});
return n;
}
template <class MutableBufferSequence>
std::size_t
pipe::stream::read_some(MutableBufferSequence const& buffers, error_code& ec)
{
static_assert(
is_mutable_buffer_sequence<MutableBufferSequence>::value,
"MutableBufferSequence requirements not met");
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
BOOST_ASSERT(!in_.op);
BOOST_ASSERT(buffer_size(buffers) > 0);
if (fc_ && fc_->fail(ec))
return 0;
std::unique_lock<std::mutex> lock{in_.m};
in_.cv.wait(lock, [&]() { return in_.b.size() > 0 || in_.eof; });
std::size_t bytes_transferred;
if (in_.b.size() > 0)
{
ec.assign(0, ec.category());
bytes_transferred = buffer_copy(buffers, in_.b.data(), read_max_);
in_.b.consume(bytes_transferred);
}
else
{
BOOST_ASSERT(in_.eof);
bytes_transferred = 0;
ec = boost::asio::error::eof;
}
++nread;
return bytes_transferred;
}
template <class MutableBufferSequence, class ReadHandler>
async_return_type<ReadHandler, void(error_code, std::size_t)>
pipe::stream::async_read_some(
MutableBufferSequence const& buffers,
ReadHandler&& handler)
{
static_assert(
is_mutable_buffer_sequence<MutableBufferSequence>::value,
"MutableBufferSequence requirements not met");
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
BOOST_ASSERT(!in_.op);
BOOST_ASSERT(buffer_size(buffers) > 0);
async_completion<ReadHandler, void(error_code, std::size_t)> init{handler};
if (fc_)
{
error_code ec;
if (fc_->fail(ec))
return ios_.post(bind_handler(init.completion_handler, ec, 0));
}
{
std::unique_lock<std::mutex> lock{in_.m};
if (in_.eof)
{
lock.unlock();
++nread;
ios_.post(bind_handler(
init.completion_handler, boost::asio::error::eof, 0));
}
else if (buffer_size(buffers) == 0 || buffer_size(in_.b.data()) > 0)
{
auto const bytes_transferred =
buffer_copy(buffers, in_.b.data(), read_max_);
in_.b.consume(bytes_transferred);
lock.unlock();
++nread;
ios_.post(bind_handler(
init.completion_handler, error_code{}, bytes_transferred));
}
else
{
in_.op.reset(
new read_op_impl<
handler_type<ReadHandler, void(error_code, std::size_t)>,
MutableBufferSequence>{
*this, buffers, init.completion_handler});
}
}
return init.result.get();
}
template <class ConstBufferSequence>
std::size_t
pipe::stream::write_some(ConstBufferSequence const& buffers)
{
static_assert(
is_const_buffer_sequence<ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
BOOST_ASSERT(!out_.eof);
error_code ec;
auto const bytes_transferred = write_some(buffers, ec);
if (ec)
BOOST_THROW_EXCEPTION(system_error{ec});
return bytes_transferred;
}
template <class ConstBufferSequence>
std::size_t
pipe::stream::write_some(ConstBufferSequence const& buffers, error_code& ec)
{
static_assert(
is_const_buffer_sequence<ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
BOOST_ASSERT(!out_.eof);
if (fc_ && fc_->fail(ec))
return 0;
auto const n = (std::min)(buffer_size(buffers), write_max_);
std::unique_lock<std::mutex> lock{out_.m};
auto const bytes_transferred = buffer_copy(out_.b.prepare(n), buffers);
out_.b.commit(bytes_transferred);
lock.unlock();
if (out_.op)
out_.op.get()->operator()();
else
out_.cv.notify_all();
++nwrite;
ec.assign(0, ec.category());
return bytes_transferred;
}
template <class ConstBufferSequence, class WriteHandler>
async_return_type<WriteHandler, void(error_code, std::size_t)>
pipe::stream::async_write_some(
ConstBufferSequence const& buffers,
WriteHandler&& handler)
{
static_assert(
is_const_buffer_sequence<ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
BOOST_ASSERT(!out_.eof);
async_completion<WriteHandler, void(error_code, std::size_t)> init{handler};
if (fc_)
{
error_code ec;
if (fc_->fail(ec))
return ios_.post(bind_handler(init.completion_handler, ec, 0));
}
auto const n = (std::min)(buffer_size(buffers), write_max_);
std::unique_lock<std::mutex> lock{out_.m};
auto const bytes_transferred = buffer_copy(out_.b.prepare(n), buffers);
out_.b.commit(bytes_transferred);
lock.unlock();
if (out_.op)
out_.op.get()->operator()();
else
out_.cv.notify_all();
++nwrite;
ios_.post(
bind_handler(init.completion_handler, error_code{}, bytes_transferred));
return init.result.get();
}
} // namespace test
} // namespace beast
#endif

View File

@@ -1,29 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_TEST_SIG_WAIT_HPP
#define BEAST_TEST_SIG_WAIT_HPP
#include <boost/asio.hpp>
namespace beast {
namespace test {
/// Block until SIGINT or SIGTERM is received.
inline void
sig_wait()
{
boost::asio::io_service ios;
boost::asio::signal_set signals(ios, SIGINT, SIGTERM);
signals.async_wait([&](boost::system::error_code const&, int) {});
ios.run();
}
} // namespace test
} // namespace beast
#endif

View File

@@ -1,166 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_TEST_STRING_IOSTREAM_HPP
#define BEAST_TEST_STRING_IOSTREAM_HPP
#include <xrpl/beast/core/async_result.hpp>
#include <xrpl/beast/core/bind_handler.hpp>
#include <xrpl/beast/core/buffer_prefix.hpp>
#include <xrpl/beast/core/error.hpp>
#include <xrpl/beast/websocket/teardown.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/throw_exception.hpp>
#include <string>
namespace beast {
namespace test {
/** A SyncStream and AsyncStream that reads from a string and writes to another
string.
This class behaves like a socket, except that written data is
appended to a string exposed as a public data member, and when
data is read it comes from a string provided at construction.
*/
class string_iostream
{
std::string s_;
boost::asio::const_buffer cb_;
boost::asio::io_service& ios_;
std::size_t read_max_;
public:
std::string str;
string_iostream(
boost::asio::io_service& ios,
std::string s,
std::size_t read_max = (std::numeric_limits<std::size_t>::max)())
: s_(std::move(s))
, cb_(boost::asio::buffer(s_))
, ios_(ios)
, read_max_(read_max)
{
}
boost::asio::io_service&
get_io_service()
{
return ios_;
}
template <class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers)
{
error_code ec;
auto const n = read_some(buffers, ec);
if (ec)
BOOST_THROW_EXCEPTION(system_error{ec});
return n;
}
template <class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers, error_code& ec)
{
auto const n =
boost::asio::buffer_copy(buffers, buffer_prefix(read_max_, cb_));
if (n > 0)
{
ec.assign(0, ec.category());
cb_ = cb_ + n;
}
else
{
ec = boost::asio::error::eof;
}
return n;
}
template <class MutableBufferSequence, class ReadHandler>
async_return_type<ReadHandler, void(error_code, std::size_t)>
async_read_some(MutableBufferSequence const& buffers, ReadHandler&& handler)
{
auto const n =
boost::asio::buffer_copy(buffers, boost::asio::buffer(s_));
error_code ec;
if (n > 0)
s_.erase(0, n);
else
ec = boost::asio::error::eof;
async_completion<ReadHandler, void(error_code, std::size_t)> init{
handler};
ios_.post(bind_handler(init.completion_handler, ec, n));
return init.result.get();
}
template <class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers)
{
error_code ec;
auto const n = write_some(buffers, ec);
if (ec)
BOOST_THROW_EXCEPTION(system_error{ec});
return n;
}
template <class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers, error_code& ec)
{
ec.assign(0, ec.category());
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
auto const n = buffer_size(buffers);
str.reserve(str.size() + n);
for (boost::asio::const_buffer buffer : buffers)
str.append(buffer_cast<char const*>(buffer), buffer_size(buffer));
return n;
}
template <class ConstBufferSequence, class WriteHandler>
async_return_type<WriteHandler, void(error_code, std::size_t)>
async_write_some(ConstBufferSequence const& buffers, WriteHandler&& handler)
{
error_code ec;
auto const bytes_transferred = write_some(buffers, ec);
async_completion<WriteHandler, void(error_code, std::size_t)> init{
handler};
get_io_service().post(
bind_handler(init.completion_handler, ec, bytes_transferred));
return init.result.get();
}
friend void
teardown(
websocket::teardown_tag,
string_iostream&,
boost::system::error_code& ec)
{
ec.assign(0, ec.category());
}
template <class TeardownHandler>
friend void
async_teardown(
websocket::teardown_tag,
string_iostream& stream,
TeardownHandler&& handler)
{
stream.get_io_service().post(
bind_handler(std::move(handler), error_code{}));
}
};
} // namespace test
} // namespace beast
#endif

View File

@@ -1,155 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_TEST_STRING_ISTREAM_HPP
#define BEAST_TEST_STRING_ISTREAM_HPP
#include <xrpl/beast/core/async_result.hpp>
#include <xrpl/beast/core/bind_handler.hpp>
#include <xrpl/beast/core/error.hpp>
#include <xrpl/beast/websocket/teardown.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/throw_exception.hpp>
#include <string>
namespace beast {
namespace test {
/** A SyncStream and AsyncStream that reads from a string.
This class behaves like a socket, except that written data is simply
discarded, and when data is read it comes from a string provided
at construction.
*/
class string_istream
{
std::string s_;
boost::asio::const_buffer cb_;
boost::asio::io_service& ios_;
std::size_t read_max_;
public:
string_istream(
boost::asio::io_service& ios,
std::string s,
std::size_t read_max = (std::numeric_limits<std::size_t>::max)())
: s_(std::move(s))
, cb_(boost::asio::buffer(s_))
, ios_(ios)
, read_max_(read_max)
{
}
boost::asio::io_service&
get_io_service()
{
return ios_;
}
template <class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers)
{
error_code ec;
auto const n = read_some(buffers, ec);
if (ec)
BOOST_THROW_EXCEPTION(system_error{ec});
return n;
}
template <class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers, error_code& ec)
{
auto const n = boost::asio::buffer_copy(buffers, cb_, read_max_);
if (n > 0)
{
ec.assign(0, ec.category());
cb_ = cb_ + n;
}
else
{
ec = boost::asio::error::eof;
}
return n;
}
template <class MutableBufferSequence, class ReadHandler>
async_return_type<ReadHandler, void(error_code, std::size_t)>
async_read_some(MutableBufferSequence const& buffers, ReadHandler&& handler)
{
auto const n =
boost::asio::buffer_copy(buffers, boost::asio::buffer(s_));
error_code ec;
if (n > 0)
s_.erase(0, n);
else
ec = boost::asio::error::eof;
async_completion<ReadHandler, void(error_code, std::size_t)> init{
handler};
ios_.post(bind_handler(init.completion_handler, ec, n));
return init.result.get();
}
template <class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers)
{
error_code ec;
auto const n = write_some(buffers, ec);
if (ec)
BOOST_THROW_EXCEPTION(system_error{ec});
return n;
}
template <class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers, error_code& ec)
{
ec.assign(0, ec.category());
return boost::asio::buffer_size(buffers);
}
template <class ConstBuffeSequence, class WriteHandler>
async_return_type<WriteHandler, void(error_code, std::size_t)>
async_write_some(ConstBuffeSequence const& buffers, WriteHandler&& handler)
{
async_completion<WriteHandler, void(error_code, std::size_t)> init{
handler};
ios_.post(bind_handler(
init.completion_handler,
error_code{},
boost::asio::buffer_size(buffers)));
return init.result.get();
}
friend void
teardown(
websocket::teardown_tag,
string_istream&,
boost::system::error_code& ec)
{
ec.assign(0, ec.category());
}
template <class TeardownHandler>
friend void
async_teardown(
websocket::teardown_tag,
string_istream& stream,
TeardownHandler&& handler)
{
stream.get_io_service().post(
bind_handler(std::move(handler), error_code{}));
}
};
} // namespace test
} // namespace beast
#endif

View File

@@ -1,137 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_TEST_STRING_OSTREAM_HPP
#define BEAST_TEST_STRING_OSTREAM_HPP
#include <xrpl/beast/core/async_result.hpp>
#include <xrpl/beast/core/bind_handler.hpp>
#include <xrpl/beast/core/buffer_prefix.hpp>
#include <xrpl/beast/core/error.hpp>
#include <xrpl/beast/websocket/teardown.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/throw_exception.hpp>
#include <string>
namespace beast {
namespace test {
class string_ostream
{
boost::asio::io_service& ios_;
std::size_t write_max_;
public:
std::string str;
explicit string_ostream(
boost::asio::io_service& ios,
std::size_t write_max = (std::numeric_limits<std::size_t>::max)())
: ios_(ios), write_max_(write_max)
{
}
boost::asio::io_service&
get_io_service()
{
return ios_;
}
template <class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers)
{
error_code ec;
auto const n = read_some(buffers, ec);
if (ec)
BOOST_THROW_EXCEPTION(system_error{ec});
return n;
}
template <class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const&, error_code& ec)
{
ec = boost::asio::error::eof;
return 0;
}
template <class MutableBufferSequence, class ReadHandler>
async_return_type<ReadHandler, void(error_code, std::size_t)>
async_read_some(MutableBufferSequence const&, ReadHandler&& handler)
{
async_completion<ReadHandler, void(error_code, std::size_t)> init{
handler};
ios_.post(
bind_handler(init.completion_handler, boost::asio::error::eof, 0));
return init.result.get();
}
template <class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers)
{
error_code ec;
auto const n = write_some(buffers, ec);
if (ec)
BOOST_THROW_EXCEPTION(system_error{ec});
return n;
}
template <class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers, error_code& ec)
{
ec.assign(0, ec.category());
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
auto const n = (std::min)(buffer_size(buffers), write_max_);
str.reserve(str.size() + n);
for (boost::asio::const_buffer buffer : buffer_prefix(n, buffers))
str.append(buffer_cast<char const*>(buffer), buffer_size(buffer));
return n;
}
template <class ConstBufferSequence, class WriteHandler>
async_return_type<WriteHandler, void(error_code, std::size_t)>
async_write_some(ConstBufferSequence const& buffers, WriteHandler&& handler)
{
error_code ec;
auto const bytes_transferred = write_some(buffers, ec);
async_completion<WriteHandler, void(error_code, std::size_t)> init{
handler};
get_io_service().post(
bind_handler(init.completion_handler, ec, bytes_transferred));
return init.result.get();
}
friend void
teardown(
websocket::teardown_tag,
string_ostream&,
boost::system::error_code& ec)
{
ec.assign(0, ec.category());
}
template <class TeardownHandler>
friend void
async_teardown(
websocket::teardown_tag,
string_ostream& stream,
TeardownHandler&& handler)
{
stream.get_io_service().post(
bind_handler(std::move(handler), error_code{}));
}
};
} // namespace test
} // namespace beast
#endif

View File

@@ -1,159 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_TEST_TEST_ALLOCATOR_HPP
#define BEAST_TEST_TEST_ALLOCATOR_HPP
#include <atomic>
#include <cstddef>
#include <memory>
namespace beast {
namespace test {
struct test_allocator_info
{
std::size_t id;
std::size_t ncopy = 0;
std::size_t nmove = 0;
std::size_t nmassign = 0;
std::size_t ncpassign = 0;
std::size_t nselect = 0;
test_allocator_info()
: id([] {
static std::atomic<std::size_t> sid(0);
return ++sid;
}())
{
}
};
template <class T, bool Equal, bool Assign, bool Move, bool Swap, bool Select>
class test_allocator;
template <class T, bool Equal, bool Assign, bool Move, bool Swap, bool Select>
struct test_allocator_base
{
};
template <class T, bool Equal, bool Assign, bool Move, bool Swap>
struct test_allocator_base<T, Equal, Assign, Move, Swap, true>
{
static test_allocator<T, Equal, Assign, Move, Swap, true>
select_on_container_copy_construction(
test_allocator<T, Equal, Assign, Move, Swap, true> const& a)
{
return test_allocator<T, Equal, Assign, Move, Swap, true>{};
}
};
template <class T, bool Equal, bool Assign, bool Move, bool Swap, bool Select>
class test_allocator
: public test_allocator_base<T, Equal, Assign, Move, Swap, Select>
{
std::shared_ptr<test_allocator_info> info_;
template <class, bool, bool, bool, bool, bool>
friend class test_allocator;
public:
using value_type = T;
using propagate_on_container_copy_assignment =
std::integral_constant<bool, Assign>;
using propagate_on_container_move_assignment =
std::integral_constant<bool, Move>;
using propagate_on_container_swap = std::integral_constant<bool, Swap>;
template <class U>
struct rebind
{
using other = test_allocator<U, Equal, Assign, Move, Swap, Select>;
};
test_allocator() : info_(std::make_shared<test_allocator_info>())
{
}
test_allocator(test_allocator const& u) noexcept : info_(u.info_)
{
++info_->ncopy;
}
template <class U>
test_allocator(
test_allocator<U, Equal, Assign, Move, Swap, Select> const& u) noexcept
: info_(u.info_)
{
++info_->ncopy;
}
test_allocator(test_allocator&& t) : info_(t.info_)
{
++info_->nmove;
}
test_allocator&
operator=(test_allocator const& u) noexcept
{
info_ = u.info_;
++info_->ncpassign;
return *this;
}
test_allocator&
operator=(test_allocator&& u) noexcept
{
info_ = u.info_;
++info_->nmassign;
return *this;
}
value_type*
allocate(std::size_t n)
{
return static_cast<value_type*>(::operator new(n * sizeof(value_type)));
}
void
deallocate(value_type* p, std::size_t) noexcept
{
::operator delete(p);
}
bool
operator==(test_allocator const& other) const
{
return id() == other.id() || Equal;
}
bool
operator!=(test_allocator const& other) const
{
return !this->operator==(other);
}
std::size_t
id() const
{
return info_->id;
}
test_allocator_info const*
operator->() const
{
return info_.get();
}
};
} // namespace test
} // namespace beast
#endif

View File

@@ -1,121 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_UNIT_TEST_DSTREAM_HPP
#define BEAST_UNIT_TEST_DSTREAM_HPP
#include <boost/config.hpp>
#include <ios>
#include <memory>
#include <ostream>
#include <streambuf>
#include <string>
#ifdef BOOST_WINDOWS
#include <boost/detail/winapi/basic_types.hpp>
//#include <boost/detail/winapi/debugapi.hpp>
#endif
namespace beast {
namespace unit_test {
#ifdef BOOST_WINDOWS
namespace detail {
template <class CharT, class Traits, class Allocator>
class dstream_buf : public std::basic_stringbuf<CharT, Traits, Allocator>
{
using ostream = std::basic_ostream<CharT, Traits>;
bool dbg_;
ostream& os_;
template <class T>
void
write(T const*) = delete;
void
write(char const* s)
{
if (dbg_)
/*boost::detail::winapi*/ ::OutputDebugStringA(s);
os_ << s;
}
void
write(wchar_t const* s)
{
if (dbg_)
/*boost::detail::winapi*/ ::OutputDebugStringW(s);
os_ << s;
}
public:
explicit dstream_buf(ostream& os)
: os_(os), dbg_(/*boost::detail::winapi*/ ::IsDebuggerPresent() != 0)
{
}
~dstream_buf()
{
sync();
}
int
sync() override
{
write(this->str().c_str());
this->str("");
return 0;
}
};
} // namespace detail
/** std::ostream with Visual Studio IDE redirection.
Instances of this stream wrap a specified `std::ostream`
(such as `std::cout` or `std::cerr`). If the IDE debugger
is attached when the stream is created, output will be
additionally copied to the Visual Studio Output window.
*/
template <
class CharT,
class Traits = std::char_traits<CharT>,
class Allocator = std::allocator<CharT>>
class basic_dstream : public std::basic_ostream<CharT, Traits>
{
detail::dstream_buf<CharT, Traits, Allocator> buf_;
public:
/** Construct a stream.
@param os The output stream to wrap.
*/
explicit basic_dstream(std::ostream& os)
: std::basic_ostream<CharT, Traits>(&buf_), buf_(os)
{
if (os.flags() & std::ios::unitbuf)
std::unitbuf(*this);
}
};
using dstream = basic_dstream<char>;
using dwstream = basic_dstream<wchar_t>;
#else
using dstream = std::ostream&;
using dwstream = std::wostream&;
#endif
} // namespace unit_test
} // namespace beast
#endif

View File

@@ -1,72 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
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 BEAST_UTILITY_HASH_PAIR_H_INCLUDED
#define BEAST_UTILITY_HASH_PAIR_H_INCLUDED
#include <functional>
#include <utility>
#include <boost/functional/hash.hpp>
#include <boost/utility/base_from_member.hpp>
namespace std {
/** Specialization of std::hash for any std::pair type. */
template <class First, class Second>
struct hash<std::pair<First, Second>>
: private boost::base_from_member<std::hash<First>, 0>,
private boost::base_from_member<std::hash<Second>, 1>
{
private:
using first_hash = boost::base_from_member<std::hash<First>, 0>;
using second_hash = boost::base_from_member<std::hash<Second>, 1>;
public:
hash()
{
}
hash(
std::hash<First> const& first_hash_,
std::hash<Second> const& second_hash_)
: first_hash(first_hash_), second_hash(second_hash_)
{
}
std::size_t
operator()(std::pair<First, Second> const& value)
{
std::size_t result(first_hash::member(value.first));
boost::hash_combine(result, second_hash::member(value.second));
return result;
}
std::size_t
operator()(std::pair<First, Second> const& value) const
{
std::size_t result(first_hash::member(value.first));
boost::hash_combine(result, second_hash::member(value.second));
return result;
}
};
} // namespace std
#endif

View File

@@ -26,7 +26,6 @@
#include <xrpld/peerfinder/detail/Fixed.h> #include <xrpld/peerfinder/detail/Fixed.h>
#include <xrpld/peerfinder/detail/Handouts.h> #include <xrpld/peerfinder/detail/Handouts.h>
#include <xrpld/peerfinder/detail/Livecache.h> #include <xrpld/peerfinder/detail/Livecache.h>
#include <xrpld/peerfinder/detail/Reporting.h>
#include <xrpld/peerfinder/detail/SlotImp.h> #include <xrpld/peerfinder/detail/SlotImp.h>
#include <xrpld/peerfinder/detail/Source.h> #include <xrpld/peerfinder/detail/Source.h>
#include <xrpld/peerfinder/detail/Store.h> #include <xrpld/peerfinder/detail/Store.h>

View File

@@ -1,49 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_PEERFINDER_REPORTING_H_INCLUDED
#define RIPPLE_PEERFINDER_REPORTING_H_INCLUDED
namespace ripple {
namespace PeerFinder {
/** Severity levels for test reporting.
This allows more fine grained control over reporting for diagnostics.
*/
struct Reporting
{
explicit Reporting() = default;
// Report simulation parameters
static bool const params = true;
// Report simulation crawl time-evolution
static bool const crawl = true;
// Report nodes aggregate statistics
static bool const nodes = true;
// Report nodes detailed information
static bool const dump_nodes = false;
};
} // namespace PeerFinder
} // namespace ripple
#endif

View File

@@ -1,100 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_PEERFINDER_SIM_FUNCTIONQUEUE_H_INCLUDED
#define RIPPLE_PEERFINDER_SIM_FUNCTIONQUEUE_H_INCLUDED
namespace ripple {
namespace PeerFinder {
namespace Sim {
/** Maintains a queue of functors that can be called later. */
class FunctionQueue
{
public:
explicit FunctionQueue() = default;
private:
class BasicWork
{
public:
virtual ~BasicWork()
{
}
virtual void
operator()() = 0;
};
template <typename Function>
class Work : public BasicWork
{
public:
explicit Work(Function f) : m_f(f)
{
}
void
operator()()
{
(m_f)();
}
private:
Function m_f;
};
std::list<std::unique_ptr<BasicWork>> m_work;
public:
/** Returns `true` if there is no remaining work */
bool
empty()
{
return m_work.empty();
}
/** Queue a function.
Function must be callable with this signature:
void (void)
*/
template <typename Function>
void
post(Function f)
{
m_work.emplace_back(std::make_unique<Work<Function>>(f));
}
/** Run all pending functions.
The functions will be invoked in the order they were queued.
*/
void
run()
{
while (!m_work.empty())
{
(*m_work.front())();
m_work.pop_front();
}
}
};
} // namespace Sim
} // namespace PeerFinder
} // namespace ripple
#endif

View File

@@ -1,76 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_PEERFINDER_SIM_GRAPHALGORITHMS_H_INCLUDED
#define RIPPLE_PEERFINDER_SIM_GRAPHALGORITHMS_H_INCLUDED
namespace ripple {
namespace PeerFinder {
namespace Sim {
template <typename Vertex>
struct VertexTraits;
/** Call a function for each vertex in a connected graph.
Function will be called with this signature:
void (Vertex&, std::size_t diameter);
*/
template <typename Vertex, typename Function>
void
breadth_first_traverse(Vertex& start, Function f)
{
using Traits = VertexTraits<Vertex>;
using Edges = typename Traits::Edges;
using Edge = typename Traits::Edge;
using Probe = std::pair<Vertex*, int>;
using Work = std::deque<Probe>;
using Visited = std::set<Vertex*>;
Work work;
Visited visited;
work.emplace_back(&start, 0);
int diameter(0);
while (!work.empty())
{
Probe const p(work.front());
work.pop_front();
if (visited.find(p.first) != visited.end())
continue;
diameter = std::max(p.second, diameter);
visited.insert(p.first);
for (typename Edges::iterator iter(Traits::edges(*p.first).begin());
iter != Traits::edges(*p.first).end();
++iter)
{
Vertex* v(Traits::vertex(*iter));
if (visited.find(v) != visited.end())
continue;
if (!iter->closed())
work.emplace_back(v, p.second + 1);
}
f(*p.first, diameter);
}
}
} // namespace Sim
} // namespace PeerFinder
} // namespace ripple
#endif

View File

@@ -1,47 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_PEERFINDER_SIM_MESSAGE_H_INCLUDED
#define RIPPLE_PEERFINDER_SIM_MESSAGE_H_INCLUDED
namespace ripple {
namespace PeerFinder {
namespace Sim {
class Message
{
public:
explicit Message(Endpoints const& endpoints) : m_payload(endpoints)
{
}
Endpoints const&
payload() const
{
return m_payload;
}
private:
Endpoints m_payload;
};
} // namespace Sim
} // namespace PeerFinder
} // namespace ripple
#endif

View File

@@ -1,37 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_PEERFINDER_SIM_NODESNAPSHOT_H_INCLUDED
#define RIPPLE_PEERFINDER_SIM_NODESNAPSHOT_H_INCLUDED
namespace ripple {
namespace PeerFinder {
namespace Sim {
/** A snapshot of a Node in the network simulator. */
struct NodeSnapshot
{
explicit NodeSnapshot() = default;
};
} // namespace Sim
} // namespace PeerFinder
} // namespace ripple
#endif

View File

@@ -1,45 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_PEERFINDER_SIM_PARAMS_H_INCLUDED
#define RIPPLE_PEERFINDER_SIM_PARAMS_H_INCLUDED
namespace ripple {
namespace PeerFinder {
namespace Sim {
/** Defines the parameters for a network simulation. */
struct Params
{
Params() : steps(50), nodes(10), maxPeers(20), outPeers(9.5), firewalled(0)
{
}
int steps;
int nodes;
int maxPeers;
double outPeers;
double firewalled; // [0, 1)
};
} // namespace Sim
} // namespace PeerFinder
} // namespace ripple
#endif

View File

@@ -1,87 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_PEERFINDER_SIM_PREDICATES_H_INCLUDED
#define RIPPLE_PEERFINDER_SIM_PREDICATES_H_INCLUDED
namespace ripple {
namespace PeerFinder {
namespace Sim {
/** UnaryPredicate, returns `true` if the 'to' node on a Link matches. */
/** @{ */
template <typename Node>
class is_remote_node_pred
{
public:
is_remote_node_pred(Node const& n) : node(n)
{
}
template <typename Link>
bool
operator()(Link const& l) const
{
return &node == &l.remote_node();
}
private:
Node const& node;
};
template <typename Node>
is_remote_node_pred<Node>
is_remote_node(Node const& node)
{
return is_remote_node_pred<Node>(node);
}
template <typename Node>
is_remote_node_pred<Node>
is_remote_node(Node const* node)
{
return is_remote_node_pred<Node>(*node);
}
/** @} */
//------------------------------------------------------------------------------
/** UnaryPredicate, `true` if the remote address matches. */
class is_remote_endpoint
{
public:
explicit is_remote_endpoint(beast::IP::Endpoint const& address)
: m_endpoint(address)
{
}
template <typename Link>
bool
operator()(Link const& link) const
{
return link.remote_endpoint() == m_endpoint;
}
private:
beast::IP::Endpoint const m_endpoint;
};
} // namespace Sim
} // namespace PeerFinder
} // namespace ripple
#endif