Rearrange sources (#4997)

This commit is contained in:
Pretty Printer
2024-06-20 09:22:15 -05:00
committed by John Freeman
parent 2e902dee53
commit e416ee72ca
994 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,41 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2018 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_BASICS_ARCHIVE_H_INCLUDED
#define RIPPLE_BASICS_ARCHIVE_H_INCLUDED
#include <boost/filesystem.hpp>
namespace ripple {
/** Extract a tar archive compressed with lz4
@param src the path of the archive to be extracted
@param dst the directory to extract to
@throws runtime_error
*/
void
extractTarLz4(
boost::filesystem::path const& src,
boost::filesystem::path const& dst);
} // namespace ripple
#endif

View File

@@ -0,0 +1,402 @@
//------------------------------------------------------------------------------
/*
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_BASICS_BASICCONFIG_H_INCLUDED
#define RIPPLE_BASICS_BASICCONFIG_H_INCLUDED
#include <ripple/basics/contract.h>
#include <boost/beast/core/string.hpp>
#include <boost/lexical_cast.hpp>
#include <algorithm>
#include <map>
#include <optional>
#include <ostream>
#include <string>
#include <vector>
namespace ripple {
using IniFileSections = std::map<std::string, std::vector<std::string>>;
//------------------------------------------------------------------------------
/** Holds a collection of configuration values.
A configuration file contains zero or more sections.
*/
class Section
{
private:
std::string name_;
std::map<std::string, std::string> lookup_;
std::vector<std::string> lines_;
std::vector<std::string> values_;
bool had_trailing_comments_ = false;
using const_iterator = decltype(lookup_)::const_iterator;
public:
/** Create an empty section. */
explicit Section(std::string const& name = "");
/** Returns the name of this section. */
std::string const&
name() const
{
return name_;
}
/** Returns all the lines in the section.
This includes everything.
*/
std::vector<std::string> const&
lines() const
{
return lines_;
}
/** Returns all the values in the section.
Values are non-empty lines which are not key/value pairs.
*/
std::vector<std::string> const&
values() const
{
return values_;
}
/**
* Set the legacy value for this section.
*/
void
legacy(std::string value)
{
if (lines_.empty())
lines_.emplace_back(std::move(value));
else
lines_[0] = std::move(value);
}
/**
* Get the legacy value for this section.
*
* @return The retrieved value. A section with an empty legacy value returns
an empty string.
*/
std::string
legacy() const
{
if (lines_.empty())
return "";
if (lines_.size() > 1)
Throw<std::runtime_error>(
"A legacy value must have exactly one line. Section: " + name_);
return lines_[0];
}
/** Set a key/value pair.
The previous value is discarded.
*/
void
set(std::string const& key, std::string const& value);
/** Append a set of lines to this section.
Lines containing key/value pairs are added to the map,
else they are added to the values list. Everything is
added to the lines list.
*/
void
append(std::vector<std::string> const& lines);
/** Append a line to this section. */
void
append(std::string const& line)
{
append(std::vector<std::string>{line});
}
/** Returns `true` if a key with the given name exists. */
bool
exists(std::string const& name) const;
template <class T = std::string>
std::optional<T>
get(std::string const& name) const
{
auto const iter = lookup_.find(name);
if (iter == lookup_.end())
return std::nullopt;
return boost::lexical_cast<T>(iter->second);
}
/// Returns a value if present, else another value.
template <class T>
T
value_or(std::string const& name, T const& other) const
{
auto const v = get<T>(name);
return v.has_value() ? *v : other;
}
// indicates if trailing comments were seen
// during the appending of any lines/values
bool
had_trailing_comments() const
{
return had_trailing_comments_;
}
friend std::ostream&
operator<<(std::ostream&, Section const& section);
// Returns `true` if there are no key/value pairs.
bool
empty() const
{
return lookup_.empty();
}
// Returns the number of key/value pairs.
std::size_t
size() const
{
return lookup_.size();
}
// For iteration of key/value pairs.
const_iterator
begin() const
{
return lookup_.cbegin();
}
// For iteration of key/value pairs.
const_iterator
cbegin() const
{
return lookup_.cbegin();
}
// For iteration of key/value pairs.
const_iterator
end() const
{
return lookup_.cend();
}
// For iteration of key/value pairs.
const_iterator
cend() const
{
return lookup_.cend();
}
};
//------------------------------------------------------------------------------
/** Holds unparsed configuration information.
The raw data sections are processed with intermediate parsers specific
to each module instead of being all parsed in a central location.
*/
class BasicConfig
{
private:
std::map<std::string, Section, boost::beast::iless> map_;
public:
/** Returns `true` if a section with the given name exists. */
bool
exists(std::string const& name) const;
/** Returns the section with the given name.
If the section does not exist, an empty section is returned.
*/
/** @{ */
Section&
section(std::string const& name);
Section const&
section(std::string const& name) const;
Section const&
operator[](std::string const& name) const
{
return section(name);
}
Section&
operator[](std::string const& name)
{
return section(name);
}
/** @} */
/** Overwrite a key/value pair with a command line argument
If the section does not exist it is created.
The previous value, if any, is overwritten.
*/
void
overwrite(
std::string const& section,
std::string const& key,
std::string const& value);
/** Remove all the key/value pairs from the section.
*/
void
deprecatedClearSection(std::string const& section);
/**
* Set a value that is not a key/value pair.
*
* The value is stored as the section's first value and may be retrieved
* through section::legacy.
*
* @param section Name of the section to modify.
* @param value Contents of the legacy value.
*/
void
legacy(std::string const& section, std::string value);
/**
* Get the legacy value of a section. A section with a
* single-line value may be retrieved as a legacy value.
*
* @param sectionName Retrieve the contents of this section's
* legacy value.
* @return Contents of the legacy value.
*/
std::string
legacy(std::string const& sectionName) const;
friend std::ostream&
operator<<(std::ostream& ss, BasicConfig const& c);
// indicates if trailing comments were seen
// in any loaded Sections
bool
had_trailing_comments() const
{
return std::any_of(map_.cbegin(), map_.cend(), [](auto s) {
return s.second.had_trailing_comments();
});
}
protected:
void
build(IniFileSections const& ifs);
};
//------------------------------------------------------------------------------
/** Set a value from a configuration Section
If the named value is not found or doesn't parse as a T,
the variable is unchanged.
@return `true` if value was set.
*/
template <class T>
bool
set(T& target, std::string const& name, Section const& section)
{
bool found_and_valid = false;
try
{
auto const val = section.get<T>(name);
if ((found_and_valid = val.has_value()))
target = *val;
}
catch (boost::bad_lexical_cast&)
{
}
return found_and_valid;
}
/** Set a value from a configuration Section
If the named value is not found or doesn't cast to T,
the variable is assigned the default.
@return `true` if the named value was found and is valid.
*/
template <class T>
bool
set(T& target,
T const& defaultValue,
std::string const& name,
Section const& section)
{
bool found_and_valid = set<T>(target, name, section);
if (!found_and_valid)
target = defaultValue;
return found_and_valid;
}
/** Retrieve a key/value pair from a section.
@return The value string converted to T if it exists
and can be parsed, or else defaultValue.
*/
// NOTE This routine might be more clumsy than the previous two
template <class T = std::string>
T
get(Section const& section,
std::string const& name,
T const& defaultValue = T{})
{
try
{
return section.value_or<T>(name, defaultValue);
}
catch (boost::bad_lexical_cast&)
{
}
return defaultValue;
}
inline std::string
get(Section const& section, std::string const& name, const char* defaultValue)
{
try
{
auto const val = section.get(name);
if (val.has_value())
return *val;
}
catch (boost::bad_lexical_cast&)
{
}
return defaultValue;
}
template <class T>
bool
get_if_exists(Section const& section, std::string const& name, T& v)
{
return set<T>(v, name, section);
}
template <>
inline bool
get_if_exists<bool>(Section const& section, std::string const& name, bool& v)
{
int intVal = 0;
auto stat = get_if_exists(section, name, intVal);
if (stat)
v = bool(intVal);
return stat;
}
} // namespace ripple
#endif

View File

@@ -0,0 +1,34 @@
//------------------------------------------------------------------------------
/*
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_BASICS_BLOB_H_INCLUDED
#define RIPPLE_BASICS_BLOB_H_INCLUDED
#include <vector>
namespace ripple {
/** Storage for linear binary data.
Blocks of binary data appear often in various idioms and structures.
*/
using Blob = std::vector<unsigned char>;
} // namespace ripple
#endif

View File

@@ -0,0 +1,238 @@
//------------------------------------------------------------------------------
/*
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_BASICS_BUFFER_H_INCLUDED
#define RIPPLE_BASICS_BUFFER_H_INCLUDED
#include <ripple/basics/Slice.h>
#include <cassert>
#include <cstdint>
#include <cstring>
#include <memory>
#include <utility>
namespace ripple {
/** Like std::vector<char> but better.
Meets the requirements of BufferFactory.
*/
class Buffer
{
private:
std::unique_ptr<std::uint8_t[]> p_;
std::size_t size_ = 0;
public:
using const_iterator = std::uint8_t const*;
Buffer() = default;
/** Create an uninitialized buffer with the given size. */
explicit Buffer(std::size_t size)
: p_(size ? new std::uint8_t[size] : nullptr), size_(size)
{
}
/** Create a buffer as a copy of existing memory.
@param data a pointer to the existing memory. If
size is non-zero, it must not be null.
@param size size of the existing memory block.
*/
Buffer(void const* data, std::size_t size) : Buffer(size)
{
if (size)
std::memcpy(p_.get(), data, size);
}
/** Copy-construct */
Buffer(Buffer const& other) : Buffer(other.p_.get(), other.size_)
{
}
/** Copy assign */
Buffer&
operator=(Buffer const& other)
{
if (this != &other)
{
if (auto p = alloc(other.size_))
std::memcpy(p, other.p_.get(), size_);
}
return *this;
}
/** Move-construct.
The other buffer is reset.
*/
Buffer(Buffer&& other) noexcept
: p_(std::move(other.p_)), size_(other.size_)
{
other.size_ = 0;
}
/** Move-assign.
The other buffer is reset.
*/
Buffer&
operator=(Buffer&& other) noexcept
{
if (this != &other)
{
p_ = std::move(other.p_);
size_ = other.size_;
other.size_ = 0;
}
return *this;
}
/** Construct from a slice */
explicit Buffer(Slice s) : Buffer(s.data(), s.size())
{
}
/** Assign from slice */
Buffer&
operator=(Slice s)
{
// Ensure the slice isn't a subset of the buffer.
assert(
s.size() == 0 || size_ == 0 || s.data() < p_.get() ||
s.data() >= p_.get() + size_);
if (auto p = alloc(s.size()))
std::memcpy(p, s.data(), s.size());
return *this;
}
/** Returns the number of bytes in the buffer. */
std::size_t
size() const noexcept
{
return size_;
}
bool
empty() const noexcept
{
return 0 == size_;
}
operator Slice() const noexcept
{
if (!size_)
return Slice{};
return Slice{p_.get(), size_};
}
/** Return a pointer to beginning of the storage.
@note The return type is guaranteed to be a pointer
to a single byte, to facilitate pointer arithmetic.
*/
/** @{ */
std::uint8_t const*
data() const noexcept
{
return p_.get();
}
std::uint8_t*
data() noexcept
{
return p_.get();
}
/** @} */
/** Reset the buffer.
All memory is deallocated. The resulting size is 0.
*/
void
clear() noexcept
{
p_.reset();
size_ = 0;
}
/** Reallocate the storage.
Existing data, if any, is discarded.
*/
std::uint8_t*
alloc(std::size_t n)
{
if (n != size_)
{
p_.reset(n ? new std::uint8_t[n] : nullptr);
size_ = n;
}
return p_.get();
}
// Meet the requirements of BufferFactory
void*
operator()(std::size_t n)
{
return alloc(n);
}
const_iterator
begin() const noexcept
{
return p_.get();
}
const_iterator
cbegin() const noexcept
{
return p_.get();
}
const_iterator
end() const noexcept
{
return p_.get() + size_;
}
const_iterator
cend() const noexcept
{
return p_.get() + size_;
}
};
inline bool
operator==(Buffer const& lhs, Buffer const& rhs) noexcept
{
if (lhs.size() != rhs.size())
return false;
if (lhs.size() == 0)
return true;
return std::memcmp(lhs.data(), rhs.data(), lhs.size()) == 0;
}
inline bool
operator!=(Buffer const& lhs, Buffer const& rhs) noexcept
{
return !(lhs == rhs);
}
} // namespace ripple
#endif

View File

@@ -0,0 +1,43 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2018 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_BASICS_BYTEUTILITIES_H_INCLUDED
#define RIPPLE_BASICS_BYTEUTILITIES_H_INCLUDED
namespace ripple {
template <class T>
constexpr auto
kilobytes(T value) noexcept
{
return value * 1024;
}
template <class T>
constexpr auto
megabytes(T value) noexcept
{
return kilobytes(kilobytes(value));
}
static_assert(kilobytes(2) == 2048, "kilobytes(2) == 2048");
static_assert(megabytes(3) == 3145728, "megabytes(3) == 3145728");
} // namespace ripple
#endif

View File

@@ -0,0 +1,166 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2020 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 RIPPLED_COMPRESSIONALGORITHMS_H_INCLUDED
#define RIPPLED_COMPRESSIONALGORITHMS_H_INCLUDED
#include <ripple/basics/contract.h>
#include <algorithm>
#include <cstdint>
#include <lz4.h>
#include <stdexcept>
#include <vector>
namespace ripple {
namespace compression_algorithms {
/** LZ4 block compression.
* @tparam BufferFactory Callable object or lambda.
* Takes the requested buffer size and returns allocated buffer pointer.
* @param in Data to compress
* @param inSize Size of the data
* @param bf Compressed buffer allocator
* @return Size of compressed data, or zero if failed to compress
*/
template <typename BufferFactory>
std::size_t
lz4Compress(void const* in, std::size_t inSize, BufferFactory&& bf)
{
if (inSize > UINT32_MAX)
Throw<std::runtime_error>("lz4 compress: invalid size");
auto const outCapacity = LZ4_compressBound(inSize);
// Request the caller to allocate and return the buffer to hold compressed
// data
auto compressed = bf(outCapacity);
auto compressedSize = LZ4_compress_default(
reinterpret_cast<const char*>(in),
reinterpret_cast<char*>(compressed),
inSize,
outCapacity);
if (compressedSize == 0)
Throw<std::runtime_error>("lz4 compress: failed");
return compressedSize;
}
/**
* @param in Compressed data
* @param inSizeUnchecked Size of compressed data
* @param decompressed Buffer to hold decompressed data
* @param decompressedSizeUnchecked Size of the decompressed buffer
* @return size of the decompressed data
*/
inline std::size_t
lz4Decompress(
std::uint8_t const* in,
std::size_t inSizeUnchecked,
std::uint8_t* decompressed,
std::size_t decompressedSizeUnchecked)
{
int const inSize = static_cast<int>(inSizeUnchecked);
int const decompressedSize = static_cast<int>(decompressedSizeUnchecked);
if (inSize <= 0)
Throw<std::runtime_error>("lz4Decompress: integer overflow (input)");
if (decompressedSize <= 0)
Throw<std::runtime_error>("lz4Decompress: integer overflow (output)");
if (LZ4_decompress_safe(
reinterpret_cast<const char*>(in),
reinterpret_cast<char*>(decompressed),
inSize,
decompressedSize) != decompressedSize)
Throw<std::runtime_error>("lz4Decompress: failed");
return decompressedSize;
}
/** LZ4 block decompression.
* @tparam InputStream ZeroCopyInputStream
* @param in Input source stream
* @param inSize Size of compressed data
* @param decompressed Buffer to hold decompressed data
* @param decompressedSize Size of the decompressed buffer
* @return size of the decompressed data
*/
template <typename InputStream>
std::size_t
lz4Decompress(
InputStream& in,
std::size_t inSize,
std::uint8_t* decompressed,
std::size_t decompressedSize)
{
std::vector<std::uint8_t> compressed;
std::uint8_t const* chunk = nullptr;
int chunkSize = 0;
int copiedInSize = 0;
auto const currentBytes = in.ByteCount();
// Use the first chunk if it is >= inSize bytes of the compressed message.
// Otherwise copy inSize bytes of chunks into compressed buffer and
// use the buffer to decompress.
while (in.Next(reinterpret_cast<void const**>(&chunk), &chunkSize))
{
if (copiedInSize == 0)
{
if (chunkSize >= inSize)
{
copiedInSize = inSize;
break;
}
compressed.resize(inSize);
}
chunkSize = chunkSize < (inSize - copiedInSize)
? chunkSize
: (inSize - copiedInSize);
std::copy(chunk, chunk + chunkSize, compressed.data() + copiedInSize);
copiedInSize += chunkSize;
if (copiedInSize == inSize)
{
chunk = compressed.data();
break;
}
}
// Put back unused bytes
if (in.ByteCount() > (currentBytes + copiedInSize))
in.BackUp(in.ByteCount() - currentBytes - copiedInSize);
if ((copiedInSize == 0 && chunkSize < inSize) ||
(copiedInSize > 0 && copiedInSize != inSize))
Throw<std::runtime_error>("lz4 decompress: insufficient input size");
return lz4Decompress(chunk, inSize, decompressed, decompressedSize);
}
} // namespace compression_algorithms
} // namespace ripple
#endif // RIPPLED_COMPRESSIONALGORITHMS_H_INCLUDED

View File

@@ -0,0 +1,156 @@
//------------------------------------------------------------------------------
/*
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_BASICS_COUNTEDOBJECT_H_INCLUDED
#define RIPPLE_BASICS_COUNTEDOBJECT_H_INCLUDED
#include <ripple/beast/type_name.h>
#include <atomic>
#include <string>
#include <utility>
#include <vector>
namespace ripple {
/** Manages all counted object types. */
class CountedObjects
{
public:
static CountedObjects&
getInstance() noexcept;
using Entry = std::pair<std::string, int>;
using List = std::vector<Entry>;
List
getCounts(int minimumThreshold) const;
public:
/** Implementation for @ref CountedObject.
@internal
*/
class Counter
{
public:
Counter(std::string name) noexcept : name_(std::move(name)), count_(0)
{
// Insert ourselves at the front of the lock-free linked list
CountedObjects& instance = CountedObjects::getInstance();
Counter* head;
do
{
head = instance.m_head.load();
next_ = head;
} while (instance.m_head.exchange(this) != head);
++instance.m_count;
}
~Counter() noexcept = default;
int
increment() noexcept
{
return ++count_;
}
int
decrement() noexcept
{
return --count_;
}
int
getCount() const noexcept
{
return count_.load();
}
Counter*
getNext() const noexcept
{
return next_;
}
std::string const&
getName() const noexcept
{
return name_;
}
private:
std::string const name_;
std::atomic<int> count_;
Counter* next_;
};
private:
CountedObjects() noexcept;
~CountedObjects() noexcept = default;
private:
std::atomic<int> m_count;
std::atomic<Counter*> m_head;
};
//------------------------------------------------------------------------------
/** Tracks the number of instances of an object.
Derived classes have their instances counted automatically. This is used
for reporting purposes.
@ingroup ripple_basics
*/
template <class Object>
class CountedObject
{
private:
static auto&
getCounter() noexcept
{
static CountedObjects::Counter c{beast::type_name<Object>()};
return c;
}
public:
CountedObject() noexcept
{
getCounter().increment();
}
CountedObject(CountedObject const&) noexcept
{
getCounter().increment();
}
CountedObject&
operator=(CountedObject const&) noexcept = default;
~CountedObject() noexcept
{
getCounter().decrement();
}
};
} // namespace ripple
#endif

View File

@@ -0,0 +1,155 @@
//------------------------------------------------------------------------------
/*
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_BASICS_DECAYINGSAMPLE_H_INCLUDED
#define RIPPLE_BASICS_DECAYINGSAMPLE_H_INCLUDED
#include <chrono>
#include <cmath>
namespace ripple {
/** Sampling function using exponential decay to provide a continuous value.
@tparam The number of seconds in the decay window.
*/
template <int Window, typename Clock>
class DecayingSample
{
public:
using value_type = typename Clock::duration::rep;
using time_point = typename Clock::time_point;
DecayingSample() = delete;
/**
@param now Start time of DecayingSample.
*/
explicit DecayingSample(time_point now) : m_value(value_type()), m_when(now)
{
}
/** Add a new sample.
The value is first aged according to the specified time.
*/
value_type
add(value_type value, time_point now)
{
decay(now);
m_value += value;
return m_value / Window;
}
/** Retrieve the current value in normalized units.
The samples are first aged according to the specified time.
*/
value_type
value(time_point now)
{
decay(now);
return m_value / Window;
}
private:
// Apply exponential decay based on the specified time.
void
decay(time_point now)
{
if (now == m_when)
return;
if (m_value != value_type())
{
std::size_t elapsed =
std::chrono::duration_cast<std::chrono::seconds>(now - m_when)
.count();
// A span larger than four times the window decays the
// value to an insignificant amount so just reset it.
//
if (elapsed > 4 * Window)
{
m_value = value_type();
}
else
{
while (elapsed--)
m_value -= (m_value + Window - 1) / Window;
}
}
m_when = now;
}
// Current value in exponential units
value_type m_value;
// Last time the aging function was applied
time_point m_when;
};
//------------------------------------------------------------------------------
/** Sampling function using exponential decay to provide a continuous value.
@tparam HalfLife The half life of a sample, in seconds.
*/
template <int HalfLife, class Clock>
class DecayWindow
{
public:
using time_point = typename Clock::time_point;
explicit DecayWindow(time_point now) : value_(0), when_(now)
{
}
void
add(double value, time_point now)
{
decay(now);
value_ += value;
}
double
value(time_point now)
{
decay(now);
return value_ / HalfLife;
}
private:
static_assert(HalfLife > 0, "half life must be positive");
void
decay(time_point now)
{
if (now <= when_)
return;
using namespace std::chrono;
auto const elapsed = duration<double>(now - when_).count();
value_ *= std::pow(2.0, -elapsed / HalfLife);
when_ = now;
}
double value_;
time_point when_;
};
} // namespace ripple
#endif

View File

@@ -0,0 +1,245 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2021 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_BASICS_EXPECTED_H_INCLUDED
#define RIPPLE_BASICS_EXPECTED_H_INCLUDED
#include <ripple/basics/contract.h>
#include <boost/outcome.hpp>
#include <concepts>
#include <stdexcept>
#include <type_traits>
namespace ripple {
/** Expected is an approximation of std::expected (hoped for in C++23)
See: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0323r10.html
The implementation is entirely based on boost::outcome_v2::result.
*/
// Exception thrown by an invalid access to Expected.
struct bad_expected_access : public std::runtime_error
{
bad_expected_access() : runtime_error("bad expected access")
{
}
};
namespace detail {
// Custom policy for Expected. Always throw on an invalid access.
struct throw_policy : public boost::outcome_v2::policy::base
{
template <class Impl>
static constexpr void
wide_value_check(Impl&& self)
{
if (!base::_has_value(std::forward<Impl>(self)))
Throw<bad_expected_access>();
}
template <class Impl>
static constexpr void
wide_error_check(Impl&& self)
{
if (!base::_has_error(std::forward<Impl>(self)))
Throw<bad_expected_access>();
}
template <class Impl>
static constexpr void
wide_exception_check(Impl&& self)
{
if (!base::_has_exception(std::forward<Impl>(self)))
Throw<bad_expected_access>();
}
};
} // namespace detail
// Definition of Unexpected, which is used to construct the unexpected
// return type of an Expected.
template <class E>
class Unexpected
{
public:
static_assert(!std::is_same<E, void>::value, "E must not be void");
Unexpected() = delete;
constexpr explicit Unexpected(E const& e) : val_(e)
{
}
constexpr explicit Unexpected(E&& e) : val_(std::move(e))
{
}
constexpr const E&
value() const&
{
return val_;
}
constexpr E&
value() &
{
return val_;
}
constexpr E&&
value() &&
{
return std::move(val_);
}
constexpr const E&&
value() const&&
{
return std::move(val_);
}
private:
E val_;
};
// Unexpected deduction guide that converts array to const*.
template <typename E, std::size_t N>
Unexpected(E (&)[N]) -> Unexpected<E const*>;
// Definition of Expected. All of the machinery comes from boost::result.
template <class T, class E>
class [[nodiscard]] Expected
: private boost::outcome_v2::result<T, E, detail::throw_policy>
{
using Base = boost::outcome_v2::result<T, E, detail::throw_policy>;
public:
template <typename U>
requires std::convertible_to<U, T> constexpr Expected(U && r)
: Base(T(std::forward<U>(r)))
{
}
template <typename U>
requires std::convertible_to<U, E> &&
(!std::is_reference_v<U>)constexpr Expected(Unexpected<U> e)
: Base(E(std::move(e.value())))
{
}
constexpr bool has_value() const
{
return Base::has_value();
}
constexpr T const& value() const
{
return Base::value();
}
constexpr T& value()
{
return Base::value();
}
constexpr E const& error() const
{
return Base::error();
}
constexpr E& error()
{
return Base::error();
}
constexpr explicit operator bool() const
{
return has_value();
}
// Add operator* and operator-> so the Expected API looks a bit more like
// what std::expected is likely to look like. See:
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0323r10.html
[[nodiscard]] constexpr T& operator*()
{
return this->value();
}
[[nodiscard]] constexpr T const& operator*() const
{
return this->value();
}
[[nodiscard]] constexpr T* operator->()
{
return &this->value();
}
[[nodiscard]] constexpr T const* operator->() const
{
return &this->value();
}
};
// Specialization of Expected<void, E>. Allows returning either success
// (without a value) or the reason for the failure.
template <class E>
class [[nodiscard]] Expected<void, E>
: private boost::outcome_v2::result<void, E, detail::throw_policy>
{
using Base = boost::outcome_v2::result<void, E, detail::throw_policy>;
public:
// The default constructor makes a successful Expected<void, E>.
// This aligns with std::expected behavior proposed in P0323R10.
constexpr Expected() : Base(boost::outcome_v2::success())
{
}
template <typename U>
requires std::convertible_to<U, E> &&
(!std::is_reference_v<U>)constexpr Expected(Unexpected<U> e)
: Base(E(std::move(e.value())))
{
}
constexpr E const& error() const
{
return Base::error();
}
constexpr E& error()
{
return Base::error();
}
constexpr explicit operator bool() const
{
return Base::has_value();
}
};
} // namespace ripple
#endif // RIPPLE_BASICS_EXPECTED_H_INCLUDED

View File

@@ -0,0 +1,556 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2019 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 BASICS_FEES_H_INCLUDED
#define BASICS_FEES_H_INCLUDED
#include <ripple/basics/XRPAmount.h>
#include <boost/multiprecision/cpp_int.hpp>
#include <limits>
#include <utility>
#include <cassert>
#include <cmath>
#include <ios>
#include <iosfwd>
#include <sstream>
#include <string>
namespace ripple {
namespace feeunit {
/** "fee units" calculations are a not-really-unitless value that is used
to express the cost of a given transaction vs. a reference transaction.
They are primarily used by the Transactor classes. */
struct feeunitTag;
/** "fee levels" are used by the transaction queue to compare the relative
cost of transactions that require different levels of effort to process.
See also: src/ripple/app/misc/FeeEscalation.md#fee-level */
struct feelevelTag;
/** unitless values are plain scalars wrapped in a TaggedFee. They are
used for calculations in this header. */
struct unitlessTag;
template <class T>
using enable_if_unit_t = typename std::enable_if_t<
std::is_class_v<T> && std::is_object_v<typename T::unit_type> &&
std::is_object_v<typename T::value_type>>;
/** `is_usable_unit_v` is checked to ensure that only values with
known valid type tags can be used (sometimes transparently) in
non-fee contexts. At the time of implementation, this includes
all known tags, but more may be added in the future, and they
should not be added automatically unless determined to be
appropriate.
*/
template <class T, class = enable_if_unit_t<T>>
constexpr bool is_usable_unit_v =
std::is_same_v<typename T::unit_type, feeunitTag> ||
std::is_same_v<typename T::unit_type, feelevelTag> ||
std::is_same_v<typename T::unit_type, unitlessTag> ||
std::is_same_v<typename T::unit_type, dropTag>;
template <class UnitTag, class T>
class TaggedFee : private boost::totally_ordered<TaggedFee<UnitTag, T>>,
private boost::additive<TaggedFee<UnitTag, T>>,
private boost::equality_comparable<TaggedFee<UnitTag, T>, T>,
private boost::dividable<TaggedFee<UnitTag, T>, T>,
private boost::modable<TaggedFee<UnitTag, T>, T>,
private boost::unit_steppable<TaggedFee<UnitTag, T>>
{
public:
using unit_type = UnitTag;
using value_type = T;
private:
value_type fee_;
protected:
template <class Other>
static constexpr bool is_compatible_v =
std::is_arithmetic_v<Other>&& std::is_arithmetic_v<value_type>&&
std::is_convertible_v<Other, value_type>;
template <class OtherFee, class = enable_if_unit_t<OtherFee>>
static constexpr bool is_compatiblefee_v =
is_compatible_v<typename OtherFee::value_type>&&
std::is_same_v<UnitTag, typename OtherFee::unit_type>;
template <class Other>
using enable_if_compatible_t =
typename std::enable_if_t<is_compatible_v<Other>>;
template <class OtherFee>
using enable_if_compatiblefee_t =
typename std::enable_if_t<is_compatiblefee_v<OtherFee>>;
public:
TaggedFee() = default;
constexpr TaggedFee(TaggedFee const& other) = default;
constexpr TaggedFee&
operator=(TaggedFee const& other) = default;
constexpr explicit TaggedFee(beast::Zero) : fee_(0)
{
}
constexpr TaggedFee& operator=(beast::Zero)
{
fee_ = 0;
return *this;
}
constexpr explicit TaggedFee(value_type fee) : fee_(fee)
{
}
TaggedFee&
operator=(value_type fee)
{
fee_ = fee;
return *this;
}
/** Instances with the same unit, and a type that is
"safe" to convert to this one can be converted
implicitly */
template <
class Other,
class = std::enable_if_t<
is_compatible_v<Other> &&
is_safetocasttovalue_v<value_type, Other>>>
constexpr TaggedFee(TaggedFee<unit_type, Other> const& fee)
: TaggedFee(safe_cast<value_type>(fee.fee()))
{
}
constexpr TaggedFee
operator*(value_type const& rhs) const
{
return TaggedFee{fee_ * rhs};
}
friend constexpr TaggedFee
operator*(value_type lhs, TaggedFee const& rhs)
{
// multiplication is commutative
return rhs * lhs;
}
constexpr value_type
operator/(TaggedFee const& rhs) const
{
return fee_ / rhs.fee_;
}
TaggedFee&
operator+=(TaggedFee const& other)
{
fee_ += other.fee();
return *this;
}
TaggedFee&
operator-=(TaggedFee const& other)
{
fee_ -= other.fee();
return *this;
}
TaggedFee&
operator++()
{
++fee_;
return *this;
}
TaggedFee&
operator--()
{
--fee_;
return *this;
}
TaggedFee&
operator*=(value_type const& rhs)
{
fee_ *= rhs;
return *this;
}
TaggedFee&
operator/=(value_type const& rhs)
{
fee_ /= rhs;
return *this;
}
template <class transparent = value_type>
std::enable_if_t<std::is_integral_v<transparent>, TaggedFee&>
operator%=(value_type const& rhs)
{
fee_ %= rhs;
return *this;
}
TaggedFee
operator-() const
{
static_assert(
std::is_signed_v<T>, "- operator illegal on unsigned fee types");
return TaggedFee{-fee_};
}
bool
operator==(TaggedFee const& other) const
{
return fee_ == other.fee_;
}
template <class Other, class = enable_if_compatible_t<Other>>
bool
operator==(TaggedFee<unit_type, Other> const& other) const
{
return fee_ == other.fee();
}
bool
operator==(value_type other) const
{
return fee_ == other;
}
template <class Other, class = enable_if_compatible_t<Other>>
bool
operator!=(TaggedFee<unit_type, Other> const& other) const
{
return !operator==(other);
}
bool
operator<(TaggedFee const& other) const
{
return fee_ < other.fee_;
}
/** Returns true if the amount is not zero */
explicit constexpr operator bool() const noexcept
{
return fee_ != 0;
}
/** Return the sign of the amount */
constexpr int
signum() const noexcept
{
return (fee_ < 0) ? -1 : (fee_ ? 1 : 0);
}
/** Returns the number of drops */
constexpr value_type
fee() const
{
return fee_;
}
template <class Other>
constexpr double
decimalFromReference(TaggedFee<unit_type, Other> reference) const
{
return static_cast<double>(fee_) / reference.fee();
}
// `is_usable_unit_v` is checked to ensure that only values with
// known valid type tags can be converted to JSON. At the time
// of implementation, that includes all known tags, but more may
// be added in the future.
std::enable_if_t<is_usable_unit_v<TaggedFee>, Json::Value>
jsonClipped() const
{
if constexpr (std::is_integral_v<value_type>)
{
using jsontype = std::conditional_t<
std::is_signed_v<value_type>,
Json::Int,
Json::UInt>;
constexpr auto min = std::numeric_limits<jsontype>::min();
constexpr auto max = std::numeric_limits<jsontype>::max();
if (fee_ < min)
return min;
if (fee_ > max)
return max;
return static_cast<jsontype>(fee_);
}
else
{
return fee_;
}
}
/** Returns the underlying value. Code SHOULD NOT call this
function unless the type has been abstracted away,
e.g. in a templated function.
*/
constexpr value_type
value() const
{
return fee_;
}
friend std::istream&
operator>>(std::istream& s, TaggedFee& val)
{
s >> val.fee_;
return s;
}
};
// Output Fees as just their numeric value.
template <class Char, class Traits, class UnitTag, class T>
std::basic_ostream<Char, Traits>&
operator<<(std::basic_ostream<Char, Traits>& os, const TaggedFee<UnitTag, T>& q)
{
return os << q.value();
}
template <class UnitTag, class T>
std::string
to_string(TaggedFee<UnitTag, T> const& amount)
{
return std::to_string(amount.fee());
}
template <class Source, class = enable_if_unit_t<Source>>
constexpr bool can_muldiv_source_v =
std::is_convertible_v<typename Source::value_type, std::uint64_t>;
template <class Dest, class = enable_if_unit_t<Dest>>
constexpr bool can_muldiv_dest_v =
can_muldiv_source_v<Dest>&& // Dest is also a source
std::is_convertible_v<std::uint64_t, typename Dest::value_type> &&
sizeof(typename Dest::value_type) >= sizeof(std::uint64_t);
template <
class Source1,
class Source2,
class = enable_if_unit_t<Source1>,
class = enable_if_unit_t<Source2>>
constexpr bool can_muldiv_sources_v =
can_muldiv_source_v<Source1>&& can_muldiv_source_v<Source2>&& std::
is_same_v<typename Source1::unit_type, typename Source2::unit_type>;
template <
class Source1,
class Source2,
class Dest,
class = enable_if_unit_t<Source1>,
class = enable_if_unit_t<Source2>,
class = enable_if_unit_t<Dest>>
constexpr bool can_muldiv_v =
can_muldiv_sources_v<Source1, Source2>&& can_muldiv_dest_v<Dest>;
// Source and Dest can be the same by default
template <
class Source1,
class Source2,
class Dest,
class = enable_if_unit_t<Source1>,
class = enable_if_unit_t<Source2>,
class = enable_if_unit_t<Dest>>
constexpr bool can_muldiv_commute_v = can_muldiv_v<Source1, Source2, Dest> &&
!std::is_same_v<typename Source1::unit_type, typename Dest::unit_type>;
template <class T>
using enable_muldiv_source_t =
typename std::enable_if_t<can_muldiv_source_v<T>>;
template <class T>
using enable_muldiv_dest_t = typename std::enable_if_t<can_muldiv_dest_v<T>>;
template <class Source1, class Source2>
using enable_muldiv_sources_t =
typename std::enable_if_t<can_muldiv_sources_v<Source1, Source2>>;
template <class Source1, class Source2, class Dest>
using enable_muldiv_t =
typename std::enable_if_t<can_muldiv_v<Source1, Source2, Dest>>;
template <class Source1, class Source2, class Dest>
using enable_muldiv_commute_t =
typename std::enable_if_t<can_muldiv_commute_v<Source1, Source2, Dest>>;
template <class T>
TaggedFee<unitlessTag, T>
scalar(T value)
{
return TaggedFee<unitlessTag, T>{value};
}
template <
class Source1,
class Source2,
class Dest,
class = enable_muldiv_t<Source1, Source2, Dest>>
std::optional<Dest>
mulDivU(Source1 value, Dest mul, Source2 div)
{
// Fees can never be negative in any context.
if (value.value() < 0 || mul.value() < 0 || div.value() < 0)
{
// split the asserts so if one hits, the user can tell which
// without a debugger.
assert(value.value() >= 0);
assert(mul.value() >= 0);
assert(div.value() >= 0);
return std::nullopt;
}
using desttype = typename Dest::value_type;
constexpr auto max = std::numeric_limits<desttype>::max();
// Shortcuts, since these happen a lot in the real world
if (value == div)
return mul;
if (mul.value() == div.value())
{
if (value.value() > max)
return std::nullopt;
return Dest{static_cast<desttype>(value.value())};
}
using namespace boost::multiprecision;
uint128_t product;
product = multiply(
product,
static_cast<std::uint64_t>(value.value()),
static_cast<std::uint64_t>(mul.value()));
auto quotient = product / div.value();
if (quotient > max)
return std::nullopt;
return Dest{static_cast<desttype>(quotient)};
}
} // namespace feeunit
template <class T>
using FeeLevel = feeunit::TaggedFee<feeunit::feelevelTag, T>;
using FeeLevel64 = FeeLevel<std::uint64_t>;
using FeeLevelDouble = FeeLevel<double>;
template <
class Source1,
class Source2,
class Dest,
class = feeunit::enable_muldiv_t<Source1, Source2, Dest>>
std::optional<Dest>
mulDiv(Source1 value, Dest mul, Source2 div)
{
return feeunit::mulDivU(value, mul, div);
}
template <
class Source1,
class Source2,
class Dest,
class = feeunit::enable_muldiv_commute_t<Source1, Source2, Dest>>
std::optional<Dest>
mulDiv(Dest value, Source1 mul, Source2 div)
{
// Multiplication is commutative
return feeunit::mulDivU(mul, value, div);
}
template <class Dest, class = feeunit::enable_muldiv_dest_t<Dest>>
std::optional<Dest>
mulDiv(std::uint64_t value, Dest mul, std::uint64_t div)
{
// Give the scalars a non-tag so the
// unit-handling version gets called.
return feeunit::mulDivU(feeunit::scalar(value), mul, feeunit::scalar(div));
}
template <class Dest, class = feeunit::enable_muldiv_dest_t<Dest>>
std::optional<Dest>
mulDiv(Dest value, std::uint64_t mul, std::uint64_t div)
{
// Multiplication is commutative
return mulDiv(mul, value, div);
}
template <
class Source1,
class Source2,
class = feeunit::enable_muldiv_sources_t<Source1, Source2>>
std::optional<std::uint64_t>
mulDiv(Source1 value, std::uint64_t mul, Source2 div)
{
// Give the scalars a dimensionless unit so the
// unit-handling version gets called.
auto unitresult = feeunit::mulDivU(value, feeunit::scalar(mul), div);
if (!unitresult)
return std::nullopt;
return unitresult->value();
}
template <
class Source1,
class Source2,
class = feeunit::enable_muldiv_sources_t<Source1, Source2>>
std::optional<std::uint64_t>
mulDiv(std::uint64_t value, Source1 mul, Source2 div)
{
// Multiplication is commutative
return mulDiv(mul, value, div);
}
template <class Dest, class Src>
constexpr std::enable_if_t<
std::is_same_v<typename Dest::unit_type, typename Src::unit_type> &&
std::is_integral_v<typename Dest::value_type> &&
std::is_integral_v<typename Src::value_type>,
Dest>
safe_cast(Src s) noexcept
{
// Dest may not have an explicit value constructor
return Dest{safe_cast<typename Dest::value_type>(s.value())};
}
template <class Dest, class Src>
constexpr std::enable_if_t<
std::is_same_v<typename Dest::unit_type, typename Src::unit_type> &&
std::is_integral_v<typename Dest::value_type> &&
std::is_integral_v<typename Src::value_type>,
Dest>
unsafe_cast(Src s) noexcept
{
// Dest may not have an explicit value constructor
return Dest{unsafe_cast<typename Dest::value_type>(s.value())};
}
} // namespace ripple
#endif // BASICS_FEES_H_INCLUDED

View File

@@ -0,0 +1,44 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2018 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_BASICS_FILEUTILITIES_H_INCLUDED
#define RIPPLE_BASICS_FILEUTILITIES_H_INCLUDED
#include <boost/filesystem.hpp>
#include <boost/system/error_code.hpp>
#include <optional>
namespace ripple {
std::string
getFileContents(
boost::system::error_code& ec,
boost::filesystem::path const& sourcePath,
std::optional<std::size_t> maxSize = std::nullopt);
void
writeFileContents(
boost::system::error_code& ec,
boost::filesystem::path const& destPath,
std::string const& contents);
} // namespace ripple
#endif

View File

@@ -0,0 +1,223 @@
//------------------------------------------------------------------------------
/*
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_BASICS_IOUAMOUNT_H_INCLUDED
#define RIPPLE_BASICS_IOUAMOUNT_H_INCLUDED
#include <ripple/basics/LocalValue.h>
#include <ripple/basics/Number.h>
#include <ripple/beast/utility/Zero.h>
#include <boost/operators.hpp>
#include <cstdint>
#include <string>
#include <utility>
namespace ripple {
/** Floating point representation of amounts with high dynamic range
Amounts are stored as a normalized signed mantissa and an exponent. The
range of the normalized exponent is [-96,80] and the range of the absolute
value of the normalized mantissa is [1000000000000000, 9999999999999999].
Arithmetic operations can throw std::overflow_error during normalization
if the amount exceeds the largest representable amount, but underflows
will silently trunctate to zero.
*/
class IOUAmount : private boost::totally_ordered<IOUAmount>,
private boost::additive<IOUAmount>
{
private:
std::int64_t mantissa_;
int exponent_;
/** Adjusts the mantissa and exponent to the proper range.
This can throw if the amount cannot be normalized, or is larger than
the largest value that can be represented as an IOU amount. Amounts
that are too small to be represented normalize to 0.
*/
void
normalize();
public:
IOUAmount() = default;
explicit IOUAmount(Number const& other);
IOUAmount(beast::Zero);
IOUAmount(std::int64_t mantissa, int exponent);
IOUAmount& operator=(beast::Zero);
operator Number() const;
IOUAmount&
operator+=(IOUAmount const& other);
IOUAmount&
operator-=(IOUAmount const& other);
IOUAmount
operator-() const;
bool
operator==(IOUAmount const& other) const;
bool
operator<(IOUAmount const& other) const;
/** Returns true if the amount is not zero */
explicit operator bool() const noexcept;
/** Return the sign of the amount */
int
signum() const noexcept;
int
exponent() const noexcept;
std::int64_t
mantissa() const noexcept;
static IOUAmount
minPositiveAmount();
};
inline IOUAmount::IOUAmount(beast::Zero)
{
*this = beast::zero;
}
inline IOUAmount::IOUAmount(std::int64_t mantissa, int exponent)
: mantissa_(mantissa), exponent_(exponent)
{
normalize();
}
inline IOUAmount& IOUAmount::operator=(beast::Zero)
{
// The -100 is used to allow 0 to sort less than small positive values
// which will have a large negative exponent.
mantissa_ = 0;
exponent_ = -100;
return *this;
}
inline IOUAmount::operator Number() const
{
return Number{mantissa_, exponent_};
}
inline IOUAmount&
IOUAmount::operator-=(IOUAmount const& other)
{
*this += -other;
return *this;
}
inline IOUAmount
IOUAmount::operator-() const
{
return {-mantissa_, exponent_};
}
inline bool
IOUAmount::operator==(IOUAmount const& other) const
{
return exponent_ == other.exponent_ && mantissa_ == other.mantissa_;
}
inline bool
IOUAmount::operator<(IOUAmount const& other) const
{
return Number{*this} < Number{other};
}
inline IOUAmount::operator bool() const noexcept
{
return mantissa_ != 0;
}
inline int
IOUAmount::signum() const noexcept
{
return (mantissa_ < 0) ? -1 : (mantissa_ ? 1 : 0);
}
inline int
IOUAmount::exponent() const noexcept
{
return exponent_;
}
inline std::int64_t
IOUAmount::mantissa() const noexcept
{
return mantissa_;
}
std::string
to_string(IOUAmount const& amount);
/* Return num*amt/den
This function keeps more precision than computing
num*amt, storing the result in an IOUAmount, then
dividing by den.
*/
IOUAmount
mulRatio(
IOUAmount const& amt,
std::uint32_t num,
std::uint32_t den,
bool roundUp);
// Since many uses of the number class do not have access to a ledger,
// getSTNumberSwitchover needs to be globally accessible.
bool
getSTNumberSwitchover();
void
setSTNumberSwitchover(bool v);
/** RAII class to set and restore the Number switchover.
*/
class NumberSO
{
bool saved_;
public:
~NumberSO()
{
setSTNumberSwitchover(saved_);
}
NumberSO(NumberSO const&) = delete;
NumberSO&
operator=(NumberSO const&) = delete;
explicit NumberSO(bool v) : saved_(getSTNumberSwitchover())
{
setSTNumberSwitchover(v);
}
};
} // namespace ripple
#endif

View File

@@ -0,0 +1,32 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2021 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_BASICS_KEYCACHE_H
#define RIPPLE_BASICS_KEYCACHE_H
#include <ripple/basics/TaggedCache.h>
#include <ripple/basics/base_uint.h>
namespace ripple {
using KeyCache = TaggedCache<uint256, int, true>;
} // namespace ripple
#endif // RIPPLE_BASICS_KEYCACHE_H

View File

@@ -0,0 +1,132 @@
//------------------------------------------------------------------------------
/*
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_BASICS_LOCALVALUE_H_INCLUDED
#define RIPPLE_BASICS_LOCALVALUE_H_INCLUDED
#include <boost/thread/tss.hpp>
#include <memory>
#include <unordered_map>
namespace ripple {
namespace detail {
struct LocalValues
{
explicit LocalValues() = default;
bool onCoro = true;
struct BasicValue
{
virtual ~BasicValue() = default;
virtual void*
get() = 0;
};
template <class T>
struct Value : BasicValue
{
T t_;
Value() = default;
explicit Value(T const& t) : t_(t)
{
}
void*
get() override
{
return &t_;
}
};
// Keys are the address of a LocalValue.
std::unordered_map<void const*, std::unique_ptr<BasicValue>> values;
static inline void
cleanup(LocalValues* lvs)
{
if (lvs && !lvs->onCoro)
delete lvs;
}
};
template <class = void>
boost::thread_specific_ptr<detail::LocalValues>&
getLocalValues()
{
static boost::thread_specific_ptr<detail::LocalValues> tsp(
&detail::LocalValues::cleanup);
return tsp;
}
} // namespace detail
template <class T>
class LocalValue
{
public:
template <class... Args>
LocalValue(Args&&... args) : t_(std::forward<Args>(args)...)
{
}
/** Stores instance of T specific to the calling coroutine or thread. */
T&
operator*();
/** Stores instance of T specific to the calling coroutine or thread. */
T*
operator->()
{
return &**this;
}
private:
T t_;
};
template <class T>
T&
LocalValue<T>::operator*()
{
auto lvs = detail::getLocalValues().get();
if (!lvs)
{
lvs = new detail::LocalValues();
lvs->onCoro = false;
detail::getLocalValues().reset(lvs);
}
else
{
auto const iter = lvs->values.find(this);
if (iter != lvs->values.end())
return *reinterpret_cast<T*>(iter->second->get());
}
return *reinterpret_cast<T*>(
lvs->values
.emplace(this, std::make_unique<detail::LocalValues::Value<T>>(t_))
.first->second->get());
}
} // namespace ripple
#endif

280
include/xrpl/basics/Log.h Normal file
View File

@@ -0,0 +1,280 @@
//------------------------------------------------------------------------------
/*
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_BASICS_LOG_H_INCLUDED
#define RIPPLE_BASICS_LOG_H_INCLUDED
#include <ripple/basics/UnorderedContainers.h>
#include <ripple/beast/utility/Journal.h>
#include <boost/beast/core/string.hpp>
#include <boost/filesystem.hpp>
#include <map>
#include <memory>
#include <mutex>
#include <utility>
namespace ripple {
// DEPRECATED use beast::severities::Severity instead
enum LogSeverity {
lsINVALID = -1, // used to indicate an invalid severity
lsTRACE = 0, // Very low-level progress information, details inside
// an operation
lsDEBUG = 1, // Function-level progress information, operations
lsINFO = 2, // Server-level progress information, major operations
lsWARNING = 3, // Conditions that warrant human attention, may indicate
// a problem
lsERROR = 4, // A condition that indicates a problem
lsFATAL = 5 // A severe condition that indicates a server problem
};
/** Manages partitions for logging. */
class Logs
{
private:
class Sink : public beast::Journal::Sink
{
private:
Logs& logs_;
std::string partition_;
public:
Sink(
std::string const& partition,
beast::severities::Severity thresh,
Logs& logs);
Sink(Sink const&) = delete;
Sink&
operator=(Sink const&) = delete;
void
write(beast::severities::Severity level, std::string const& text)
override;
};
/** Manages a system file containing logged output.
The system file remains open during program execution. Interfaces
are provided for interoperating with standard log management
tools like logrotate(8):
http://linuxcommand.org/man_pages/logrotate8.html
@note None of the listed interfaces are thread-safe.
*/
class File
{
public:
/** Construct with no associated system file.
A system file may be associated later with @ref open.
@see open
*/
File();
/** Destroy the object.
If a system file is associated, it will be flushed and closed.
*/
~File() = default;
/** Determine if a system file is associated with the log.
@return `true` if a system file is associated and opened for
writing.
*/
bool
isOpen() const noexcept;
/** Associate a system file with the log.
If the file does not exist an attempt is made to create it
and open it for writing. If the file already exists an attempt is
made to open it for appending.
If a system file is already associated with the log, it is closed
first.
@return `true` if the file was opened.
*/
bool
open(boost::filesystem::path const& path);
/** Close and re-open the system file associated with the log
This assists in interoperating with external log management tools.
@return `true` if the file was opened.
*/
bool
closeAndReopen();
/** Close the system file if it is open. */
void
close();
/** write to the log file.
Does nothing if there is no associated system file.
*/
void
write(char const* text);
/** write to the log file and append an end of line marker.
Does nothing if there is no associated system file.
*/
void
writeln(char const* text);
/** Write to the log file using std::string. */
/** @{ */
void
write(std::string const& str)
{
write(str.c_str());
}
void
writeln(std::string const& str)
{
writeln(str.c_str());
}
/** @} */
private:
std::unique_ptr<std::ofstream> m_stream;
boost::filesystem::path m_path;
};
std::mutex mutable mutex_;
std::map<
std::string,
std::unique_ptr<beast::Journal::Sink>,
boost::beast::iless>
sinks_;
beast::severities::Severity thresh_;
File file_;
bool silent_ = false;
public:
Logs(beast::severities::Severity level);
Logs(Logs const&) = delete;
Logs&
operator=(Logs const&) = delete;
virtual ~Logs() = default;
bool
open(boost::filesystem::path const& pathToLogFile);
beast::Journal::Sink&
get(std::string const& name);
beast::Journal::Sink&
operator[](std::string const& name);
beast::Journal
journal(std::string const& name);
beast::severities::Severity
threshold() const;
void
threshold(beast::severities::Severity thresh);
std::vector<std::pair<std::string, std::string>>
partition_severities() const;
void
write(
beast::severities::Severity level,
std::string const& partition,
std::string const& text,
bool console);
std::string
rotate();
/**
* Set flag to write logs to stderr (false) or not (true).
*
* @param bSilent Set flag accordingly.
*/
void
silent(bool bSilent)
{
silent_ = bSilent;
}
virtual std::unique_ptr<beast::Journal::Sink>
makeSink(
std::string const& partition,
beast::severities::Severity startingLevel);
public:
static LogSeverity
fromSeverity(beast::severities::Severity level);
static beast::severities::Severity
toSeverity(LogSeverity level);
static std::string
toString(LogSeverity s);
static LogSeverity
fromString(std::string const& s);
private:
enum {
// Maximum line length for log messages.
// If the message exceeds this length it will be truncated with elipses.
maximumMessageCharacters = 12 * 1024
};
static void
format(
std::string& output,
std::string const& message,
beast::severities::Severity severity,
std::string const& partition);
};
// Wraps a Journal::Stream to skip evaluation of
// expensive argument lists if the stream is not active.
#ifndef JLOG
#define JLOG(x) \
if (!x) \
{ \
} \
else \
x
#endif
//------------------------------------------------------------------------------
// Debug logging:
/** Set the sink for the debug journal.
@param sink unique_ptr to new debug Sink.
@return unique_ptr to the previous Sink. nullptr if there was no Sink.
*/
std::unique_ptr<beast::Journal::Sink>
setDebugLogSink(std::unique_ptr<beast::Journal::Sink> sink);
/** Returns a debug journal.
The journal may drain to a null sink, so its output
may never be seen. Never use it for critical
information.
*/
beast::Journal
debugLog();
} // namespace ripple
#endif

View File

@@ -0,0 +1,68 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2020 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_BASICS_MATHUTILITIES_H_INCLUDED
#define RIPPLE_BASICS_MATHUTILITIES_H_INCLUDED
#include <algorithm>
#include <cassert>
#include <cstddef>
namespace ripple {
/** Calculate one number divided by another number in percentage.
* The result is rounded up to the next integer, and capped in the range [0,100]
* E.g. calculatePercent(1, 100) = 1 because 1/100 = 0.010000
* calculatePercent(1, 99) = 2 because 1/99 = 0.010101
* calculatePercent(0, 100) = 0
* calculatePercent(100, 100) = 100
* calculatePercent(200, 100) = 100 because the result is capped to 100
*
* @param count dividend
* @param total divisor
* @return the percentage, in [0, 100]
*
* @note total cannot be zero.
* */
constexpr std::size_t
calculatePercent(std::size_t count, std::size_t total)
{
assert(total != 0);
return ((std::min(count, total) * 100) + total - 1) / total;
}
// unit tests
static_assert(calculatePercent(1, 2) == 50);
static_assert(calculatePercent(0, 100) == 0);
static_assert(calculatePercent(100, 100) == 100);
static_assert(calculatePercent(200, 100) == 100);
static_assert(calculatePercent(1, 100) == 1);
static_assert(calculatePercent(1, 99) == 2);
static_assert(calculatePercent(6, 14) == 43);
static_assert(calculatePercent(29, 33) == 88);
static_assert(calculatePercent(1, 64) == 2);
static_assert(calculatePercent(0, 100'000'000) == 0);
static_assert(calculatePercent(1, 100'000'000) == 1);
static_assert(calculatePercent(50'000'000, 100'000'000) == 50);
static_assert(calculatePercent(50'000'001, 100'000'000) == 51);
static_assert(calculatePercent(99'999'999, 100'000'000) == 100);
} // namespace ripple
#endif

View File

@@ -0,0 +1,412 @@
//------------------------------------------------------------------------------
/*
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 RIPPLE_BASICS_NUMBER_H_INCLUDED
#define RIPPLE_BASICS_NUMBER_H_INCLUDED
#include <ripple/basics/XRPAmount.h>
#include <cstdint>
#include <limits>
#include <ostream>
#include <string>
namespace ripple {
class Number;
std::string
to_string(Number const& amount);
class Number
{
using rep = std::int64_t;
rep mantissa_{0};
int exponent_{std::numeric_limits<int>::lowest()};
public:
struct unchecked
{
explicit unchecked() = default;
};
explicit constexpr Number() = default;
Number(rep mantissa);
explicit Number(rep mantissa, int exponent);
explicit constexpr Number(rep mantissa, int exponent, unchecked) noexcept;
Number(XRPAmount const& x);
constexpr rep
mantissa() const noexcept;
constexpr int
exponent() const noexcept;
constexpr Number
operator+() const noexcept;
constexpr Number
operator-() const noexcept;
Number&
operator++();
Number
operator++(int);
Number&
operator--();
Number
operator--(int);
Number&
operator+=(Number const& x);
Number&
operator-=(Number const& x);
Number&
operator*=(Number const& x);
Number&
operator/=(Number const& x);
static constexpr Number
min() noexcept;
static constexpr Number
max() noexcept;
static constexpr Number
lowest() noexcept;
explicit operator XRPAmount() const; // round to nearest, even on tie
explicit operator rep() const; // round to nearest, even on tie
friend constexpr bool
operator==(Number const& x, Number const& y) noexcept
{
return x.mantissa_ == y.mantissa_ && x.exponent_ == y.exponent_;
}
friend constexpr bool
operator!=(Number const& x, Number const& y) noexcept
{
return !(x == y);
}
friend constexpr bool
operator<(Number const& x, Number const& y) noexcept
{
// If the two amounts have different signs (zero is treated as positive)
// then the comparison is true iff the left is negative.
bool const lneg = x.mantissa_ < 0;
bool const rneg = y.mantissa_ < 0;
if (lneg != rneg)
return lneg;
// Both have same sign and the left is zero: the right must be
// greater than 0.
if (x.mantissa_ == 0)
return y.mantissa_ > 0;
// Both have same sign, the right is zero and the left is non-zero.
if (y.mantissa_ == 0)
return false;
// Both have the same sign, compare by exponents:
if (x.exponent_ > y.exponent_)
return lneg;
if (x.exponent_ < y.exponent_)
return !lneg;
// If equal exponents, compare mantissas
return x.mantissa_ < y.mantissa_;
}
/** Return the sign of the amount */
constexpr int
signum() const noexcept
{
return (mantissa_ < 0) ? -1 : (mantissa_ ? 1 : 0);
}
friend constexpr bool
operator>(Number const& x, Number const& y) noexcept
{
return y < x;
}
friend constexpr bool
operator<=(Number const& x, Number const& y) noexcept
{
return !(y < x);
}
friend constexpr bool
operator>=(Number const& x, Number const& y) noexcept
{
return !(x < y);
}
friend std::ostream&
operator<<(std::ostream& os, Number const& x)
{
return os << to_string(x);
}
// Thread local rounding control. Default is to_nearest
enum rounding_mode { to_nearest, towards_zero, downward, upward };
static rounding_mode
getround();
// Returns previously set mode
static rounding_mode
setround(rounding_mode mode);
private:
static thread_local rounding_mode mode_;
void
normalize();
constexpr bool
isnormal() const noexcept;
// The range for the mantissa when normalized
constexpr static std::int64_t minMantissa = 1'000'000'000'000'000LL;
constexpr static std::int64_t maxMantissa = 9'999'999'999'999'999LL;
// The range for the exponent when normalized
constexpr static int minExponent = -32768;
constexpr static int maxExponent = 32768;
class Guard;
};
inline constexpr Number::Number(rep mantissa, int exponent, unchecked) noexcept
: mantissa_{mantissa}, exponent_{exponent}
{
}
inline Number::Number(rep mantissa, int exponent)
: mantissa_{mantissa}, exponent_{exponent}
{
normalize();
}
inline Number::Number(rep mantissa) : Number{mantissa, 0}
{
}
inline Number::Number(XRPAmount const& x) : Number{x.drops()}
{
}
inline constexpr Number::rep
Number::mantissa() const noexcept
{
return mantissa_;
}
inline constexpr int
Number::exponent() const noexcept
{
return exponent_;
}
inline constexpr Number
Number::operator+() const noexcept
{
return *this;
}
inline constexpr Number
Number::operator-() const noexcept
{
auto x = *this;
x.mantissa_ = -x.mantissa_;
return x;
}
inline Number&
Number::operator++()
{
*this += Number{1000000000000000, -15, unchecked{}};
return *this;
}
inline Number
Number::operator++(int)
{
auto x = *this;
++(*this);
return x;
}
inline Number&
Number::operator--()
{
*this -= Number{1000000000000000, -15, unchecked{}};
return *this;
}
inline Number
Number::operator--(int)
{
auto x = *this;
--(*this);
return x;
}
inline Number&
Number::operator-=(Number const& x)
{
return *this += -x;
}
inline Number
operator+(Number const& x, Number const& y)
{
auto z = x;
z += y;
return z;
}
inline Number
operator-(Number const& x, Number const& y)
{
auto z = x;
z -= y;
return z;
}
inline Number
operator*(Number const& x, Number const& y)
{
auto z = x;
z *= y;
return z;
}
inline Number
operator/(Number const& x, Number const& y)
{
auto z = x;
z /= y;
return z;
}
inline constexpr Number
Number::min() noexcept
{
return Number{minMantissa, minExponent, unchecked{}};
}
inline constexpr Number
Number::max() noexcept
{
return Number{maxMantissa, maxExponent, unchecked{}};
}
inline constexpr Number
Number::lowest() noexcept
{
return -Number{maxMantissa, maxExponent, unchecked{}};
}
inline constexpr bool
Number::isnormal() const noexcept
{
auto const abs_m = mantissa_ < 0 ? -mantissa_ : mantissa_;
return minMantissa <= abs_m && abs_m <= maxMantissa &&
minExponent <= exponent_ && exponent_ <= maxExponent;
}
inline constexpr Number
abs(Number x) noexcept
{
if (x < Number{})
x = -x;
return x;
}
// Returns f^n
// Uses a log_2(n) number of multiplications
Number
power(Number const& f, unsigned n);
// Returns f^(1/d)
// Uses NewtonRaphson iterations until the result stops changing
// to find the root of the polynomial g(x) = x^d - f
Number
root(Number f, unsigned d);
Number
root2(Number f);
// Returns f^(n/d)
Number
power(Number const& f, unsigned n, unsigned d);
// Return 0 if abs(x) < limit, else returns x
inline constexpr Number
squelch(Number const& x, Number const& limit) noexcept
{
if (abs(x) < limit)
return Number{};
return x;
}
class saveNumberRoundMode
{
Number::rounding_mode mode_;
public:
~saveNumberRoundMode()
{
Number::setround(mode_);
}
explicit saveNumberRoundMode(Number::rounding_mode mode) noexcept
: mode_{mode}
{
}
saveNumberRoundMode(saveNumberRoundMode const&) = delete;
saveNumberRoundMode&
operator=(saveNumberRoundMode const&) = delete;
};
// saveNumberRoundMode doesn't do quite enough for us. What we want is a
// Number::RoundModeGuard that sets the new mode and restores the old mode
// when it leaves scope. Since Number doesn't have that facility, we'll
// build it here.
class NumberRoundModeGuard
{
saveNumberRoundMode saved_;
public:
explicit NumberRoundModeGuard(Number::rounding_mode mode) noexcept
: saved_{Number::setround(mode)}
{
}
NumberRoundModeGuard(NumberRoundModeGuard const&) = delete;
NumberRoundModeGuard&
operator=(NumberRoundModeGuard const&) = delete;
};
} // namespace ripple
#endif // RIPPLE_BASICS_NUMBER_H_INCLUDED

View File

@@ -0,0 +1,40 @@
# Basics
Utility functions and classes.
ripple/basic should contain no dependencies on other modules.
Choosing a rippled container.
=============================
* `std::vector`
* For ordered containers with most insertions or erases at the end.
* `std::deque`
* For ordered containers with most insertions or erases at the start or end.
* `std::list`
* For ordered containers with inserts and erases to the middle.
* For containers with iterators stable over insert and erase.
* Generally slower and bigger than `std::vector` or `std::deque` except for
those cases.
* `std::set`
* For sorted containers.
* `ripple::hash_set`
* Where inserts and contains need to be O(1).
* For "small" sets, `std::set` might be faster and smaller.
* `ripple::hardened_hash_set`
* For data sets where the key could be manipulated by an attacker
in an attempt to mount an algorithmic complexity attack: see
http://en.wikipedia.org/wiki/Algorithmic_complexity_attack
The following container is deprecated
* `std::unordered_set`
* Use `ripple::hash_set` instead, which uses a better hashing algorithm.
* Or use `ripple::hardened_hash_set` to prevent algorithmic complexity attacks.

View File

@@ -0,0 +1,196 @@
//------------------------------------------------------------------------------
/*
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_BASICS_RANGESET_H_INCLUDED
#define RIPPLE_BASICS_RANGESET_H_INCLUDED
#include <ripple/beast/core/LexicalCast.h>
#include <boost/algorithm/string.hpp>
#include <boost/icl/closed_interval.hpp>
#include <boost/icl/interval_set.hpp>
#include <optional>
#include <string>
#include <vector>
namespace ripple {
/** A closed interval over the domain T.
For an instance ClosedInterval c, this represents the closed interval
(c.first(), c.last()). A single element interval has c.first() == c.last().
This is simply a type-alias for boost interval container library interval
set, so users should consult that documentation for available supporting
member and free functions.
*/
template <class T>
using ClosedInterval = boost::icl::closed_interval<T>;
/** Create a closed range interval
Helper function to create a closed range interval without having to qualify
the template argument.
*/
template <class T>
ClosedInterval<T>
range(T low, T high)
{
return ClosedInterval<T>(low, high);
}
/** A set of closed intervals over the domain T.
Represents a set of values of the domain T using the minimum number
of disjoint ClosedInterval<T>. This is useful to represent ranges of
T where a few instances are missing, e.g. the set 1-5,8-9,11-14.
This is simply a type-alias for boost interval container library interval
set, so users should consult that documentation for available supporting
member and free functions.
*/
template <class T>
using RangeSet = boost::icl::interval_set<T, std::less, ClosedInterval<T>>;
/** Convert a ClosedInterval to a styled string
The styled string is
"c.first()-c.last()" if c.first() != c.last()
"c.first()" if c.first() == c.last()
@param ci The closed interval to convert
@return The style string
*/
template <class T>
std::string
to_string(ClosedInterval<T> const& ci)
{
if (ci.first() == ci.last())
return std::to_string(ci.first());
return std::to_string(ci.first()) + "-" + std::to_string(ci.last());
}
/** Convert the given RangeSet to a styled string.
The styled string representation is the set of disjoint intervals joined
by commas. The string "empty" is returned if the set is empty.
@param rs The rangeset to convert
@return The styled string
*/
template <class T>
std::string
to_string(RangeSet<T> const& rs)
{
if (rs.empty())
return "empty";
std::string s;
for (auto const& interval : rs)
s += ripple::to_string(interval) + ",";
s.pop_back();
return s;
}
/** Convert the given styled string to a RangeSet.
The styled string representation is the set
of disjoint intervals joined by commas.
@param rs The set to be populated
@param s The styled string to convert
@return True on successfully converting styled string
*/
template <class T>
[[nodiscard]] bool
from_string(RangeSet<T>& rs, std::string const& s)
{
std::vector<std::string> intervals;
std::vector<std::string> tokens;
bool result{true};
rs.clear();
boost::split(tokens, s, boost::algorithm::is_any_of(","));
for (auto const& t : tokens)
{
boost::split(intervals, t, boost::algorithm::is_any_of("-"));
switch (intervals.size())
{
case 1: {
T front;
if (!beast::lexicalCastChecked(front, intervals.front()))
result = false;
else
rs.insert(front);
break;
}
case 2: {
T front;
if (!beast::lexicalCastChecked(front, intervals.front()))
result = false;
else
{
T back;
if (!beast::lexicalCastChecked(back, intervals.back()))
result = false;
else
rs.insert(range(front, back));
}
break;
}
default:
result = false;
}
if (!result)
break;
intervals.clear();
}
if (!result)
rs.clear();
return result;
}
/** Find the largest value not in the set that is less than a given value.
@param rs The set of interest
@param t The value that must be larger than the result
@param minVal (Default is 0) The smallest allowed value
@return The largest v such that minV <= v < t and !contains(rs, v) or
std::nullopt if no such v exists.
*/
template <class T>
std::optional<T>
prevMissing(RangeSet<T> const& rs, T t, T minVal = 0)
{
if (rs.empty() || t == minVal)
return std::nullopt;
RangeSet<T> tgt{ClosedInterval<T>{minVal, t - 1}};
tgt -= rs;
if (tgt.empty())
return std::nullopt;
return boost::icl::last(tgt);
}
} // namespace ripple
#endif

View File

@@ -0,0 +1,71 @@
//------------------------------------------------------------------------------
/*
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_BASICS_RESOLVER_H_INCLUDED
#define RIPPLE_BASICS_RESOLVER_H_INCLUDED
#include <functional>
#include <vector>
#include <ripple/beast/net/IPEndpoint.h>
namespace ripple {
class Resolver
{
public:
using HandlerType =
std::function<void(std::string, std::vector<beast::IP::Endpoint>)>;
virtual ~Resolver() = 0;
/** Issue an asynchronous stop request. */
virtual void
stop_async() = 0;
/** Issue a synchronous stop request. */
virtual void
stop() = 0;
/** Issue a synchronous start request. */
virtual void
start() = 0;
/** resolve all hostnames on the list
@param names the names to be resolved
@param handler the handler to call
*/
/** @{ */
template <class Handler>
void
resolve(std::vector<std::string> const& names, Handler handler)
{
resolve(names, HandlerType(handler));
}
virtual void
resolve(
std::vector<std::string> const& names,
HandlerType const& handler) = 0;
/** @} */
};
} // namespace ripple
#endif

View File

@@ -0,0 +1,40 @@
//------------------------------------------------------------------------------
/*
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_BASICS_RESOLVERASIO_H_INCLUDED
#define RIPPLE_BASICS_RESOLVERASIO_H_INCLUDED
#include <ripple/basics/Resolver.h>
#include <ripple/beast/utility/Journal.h>
#include <boost/asio/io_service.hpp>
namespace ripple {
class ResolverAsio : public Resolver
{
public:
explicit ResolverAsio() = default;
static std::unique_ptr<ResolverAsio>
New(boost::asio::io_service&, beast::Journal);
};
} // namespace ripple
#endif

View File

@@ -0,0 +1,113 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2021 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_BASICS_SHAMAP_HASH_H_INCLUDED
#define RIPPLE_BASICS_SHAMAP_HASH_H_INCLUDED
#include <ripple/basics/base_uint.h>
#include <ostream>
namespace ripple {
// A SHAMapHash is the hash of a node in a SHAMap, and also the
// type of the hash of the entire SHAMap.
class SHAMapHash
{
uint256 hash_;
public:
SHAMapHash() = default;
explicit SHAMapHash(uint256 const& hash) : hash_(hash)
{
}
uint256 const&
as_uint256() const
{
return hash_;
}
uint256&
as_uint256()
{
return hash_;
}
bool
isZero() const
{
return hash_.isZero();
}
bool
isNonZero() const
{
return hash_.isNonZero();
}
int
signum() const
{
return hash_.signum();
}
void
zero()
{
hash_.zero();
}
friend bool
operator==(SHAMapHash const& x, SHAMapHash const& y)
{
return x.hash_ == y.hash_;
}
friend bool
operator<(SHAMapHash const& x, SHAMapHash const& y)
{
return x.hash_ < y.hash_;
}
friend std::ostream&
operator<<(std::ostream& os, SHAMapHash const& x)
{
return os << x.hash_;
}
friend std::string
to_string(SHAMapHash const& x)
{
return to_string(x.hash_);
}
template <class H>
friend void
hash_append(H& h, SHAMapHash const& x)
{
hash_append(h, x.hash_);
}
};
inline bool
operator!=(SHAMapHash const& x, SHAMapHash const& y)
{
return !(x == y);
}
} // namespace ripple
#endif // RIPPLE_BASICS_SHAMAP_HASH_H_INCLUDED

View File

@@ -0,0 +1,432 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright 2022, Nikolaos D. Bougalis <nikb@bougalis.net>
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_BASICS_SLABALLOCATOR_H_INCLUDED
#define RIPPLE_BASICS_SLABALLOCATOR_H_INCLUDED
#include <ripple/beast/type_name.h>
#include <boost/align.hpp>
#include <boost/container/static_vector.hpp>
#include <boost/predef.h>
#include <algorithm>
#include <atomic>
#include <cassert>
#include <cstdint>
#include <cstring>
#include <mutex>
#if BOOST_OS_LINUX
#include <sys/mman.h>
#endif
namespace ripple {
template <typename Type>
class SlabAllocator
{
static_assert(
sizeof(Type) >= sizeof(std::uint8_t*),
"SlabAllocator: the requested object must be larger than a pointer.");
static_assert(alignof(Type) == 8 || alignof(Type) == 4);
/** A block of memory that is owned by a slab allocator */
struct SlabBlock
{
// A mutex to protect the freelist for this block:
std::mutex m_;
// A linked list of appropriately sized free buffers:
std::uint8_t* l_ = nullptr;
// The next memory block
SlabBlock* next_;
// The underlying memory block:
std::uint8_t const* const p_ = nullptr;
// The extent of the underlying memory block:
std::size_t const size_;
SlabBlock(
SlabBlock* next,
std::uint8_t* data,
std::size_t size,
std::size_t item)
: next_(next), p_(data), size_(size)
{
// We don't need to grab the mutex here, since we're the only
// ones with access at this moment.
while (data + item <= p_ + size_)
{
// Use memcpy to avoid unaligned UB
// (will optimize to equivalent code)
std::memcpy(data, &l_, sizeof(std::uint8_t*));
l_ = data;
data += item;
}
}
~SlabBlock()
{
// Calling this destructor will release the allocated memory but
// will not properly destroy any objects that are constructed in
// the block itself.
}
SlabBlock(SlabBlock const& other) = delete;
SlabBlock&
operator=(SlabBlock const& other) = delete;
SlabBlock(SlabBlock&& other) = delete;
SlabBlock&
operator=(SlabBlock&& other) = delete;
/** Determines whether the given pointer belongs to this allocator */
bool
own(std::uint8_t const* p) const noexcept
{
return (p >= p_) && (p < p_ + size_);
}
std::uint8_t*
allocate() noexcept
{
std::uint8_t* ret;
{
std::lock_guard l(m_);
ret = l_;
if (ret)
{
// Use memcpy to avoid unaligned UB
// (will optimize to equivalent code)
std::memcpy(&l_, ret, sizeof(std::uint8_t*));
}
}
return ret;
}
/** Return an item to this allocator's freelist.
@param ptr The pointer to the chunk of memory being deallocated.
@note This is a dangerous, private interface; the item being
returned should belong to this allocator. Debug builds
will check and assert if this is not the case. Release
builds will not.
*/
void
deallocate(std::uint8_t* ptr) noexcept
{
assert(own(ptr));
std::lock_guard l(m_);
// Use memcpy to avoid unaligned UB
// (will optimize to equivalent code)
std::memcpy(ptr, &l_, sizeof(std::uint8_t*));
l_ = ptr;
}
};
private:
// A linked list of slabs
std::atomic<SlabBlock*> slabs_ = nullptr;
// The alignment requirements of the item we're allocating:
std::size_t const itemAlignment_;
// The size of an item, including the extra bytes requested and
// any padding needed for alignment purposes:
std::size_t const itemSize_;
// The size of each individual slab:
std::size_t const slabSize_;
public:
/** Constructs a slab allocator able to allocate objects of a fixed size
@param count the number of items the slab allocator can allocate; note
that a count of 0 is valid and means that the allocator
is, effectively, disabled. This can be very useful in some
contexts (e.g. when mimimal memory usage is needed) and
allows for graceful failure.
*/
constexpr explicit SlabAllocator(
std::size_t extra,
std::size_t alloc = 0,
std::size_t align = 0)
: itemAlignment_(align ? align : alignof(Type))
, itemSize_(
boost::alignment::align_up(sizeof(Type) + extra, itemAlignment_))
, slabSize_(alloc)
{
assert((itemAlignment_ & (itemAlignment_ - 1)) == 0);
}
SlabAllocator(SlabAllocator const& other) = delete;
SlabAllocator&
operator=(SlabAllocator const& other) = delete;
SlabAllocator(SlabAllocator&& other) = delete;
SlabAllocator&
operator=(SlabAllocator&& other) = delete;
~SlabAllocator()
{
// FIXME: We can't destroy the memory blocks we've allocated, because
// we can't be sure that they are not being used. Cleaning the
// shutdown process up could make this possible.
}
/** Returns the size of the memory block this allocator returns. */
constexpr std::size_t
size() const noexcept
{
return itemSize_;
}
/** Returns a suitably aligned pointer, if one is available.
@return a pointer to a block of memory from the allocator, or
nullptr if the allocator can't satisfy this request.
*/
std::uint8_t*
allocate() noexcept
{
auto slab = slabs_.load();
while (slab != nullptr)
{
if (auto ret = slab->allocate())
return ret;
slab = slab->next_;
}
// No slab can satisfy our request, so we attempt to allocate a new
// one here:
std::size_t size = slabSize_;
// We want to allocate the memory at a 2 MiB boundary, to make it
// possible to use hugepage mappings on Linux:
auto buf =
boost::alignment::aligned_alloc(megabytes(std::size_t(2)), size);
// clang-format off
if (!buf) [[unlikely]]
return nullptr;
// clang-format on
#if BOOST_OS_LINUX
// When allocating large blocks, attempt to leverage Linux's
// transparent hugepage support. It is unclear and difficult
// to accurately determine if doing this impacts performance
// enough to justify using platform-specific tricks.
if (size >= megabytes(std::size_t(4)))
madvise(buf, size, MADV_HUGEPAGE);
#endif
// We need to carve out a bit of memory for the slab header
// and then align the rest appropriately:
auto slabData = reinterpret_cast<void*>(
reinterpret_cast<std::uint8_t*>(buf) + sizeof(SlabBlock));
auto slabSize = size - sizeof(SlabBlock);
// This operation is essentially guaranteed not to fail but
// let's be careful anyways.
if (!boost::alignment::align(
itemAlignment_, itemSize_, slabData, slabSize))
{
boost::alignment::aligned_free(buf);
return nullptr;
}
slab = new (buf) SlabBlock(
slabs_.load(),
reinterpret_cast<std::uint8_t*>(slabData),
slabSize,
itemSize_);
// Link the new slab
while (!slabs_.compare_exchange_weak(
slab->next_,
slab,
std::memory_order_release,
std::memory_order_relaxed))
{
; // Nothing to do
}
return slab->allocate();
}
/** Returns the memory block to the allocator.
@param ptr A pointer to a memory block.
@param size If non-zero, a hint as to the size of the block.
@return true if this memory block belonged to the allocator and has
been released; false otherwise.
*/
bool
deallocate(std::uint8_t* ptr) noexcept
{
assert(ptr);
for (auto slab = slabs_.load(); slab != nullptr; slab = slab->next_)
{
if (slab->own(ptr))
{
slab->deallocate(ptr);
return true;
}
}
return false;
}
};
/** A collection of slab allocators of various sizes for a given type. */
template <typename Type>
class SlabAllocatorSet
{
private:
// The list of allocators that belong to this set
boost::container::static_vector<SlabAllocator<Type>, 64> allocators_;
std::size_t maxSize_ = 0;
public:
class SlabConfig
{
friend class SlabAllocatorSet;
private:
std::size_t extra;
std::size_t alloc;
std::size_t align;
public:
constexpr SlabConfig(
std::size_t extra_,
std::size_t alloc_ = 0,
std::size_t align_ = alignof(Type))
: extra(extra_), alloc(alloc_), align(align_)
{
}
};
constexpr SlabAllocatorSet(std::vector<SlabConfig> cfg)
{
// Ensure that the specified allocators are sorted from smallest to
// largest by size:
std::sort(
std::begin(cfg),
std::end(cfg),
[](SlabConfig const& a, SlabConfig const& b) {
return a.extra < b.extra;
});
// We should never have two slabs of the same size
if (std::adjacent_find(
std::begin(cfg),
std::end(cfg),
[](SlabConfig const& a, SlabConfig const& b) {
return a.extra == b.extra;
}) != cfg.end())
{
throw std::runtime_error(
"SlabAllocatorSet<" + beast::type_name<Type>() +
">: duplicate slab size");
}
for (auto const& c : cfg)
{
auto& a = allocators_.emplace_back(c.extra, c.alloc, c.align);
if (a.size() > maxSize_)
maxSize_ = a.size();
}
}
SlabAllocatorSet(SlabAllocatorSet const& other) = delete;
SlabAllocatorSet&
operator=(SlabAllocatorSet const& other) = delete;
SlabAllocatorSet(SlabAllocatorSet&& other) = delete;
SlabAllocatorSet&
operator=(SlabAllocatorSet&& other) = delete;
~SlabAllocatorSet()
{
}
/** Returns a suitably aligned pointer, if one is available.
@param extra The number of extra bytes, above and beyond the size of
the object, that should be returned by the allocator.
@return a pointer to a block of memory, or nullptr if the allocator
can't satisfy this request.
*/
std::uint8_t*
allocate(std::size_t extra) noexcept
{
if (auto const size = sizeof(Type) + extra; size <= maxSize_)
{
for (auto& a : allocators_)
{
if (a.size() >= size)
return a.allocate();
}
}
return nullptr;
}
/** Returns the memory block to the allocator.
@param ptr A pointer to a memory block.
@return true if this memory block belonged to one of the allocators
in this set and has been released; false otherwise.
*/
bool
deallocate(std::uint8_t* ptr) noexcept
{
for (auto& a : allocators_)
{
if (a.deallocate(ptr))
return true;
}
return false;
}
};
} // namespace ripple
#endif // RIPPLE_BASICS_SLABALLOCATOR_H_INCLUDED

264
include/xrpl/basics/Slice.h Normal file
View File

@@ -0,0 +1,264 @@
//------------------------------------------------------------------------------
/*
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_BASICS_SLICE_H_INCLUDED
#define RIPPLE_BASICS_SLICE_H_INCLUDED
#include <ripple/basics/contract.h>
#include <ripple/basics/strHex.h>
#include <algorithm>
#include <array>
#include <cassert>
#include <cstdint>
#include <cstring>
#include <limits>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <vector>
namespace ripple {
/** An immutable linear range of bytes.
A fully constructed Slice is guaranteed to be in a valid state.
A Slice is lightweight and copyable, it retains no ownership
of the underlying memory.
*/
class Slice
{
private:
std::uint8_t const* data_ = nullptr;
std::size_t size_ = 0;
public:
using value_type = std::uint8_t;
using const_iterator = value_type const*;
/** Default constructed Slice has length 0. */
Slice() noexcept = default;
Slice(Slice const&) noexcept = default;
Slice&
operator=(Slice const&) noexcept = default;
/** Create a slice pointing to existing memory. */
Slice(void const* data, std::size_t size) noexcept
: data_(reinterpret_cast<std::uint8_t const*>(data)), size_(size)
{
}
/** Return `true` if the byte range is empty. */
[[nodiscard]] bool
empty() const noexcept
{
return size_ == 0;
}
/** Returns the number of bytes in the storage.
This may be zero for an empty range.
*/
/** @{ */
[[nodiscard]] std::size_t
size() const noexcept
{
return size_;
}
[[nodiscard]] std::size_t
length() const noexcept
{
return size_;
}
/** @} */
/** Return a pointer to beginning of the storage.
@note The return type is guaranteed to be a pointer
to a single byte, to facilitate pointer arithmetic.
*/
std::uint8_t const*
data() const noexcept
{
return data_;
}
/** Access raw bytes. */
std::uint8_t
operator[](std::size_t i) const noexcept
{
assert(i < size_);
return data_[i];
}
/** Advance the buffer. */
/** @{ */
Slice&
operator+=(std::size_t n)
{
if (n > size_)
Throw<std::domain_error>("too small");
data_ += n;
size_ -= n;
return *this;
}
Slice
operator+(std::size_t n) const
{
Slice temp = *this;
return temp += n;
}
/** @} */
/** Shrinks the slice by moving its start forward by n characters. */
void
remove_prefix(std::size_t n)
{
data_ += n;
size_ -= n;
}
/** Shrinks the slice by moving its end backward by n characters. */
void
remove_suffix(std::size_t n)
{
size_ -= n;
}
const_iterator
begin() const noexcept
{
return data_;
}
const_iterator
cbegin() const noexcept
{
return data_;
}
const_iterator
end() const noexcept
{
return data_ + size_;
}
const_iterator
cend() const noexcept
{
return data_ + size_;
}
/** Return a "sub slice" of given length starting at the given position
Note that the subslice encompasses the range [pos, pos + rcount),
where rcount is the smaller of count and size() - pos.
@param pos position of the first character
@count requested length
@returns The requested subslice, if the request is valid.
@throws std::out_of_range if pos > size()
*/
Slice
substr(
std::size_t pos,
std::size_t count = std::numeric_limits<std::size_t>::max()) const
{
if (pos > size())
throw std::out_of_range("Requested sub-slice is out of bounds");
return {data_ + pos, std::min(count, size() - pos)};
}
};
//------------------------------------------------------------------------------
template <class Hasher>
inline void
hash_append(Hasher& h, Slice const& v)
{
h(v.data(), v.size());
}
inline bool
operator==(Slice const& lhs, Slice const& rhs) noexcept
{
if (lhs.size() != rhs.size())
return false;
if (lhs.size() == 0)
return true;
return std::memcmp(lhs.data(), rhs.data(), lhs.size()) == 0;
}
inline bool
operator!=(Slice const& lhs, Slice const& rhs) noexcept
{
return !(lhs == rhs);
}
inline bool
operator<(Slice const& lhs, Slice const& rhs) noexcept
{
return std::lexicographical_compare(
lhs.data(),
lhs.data() + lhs.size(),
rhs.data(),
rhs.data() + rhs.size());
}
template <class Stream>
Stream&
operator<<(Stream& s, Slice const& v)
{
s << strHex(v);
return s;
}
template <class T, std::size_t N>
std::enable_if_t<
std::is_same<T, char>::value || std::is_same<T, unsigned char>::value,
Slice>
makeSlice(std::array<T, N> const& a)
{
return Slice(a.data(), a.size());
}
template <class T, class Alloc>
std::enable_if_t<
std::is_same<T, char>::value || std::is_same<T, unsigned char>::value,
Slice>
makeSlice(std::vector<T, Alloc> const& v)
{
return Slice(v.data(), v.size());
}
template <class Traits, class Alloc>
Slice
makeSlice(std::basic_string<char, Traits, Alloc> const& s)
{
return Slice(s.data(), s.size());
}
} // namespace ripple
#endif

View File

@@ -0,0 +1,157 @@
//------------------------------------------------------------------------------
/*
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_BASICS_STRINGUTILITIES_H_INCLUDED
#define RIPPLE_BASICS_STRINGUTILITIES_H_INCLUDED
#include <ripple/basics/Blob.h>
#include <ripple/basics/strHex.h>
#include <boost/format.hpp>
#include <boost/utility/string_view.hpp>
#include <array>
#include <cstdint>
#include <optional>
#include <sstream>
#include <string>
namespace ripple {
/** Format arbitrary binary data as an SQLite "blob literal".
In SQLite, blob literals must be encoded when used in a query. Per
https://sqlite.org/lang_expr.html#literal_values_constants_ they are
encoded as string literals containing hexadecimal data and preceded
by a single 'X' character.
@param blob An arbitrary blob of binary data
@return The input, encoded as a blob literal.
*/
std::string
sqlBlobLiteral(Blob const& blob);
template <class Iterator>
std::optional<Blob>
strUnHex(std::size_t strSize, Iterator begin, Iterator end)
{
static constexpr std::array<int, 256> const unxtab = []() {
std::array<int, 256> t{};
for (auto& x : t)
x = -1;
for (int i = 0; i < 10; ++i)
t['0' + i] = i;
for (int i = 0; i < 6; ++i)
{
t['A' + i] = 10 + i;
t['a' + i] = 10 + i;
}
return t;
}();
Blob out;
out.reserve((strSize + 1) / 2);
auto iter = begin;
if (strSize & 1)
{
int c = unxtab[*iter++];
if (c < 0)
return {};
out.push_back(c);
}
while (iter != end)
{
int cHigh = unxtab[*iter++];
if (cHigh < 0)
return {};
int cLow = unxtab[*iter++];
if (cLow < 0)
return {};
out.push_back(static_cast<unsigned char>((cHigh << 4) | cLow));
}
return {std::move(out)};
}
inline std::optional<Blob>
strUnHex(std::string const& strSrc)
{
return strUnHex(strSrc.size(), strSrc.cbegin(), strSrc.cend());
}
inline std::optional<Blob>
strViewUnHex(std::string_view strSrc)
{
return strUnHex(strSrc.size(), strSrc.cbegin(), strSrc.cend());
}
struct parsedURL
{
explicit parsedURL() = default;
std::string scheme;
std::string username;
std::string password;
std::string domain;
std::optional<std::uint16_t> port;
std::string path;
bool
operator==(parsedURL const& other) const
{
return scheme == other.scheme && domain == other.domain &&
port == other.port && path == other.path;
}
};
bool
parseUrl(parsedURL& pUrl, std::string const& strUrl);
std::string
trim_whitespace(std::string str);
std::optional<std::uint64_t>
to_uint64(std::string const& s);
/** Determines if the given string looks like a TOML-file hosting domain.
Do not use this function to determine if a particular string is a valid
domain, as this function may reject domains that are otherwise valid and
doesn't check whether the TLD is valid.
*/
bool
isProperlyFormedTomlDomain(std::string_view domain);
} // namespace ripple
#endif

View File

@@ -0,0 +1,798 @@
//------------------------------------------------------------------------------
/*
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_BASICS_TAGGEDCACHE_H_INCLUDED
#define RIPPLE_BASICS_TAGGEDCACHE_H_INCLUDED
#include <ripple/basics/Log.h>
#include <ripple/basics/UnorderedContainers.h>
#include <ripple/basics/hardened_hash.h>
#include <ripple/beast/clock/abstract_clock.h>
#include <ripple/beast/insight/Insight.h>
#include <atomic>
#include <functional>
#include <mutex>
#include <thread>
#include <type_traits>
#include <vector>
namespace ripple {
/** Map/cache combination.
This class implements a cache and a map. The cache keeps objects alive
in the map. The map allows multiple code paths that reference objects
with the same tag to get the same actual object.
So long as data is in the cache, it will stay in memory.
If it stays in memory even after it is ejected from the cache,
the map will track it.
@note Callers must not modify data objects that are stored in the cache
unless they hold their own lock over all cache operations.
*/
template <
class Key,
class T,
bool IsKeyCache = false,
class Hash = hardened_hash<>,
class KeyEqual = std::equal_to<Key>,
class Mutex = std::recursive_mutex>
class TaggedCache
{
public:
using mutex_type = Mutex;
using key_type = Key;
using mapped_type = T;
using clock_type = beast::abstract_clock<std::chrono::steady_clock>;
public:
TaggedCache(
std::string const& name,
int size,
clock_type::duration expiration,
clock_type& clock,
beast::Journal journal,
beast::insight::Collector::ptr const& collector =
beast::insight::NullCollector::New())
: m_journal(journal)
, m_clock(clock)
, m_stats(
name,
std::bind(&TaggedCache::collect_metrics, this),
collector)
, m_name(name)
, m_target_size(size)
, m_target_age(expiration)
, m_cache_count(0)
, m_hits(0)
, m_misses(0)
{
}
public:
/** Return the clock associated with the cache. */
clock_type&
clock()
{
return m_clock;
}
/** Returns the number of items in the container. */
std::size_t
size() const
{
std::lock_guard lock(m_mutex);
return m_cache.size();
}
void
setTargetSize(int s)
{
std::lock_guard lock(m_mutex);
m_target_size = s;
if (s > 0)
{
for (auto& partition : m_cache.map())
{
partition.rehash(static_cast<std::size_t>(
(s + (s >> 2)) /
(partition.max_load_factor() * m_cache.partitions()) +
1));
}
}
JLOG(m_journal.debug()) << m_name << " target size set to " << s;
}
clock_type::duration
getTargetAge() const
{
std::lock_guard lock(m_mutex);
return m_target_age;
}
void
setTargetAge(clock_type::duration s)
{
std::lock_guard lock(m_mutex);
m_target_age = s;
JLOG(m_journal.debug())
<< m_name << " target age set to " << m_target_age.count();
}
int
getCacheSize() const
{
std::lock_guard lock(m_mutex);
return m_cache_count;
}
int
getTrackSize() const
{
std::lock_guard lock(m_mutex);
return m_cache.size();
}
float
getHitRate()
{
std::lock_guard lock(m_mutex);
auto const total = static_cast<float>(m_hits + m_misses);
return m_hits * (100.0f / std::max(1.0f, total));
}
void
clear()
{
std::lock_guard lock(m_mutex);
m_cache.clear();
m_cache_count = 0;
}
void
reset()
{
std::lock_guard lock(m_mutex);
m_cache.clear();
m_cache_count = 0;
m_hits = 0;
m_misses = 0;
}
/** Refresh the last access time on a key if present.
@return `true` If the key was found.
*/
template <class KeyComparable>
bool
touch_if_exists(KeyComparable const& key)
{
std::lock_guard lock(m_mutex);
auto const iter(m_cache.find(key));
if (iter == m_cache.end())
{
++m_stats.misses;
return false;
}
iter->second.touch(m_clock.now());
++m_stats.hits;
return true;
}
using SweptPointersVector = std::pair<
std::vector<std::shared_ptr<mapped_type>>,
std::vector<std::weak_ptr<mapped_type>>>;
void
sweep()
{
// Keep references to all the stuff we sweep
// For performance, each worker thread should exit before the swept data
// is destroyed but still within the main cache lock.
std::vector<SweptPointersVector> allStuffToSweep(m_cache.partitions());
clock_type::time_point const now(m_clock.now());
clock_type::time_point when_expire;
auto const start = std::chrono::steady_clock::now();
{
std::lock_guard lock(m_mutex);
if (m_target_size == 0 ||
(static_cast<int>(m_cache.size()) <= m_target_size))
{
when_expire = now - m_target_age;
}
else
{
when_expire =
now - m_target_age * m_target_size / m_cache.size();
clock_type::duration const minimumAge(std::chrono::seconds(1));
if (when_expire > (now - minimumAge))
when_expire = now - minimumAge;
JLOG(m_journal.trace())
<< m_name << " is growing fast " << m_cache.size() << " of "
<< m_target_size << " aging at "
<< (now - when_expire).count() << " of "
<< m_target_age.count();
}
std::vector<std::thread> workers;
workers.reserve(m_cache.partitions());
std::atomic<int> allRemovals = 0;
for (std::size_t p = 0; p < m_cache.partitions(); ++p)
{
workers.push_back(sweepHelper(
when_expire,
now,
m_cache.map()[p],
allStuffToSweep[p],
allRemovals,
lock));
}
for (std::thread& worker : workers)
worker.join();
m_cache_count -= allRemovals;
}
// At this point allStuffToSweep will go out of scope outside the lock
// and decrement the reference count on each strong pointer.
JLOG(m_journal.debug())
<< m_name << " TaggedCache sweep lock duration "
<< std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - start)
.count()
<< "ms";
}
bool
del(const key_type& key, bool valid)
{
// Remove from cache, if !valid, remove from map too. Returns true if
// removed from cache
std::lock_guard lock(m_mutex);
auto cit = m_cache.find(key);
if (cit == m_cache.end())
return false;
Entry& entry = cit->second;
bool ret = false;
if (entry.isCached())
{
--m_cache_count;
entry.ptr.reset();
ret = true;
}
if (!valid || entry.isExpired())
m_cache.erase(cit);
return ret;
}
/** Replace aliased objects with originals.
Due to concurrency it is possible for two separate objects with
the same content and referring to the same unique "thing" to exist.
This routine eliminates the duplicate and performs a replacement
on the callers shared pointer if needed.
@param key The key corresponding to the object
@param data A shared pointer to the data corresponding to the object.
@param replace Function that decides if cache should be replaced
@return `true` If the key already existed.
*/
public:
bool
canonicalize(
const key_type& key,
std::shared_ptr<T>& data,
std::function<bool(std::shared_ptr<T> const&)>&& replace)
{
// Return canonical value, store if needed, refresh in cache
// Return values: true=we had the data already
std::lock_guard lock(m_mutex);
auto cit = m_cache.find(key);
if (cit == m_cache.end())
{
m_cache.emplace(
std::piecewise_construct,
std::forward_as_tuple(key),
std::forward_as_tuple(m_clock.now(), data));
++m_cache_count;
return false;
}
Entry& entry = cit->second;
entry.touch(m_clock.now());
if (entry.isCached())
{
if (replace(entry.ptr))
{
entry.ptr = data;
entry.weak_ptr = data;
}
else
{
data = entry.ptr;
}
return true;
}
auto cachedData = entry.lock();
if (cachedData)
{
if (replace(entry.ptr))
{
entry.ptr = data;
entry.weak_ptr = data;
}
else
{
entry.ptr = cachedData;
data = cachedData;
}
++m_cache_count;
return true;
}
entry.ptr = data;
entry.weak_ptr = data;
++m_cache_count;
return false;
}
bool
canonicalize_replace_cache(
const key_type& key,
std::shared_ptr<T> const& data)
{
return canonicalize(
key,
const_cast<std::shared_ptr<T>&>(data),
[](std::shared_ptr<T> const&) { return true; });
}
bool
canonicalize_replace_client(const key_type& key, std::shared_ptr<T>& data)
{
return canonicalize(
key, data, [](std::shared_ptr<T> const&) { return false; });
}
std::shared_ptr<T>
fetch(const key_type& key)
{
std::lock_guard<mutex_type> l(m_mutex);
auto ret = initialFetch(key, l);
if (!ret)
++m_misses;
return ret;
}
/** Insert the element into the container.
If the key already exists, nothing happens.
@return `true` If the element was inserted
*/
template <class ReturnType = bool>
auto
insert(key_type const& key, T const& value)
-> std::enable_if_t<!IsKeyCache, ReturnType>
{
auto p = std::make_shared<T>(std::cref(value));
return canonicalize_replace_client(key, p);
}
template <class ReturnType = bool>
auto
insert(key_type const& key) -> std::enable_if_t<IsKeyCache, ReturnType>
{
std::lock_guard lock(m_mutex);
clock_type::time_point const now(m_clock.now());
auto [it, inserted] = m_cache.emplace(
std::piecewise_construct,
std::forward_as_tuple(key),
std::forward_as_tuple(now));
if (!inserted)
it->second.last_access = now;
return inserted;
}
// VFALCO NOTE It looks like this returns a copy of the data in
// the output parameter 'data'. This could be expensive.
// Perhaps it should work like standard containers, which
// simply return an iterator.
//
bool
retrieve(const key_type& key, T& data)
{
// retrieve the value of the stored data
auto entry = fetch(key);
if (!entry)
return false;
data = *entry;
return true;
}
mutex_type&
peekMutex()
{
return m_mutex;
}
std::vector<key_type>
getKeys() const
{
std::vector<key_type> v;
{
std::lock_guard lock(m_mutex);
v.reserve(m_cache.size());
for (auto const& _ : m_cache)
v.push_back(_.first);
}
return v;
}
// CachedSLEs functions.
/** Returns the fraction of cache hits. */
double
rate() const
{
std::lock_guard lock(m_mutex);
auto const tot = m_hits + m_misses;
if (tot == 0)
return 0;
return double(m_hits) / tot;
}
/** Fetch an item from the cache.
If the digest was not found, Handler
will be called with this signature:
std::shared_ptr<SLE const>(void)
*/
template <class Handler>
std::shared_ptr<T>
fetch(key_type const& digest, Handler const& h)
{
{
std::lock_guard l(m_mutex);
if (auto ret = initialFetch(digest, l))
return ret;
}
auto sle = h();
if (!sle)
return {};
std::lock_guard l(m_mutex);
++m_misses;
auto const [it, inserted] =
m_cache.emplace(digest, Entry(m_clock.now(), std::move(sle)));
if (!inserted)
it->second.touch(m_clock.now());
return it->second.ptr;
}
// End CachedSLEs functions.
private:
std::shared_ptr<T>
initialFetch(key_type const& key, std::lock_guard<mutex_type> const& l)
{
auto cit = m_cache.find(key);
if (cit == m_cache.end())
return {};
Entry& entry = cit->second;
if (entry.isCached())
{
++m_hits;
entry.touch(m_clock.now());
return entry.ptr;
}
entry.ptr = entry.lock();
if (entry.isCached())
{
// independent of cache size, so not counted as a hit
++m_cache_count;
entry.touch(m_clock.now());
return entry.ptr;
}
m_cache.erase(cit);
return {};
}
void
collect_metrics()
{
m_stats.size.set(getCacheSize());
{
beast::insight::Gauge::value_type hit_rate(0);
{
std::lock_guard lock(m_mutex);
auto const total(m_hits + m_misses);
if (total != 0)
hit_rate = (m_hits * 100) / total;
}
m_stats.hit_rate.set(hit_rate);
}
}
private:
struct Stats
{
template <class Handler>
Stats(
std::string const& prefix,
Handler const& handler,
beast::insight::Collector::ptr const& collector)
: hook(collector->make_hook(handler))
, size(collector->make_gauge(prefix, "size"))
, hit_rate(collector->make_gauge(prefix, "hit_rate"))
, hits(0)
, misses(0)
{
}
beast::insight::Hook hook;
beast::insight::Gauge size;
beast::insight::Gauge hit_rate;
std::size_t hits;
std::size_t misses;
};
class KeyOnlyEntry
{
public:
clock_type::time_point last_access;
explicit KeyOnlyEntry(clock_type::time_point const& last_access_)
: last_access(last_access_)
{
}
void
touch(clock_type::time_point const& now)
{
last_access = now;
}
};
class ValueEntry
{
public:
std::shared_ptr<mapped_type> ptr;
std::weak_ptr<mapped_type> weak_ptr;
clock_type::time_point last_access;
ValueEntry(
clock_type::time_point const& last_access_,
std::shared_ptr<mapped_type> const& ptr_)
: ptr(ptr_), weak_ptr(ptr_), last_access(last_access_)
{
}
bool
isWeak() const
{
return ptr == nullptr;
}
bool
isCached() const
{
return ptr != nullptr;
}
bool
isExpired() const
{
return weak_ptr.expired();
}
std::shared_ptr<mapped_type>
lock()
{
return weak_ptr.lock();
}
void
touch(clock_type::time_point const& now)
{
last_access = now;
}
};
typedef
typename std::conditional<IsKeyCache, KeyOnlyEntry, ValueEntry>::type
Entry;
using KeyOnlyCacheType =
hardened_partitioned_hash_map<key_type, KeyOnlyEntry, Hash, KeyEqual>;
using KeyValueCacheType =
hardened_partitioned_hash_map<key_type, ValueEntry, Hash, KeyEqual>;
using cache_type =
hardened_partitioned_hash_map<key_type, Entry, Hash, KeyEqual>;
[[nodiscard]] std::thread
sweepHelper(
clock_type::time_point const& when_expire,
[[maybe_unused]] clock_type::time_point const& now,
typename KeyValueCacheType::map_type& partition,
SweptPointersVector& stuffToSweep,
std::atomic<int>& allRemovals,
std::lock_guard<std::recursive_mutex> const&)
{
return std::thread([&, this]() {
int cacheRemovals = 0;
int mapRemovals = 0;
// Keep references to all the stuff we sweep
// so that we can destroy them outside the lock.
stuffToSweep.first.reserve(partition.size());
stuffToSweep.second.reserve(partition.size());
{
auto cit = partition.begin();
while (cit != partition.end())
{
if (cit->second.isWeak())
{
// weak
if (cit->second.isExpired())
{
stuffToSweep.second.push_back(
std::move(cit->second.weak_ptr));
++mapRemovals;
cit = partition.erase(cit);
}
else
{
++cit;
}
}
else if (cit->second.last_access <= when_expire)
{
// strong, expired
++cacheRemovals;
if (cit->second.ptr.use_count() == 1)
{
stuffToSweep.first.push_back(
std::move(cit->second.ptr));
++mapRemovals;
cit = partition.erase(cit);
}
else
{
// remains weakly cached
cit->second.ptr.reset();
++cit;
}
}
else
{
// strong, not expired
++cit;
}
}
}
if (mapRemovals || cacheRemovals)
{
JLOG(m_journal.debug())
<< "TaggedCache partition sweep " << m_name
<< ": cache = " << partition.size() << "-" << cacheRemovals
<< ", map-=" << mapRemovals;
}
allRemovals += cacheRemovals;
});
}
[[nodiscard]] std::thread
sweepHelper(
clock_type::time_point const& when_expire,
clock_type::time_point const& now,
typename KeyOnlyCacheType::map_type& partition,
SweptPointersVector&,
std::atomic<int>& allRemovals,
std::lock_guard<std::recursive_mutex> const&)
{
return std::thread([&, this]() {
int cacheRemovals = 0;
int mapRemovals = 0;
// Keep references to all the stuff we sweep
// so that we can destroy them outside the lock.
{
auto cit = partition.begin();
while (cit != partition.end())
{
if (cit->second.last_access > now)
{
cit->second.last_access = now;
++cit;
}
else if (cit->second.last_access <= when_expire)
{
cit = partition.erase(cit);
}
else
{
++cit;
}
}
}
if (mapRemovals || cacheRemovals)
{
JLOG(m_journal.debug())
<< "TaggedCache partition sweep " << m_name
<< ": cache = " << partition.size() << "-" << cacheRemovals
<< ", map-=" << mapRemovals;
}
allRemovals += cacheRemovals;
});
};
beast::Journal m_journal;
clock_type& m_clock;
Stats m_stats;
mutex_type mutable m_mutex;
// Used for logging
std::string m_name;
// Desired number of cache entries (0 = ignore)
int m_target_size;
// Desired maximum cache age
clock_type::duration m_target_age;
// Number of items cached
int m_cache_count;
cache_type m_cache; // Hold strong reference to recent objects
std::uint64_t m_hits;
std::uint64_t m_misses;
};
} // namespace ripple
#endif

View File

@@ -0,0 +1,63 @@
#ifndef RIPPLE_BASICS_THREAD_SAFTY_ANALYSIS_H_INCLUDED
#define RIPPLE_BASICS_THREAD_SAFTY_ANALYSIS_H_INCLUDED
#ifdef RIPPLE_ENABLE_THREAD_SAFETY_ANNOTATIONS
#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
#else
#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
#endif
#define CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
#define SCOPED_CAPABILITY THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
#define ACQUIRED_BEFORE(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
#define ACQUIRED_AFTER(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
#define REQUIRES(...) \
THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))
#define REQUIRES_SHARED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))
#define ACQUIRE(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))
#define ACQUIRE_SHARED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))
#define RELEASE(...) \
THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))
#define RELEASE_SHARED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))
#define RELEASE_GENERIC(...) \
THREAD_ANNOTATION_ATTRIBUTE__(release_generic_capability(__VA_ARGS__))
#define TRY_ACQUIRE(...) \
THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))
#define TRY_ACQUIRE_SHARED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))
#define EXCLUDES(...) THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
#define ASSERT_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))
#define ASSERT_SHARED_CAPABILITY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))
#define RETURN_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
#define NO_THREAD_SAFETY_ANALYSIS \
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
#endif

View File

@@ -0,0 +1,67 @@
//------------------------------------------------------------------------------
/*
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_BASICS_TOSTRING_H_INCLUDED
#define RIPPLE_BASICS_TOSTRING_H_INCLUDED
#include <string>
#include <type_traits>
namespace ripple {
/** to_string() generalizes std::to_string to handle bools, chars, and strings.
It's also possible to provide implementation of to_string for a class
which needs a string implementation.
*/
template <class T>
typename std::enable_if<std::is_arithmetic<T>::value, std::string>::type
to_string(T t)
{
return std::to_string(t);
}
inline std::string
to_string(bool b)
{
return b ? "true" : "false";
}
inline std::string
to_string(char c)
{
return std::string(1, c);
}
inline std::string
to_string(std::string s)
{
return s;
}
inline std::string
to_string(char const* s)
{
return s;
}
} // namespace ripple
#endif

View File

@@ -0,0 +1,125 @@
//------------------------------------------------------------------------------
/*
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_BASICS_UNORDEREDCONTAINERS_H_INCLUDED
#define RIPPLE_BASICS_UNORDEREDCONTAINERS_H_INCLUDED
#include <ripple/basics/hardened_hash.h>
#include <ripple/basics/partitioned_unordered_map.h>
#include <ripple/beast/hash/hash_append.h>
#include <ripple/beast/hash/uhash.h>
#include <ripple/beast/hash/xxhasher.h>
#include <unordered_map>
#include <unordered_set>
/**
* Use hash_* containers for keys that do not need a cryptographically secure
* hashing algorithm.
*
* Use hardened_hash_* containers for keys that do need a secure hashing
* algorithm.
*
* The cryptographic security of containers where a hash function is used as a
* template parameter depends entirely on that hash function and not at all on
* what container it is.
*/
namespace ripple {
// hash containers
template <
class Key,
class Value,
class Hash = beast::uhash<>,
class Pred = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<Key const, Value>>>
using hash_map = std::unordered_map<Key, Value, Hash, Pred, Allocator>;
template <
class Key,
class Value,
class Hash = beast::uhash<>,
class Pred = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<Key const, Value>>>
using hash_multimap =
std::unordered_multimap<Key, Value, Hash, Pred, Allocator>;
template <
class Value,
class Hash = beast::uhash<>,
class Pred = std::equal_to<Value>,
class Allocator = std::allocator<Value>>
using hash_set = std::unordered_set<Value, Hash, Pred, Allocator>;
template <
class Value,
class Hash = beast::uhash<>,
class Pred = std::equal_to<Value>,
class Allocator = std::allocator<Value>>
using hash_multiset = std::unordered_multiset<Value, Hash, Pred, Allocator>;
// hardened_hash containers
using strong_hash = beast::xxhasher;
template <
class Key,
class Value,
class Hash = hardened_hash<strong_hash>,
class Pred = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<Key const, Value>>>
using hardened_hash_map = std::unordered_map<Key, Value, Hash, Pred, Allocator>;
template <
class Key,
class Value,
class Hash = hardened_hash<strong_hash>,
class Pred = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<Key const, Value>>>
using hardened_partitioned_hash_map =
partitioned_unordered_map<Key, Value, Hash, Pred, Allocator>;
template <
class Key,
class Value,
class Hash = hardened_hash<strong_hash>,
class Pred = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<Key const, Value>>>
using hardened_hash_multimap =
std::unordered_multimap<Key, Value, Hash, Pred, Allocator>;
template <
class Value,
class Hash = hardened_hash<strong_hash>,
class Pred = std::equal_to<Value>,
class Allocator = std::allocator<Value>>
using hardened_hash_set = std::unordered_set<Value, Hash, Pred, Allocator>;
template <
class Value,
class Hash = hardened_hash<strong_hash>,
class Pred = std::equal_to<Value>,
class Allocator = std::allocator<Value>>
using hardened_hash_multiset =
std::unordered_multiset<Value, Hash, Pred, Allocator>;
} // namespace ripple
#endif

View File

@@ -0,0 +1,69 @@
//------------------------------------------------------------------------------
/*
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_BASICS_UPTIMETIMER_H_INCLUDED
#define RIPPLE_BASICS_UPTIMETIMER_H_INCLUDED
#include <atomic>
#include <chrono>
#include <ratio>
#include <thread>
namespace ripple {
/** Tracks program uptime to seconds precision.
The timer caches the current time as a performance optimization.
This allows clients to query the current time thousands of times
per second.
*/
class UptimeClock
{
public:
using rep = int;
using period = std::ratio<1>;
using duration = std::chrono::duration<rep, period>;
using time_point = std::chrono::time_point<UptimeClock>;
static constexpr bool is_steady = std::chrono::system_clock::is_steady;
explicit UptimeClock() = default;
static time_point
now(); // seconds since rippled program start
private:
static std::atomic<rep> now_;
static std::atomic<bool> stop_;
struct update_thread : private std::thread
{
~update_thread();
update_thread(update_thread&&) = default;
using std::thread::thread;
};
static update_thread
start_clock();
};
} // namespace ripple
#endif

View File

@@ -0,0 +1,302 @@
//------------------------------------------------------------------------------
/*
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_BASICS_XRPAMOUNT_H_INCLUDED
#define RIPPLE_BASICS_XRPAMOUNT_H_INCLUDED
#include <ripple/basics/contract.h>
#include <ripple/basics/safe_cast.h>
#include <ripple/beast/utility/Zero.h>
#include <ripple/json/json_value.h>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/operators.hpp>
#include <cstdint>
#include <optional>
#include <string>
#include <type_traits>
namespace ripple {
namespace feeunit {
/** "drops" are the smallest divisible amount of XRP. This is what most
of the code uses. */
struct dropTag;
} // namespace feeunit
class XRPAmount : private boost::totally_ordered<XRPAmount>,
private boost::additive<XRPAmount>,
private boost::equality_comparable<XRPAmount, std::int64_t>,
private boost::additive<XRPAmount, std::int64_t>
{
public:
using unit_type = feeunit::dropTag;
using value_type = std::int64_t;
private:
value_type drops_;
public:
XRPAmount() = default;
constexpr XRPAmount(XRPAmount const& other) = default;
constexpr XRPAmount&
operator=(XRPAmount const& other) = default;
constexpr XRPAmount(beast::Zero) : drops_(0)
{
}
constexpr XRPAmount& operator=(beast::Zero)
{
drops_ = 0;
return *this;
}
constexpr explicit XRPAmount(value_type drops) : drops_(drops)
{
}
XRPAmount&
operator=(value_type drops)
{
drops_ = drops;
return *this;
}
constexpr XRPAmount
operator*(value_type const& rhs) const
{
return XRPAmount{drops_ * rhs};
}
friend constexpr XRPAmount
operator*(value_type lhs, XRPAmount const& rhs)
{
// multiplication is commutative
return rhs * lhs;
}
XRPAmount&
operator+=(XRPAmount const& other)
{
drops_ += other.drops();
return *this;
}
XRPAmount&
operator-=(XRPAmount const& other)
{
drops_ -= other.drops();
return *this;
}
XRPAmount&
operator+=(value_type const& rhs)
{
drops_ += rhs;
return *this;
}
XRPAmount&
operator-=(value_type const& rhs)
{
drops_ -= rhs;
return *this;
}
XRPAmount&
operator*=(value_type const& rhs)
{
drops_ *= rhs;
return *this;
}
XRPAmount
operator-() const
{
return XRPAmount{-drops_};
}
bool
operator==(XRPAmount const& other) const
{
return drops_ == other.drops_;
}
bool
operator==(value_type other) const
{
return drops_ == other;
}
bool
operator<(XRPAmount const& other) const
{
return drops_ < other.drops_;
}
/** Returns true if the amount is not zero */
explicit constexpr operator bool() const noexcept
{
return drops_ != 0;
}
/** Return the sign of the amount */
constexpr int
signum() const noexcept
{
return (drops_ < 0) ? -1 : (drops_ ? 1 : 0);
}
/** Returns the number of drops */
constexpr value_type
drops() const
{
return drops_;
}
constexpr double
decimalXRP() const;
template <class Dest>
std::optional<Dest>
dropsAs() const
{
if ((drops_ > std::numeric_limits<Dest>::max()) ||
(!std::numeric_limits<Dest>::is_signed && drops_ < 0) ||
(std::numeric_limits<Dest>::is_signed &&
drops_ < std::numeric_limits<Dest>::lowest()))
{
return std::nullopt;
}
return static_cast<Dest>(drops_);
}
template <class Dest>
Dest
dropsAs(Dest defaultValue) const
{
return dropsAs<Dest>().value_or(defaultValue);
}
template <class Dest>
Dest
dropsAs(XRPAmount defaultValue) const
{
return dropsAs<Dest>().value_or(defaultValue.drops());
}
Json::Value
jsonClipped() const
{
static_assert(
std::is_signed_v<value_type> && std::is_integral_v<value_type>,
"Expected XRPAmount to be a signed integral type");
constexpr auto min = std::numeric_limits<Json::Int>::min();
constexpr auto max = std::numeric_limits<Json::Int>::max();
if (drops_ < min)
return min;
if (drops_ > max)
return max;
return static_cast<Json::Int>(drops_);
}
/** Returns the underlying value. Code SHOULD NOT call this
function unless the type has been abstracted away,
e.g. in a templated function.
*/
constexpr value_type
value() const
{
return drops_;
}
friend std::istream&
operator>>(std::istream& s, XRPAmount& val)
{
s >> val.drops_;
return s;
}
static XRPAmount
minPositiveAmount()
{
return XRPAmount{1};
}
};
/** Number of drops per 1 XRP */
constexpr XRPAmount DROPS_PER_XRP{1'000'000};
constexpr double
XRPAmount::decimalXRP() const
{
return static_cast<double>(drops_) / DROPS_PER_XRP.drops();
}
// Output XRPAmount as just the drops value.
template <class Char, class Traits>
std::basic_ostream<Char, Traits>&
operator<<(std::basic_ostream<Char, Traits>& os, const XRPAmount& q)
{
return os << q.drops();
}
inline std::string
to_string(XRPAmount const& amount)
{
return std::to_string(amount.drops());
}
inline XRPAmount
mulRatio(
XRPAmount const& amt,
std::uint32_t num,
std::uint32_t den,
bool roundUp)
{
using namespace boost::multiprecision;
if (!den)
Throw<std::runtime_error>("division by zero");
int128_t const amt128(amt.drops());
auto const neg = amt.drops() < 0;
auto const m = amt128 * num;
auto r = m / den;
if (m % den)
{
if (!neg && roundUp)
r += 1;
if (neg && !roundUp)
r -= 1;
}
if (r > std::numeric_limits<XRPAmount::value_type>::max())
Throw<std::overflow_error>("XRP mulRatio overflow");
return XRPAmount(r.convert_to<XRPAmount::value_type>());
}
} // namespace ripple
#endif // RIPPLE_BASICS_XRPAMOUNT_H_INCLUDED

View File

@@ -0,0 +1,120 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2019 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_ALGORITHM_H_INCLUDED
#define RIPPLE_ALGORITHM_H_INCLUDED
#include <iterator>
#include <utility>
namespace ripple {
// Requires: [first1, last1) and [first2, last2) are ordered ranges according to
// comp.
// Effects: For each pair of elements {i, j} in the intersection of the sorted
// sequences [first1, last1) and [first2, last2), perform action(i, j).
// Note: This algorithm is evolved from std::set_intersection.
template <class InputIter1, class InputIter2, class Action, class Comp>
void
generalized_set_intersection(
InputIter1 first1,
InputIter1 last1,
InputIter2 first2,
InputIter2 last2,
Action action,
Comp comp)
{
while (first1 != last1 && first2 != last2)
{
if (comp(*first1, *first2)) // if *first1 < *first2
++first1; // then reduce first range
else
{
if (!comp(*first2, *first1)) // if *first1 == *first2
{ // then this is an intersection
action(*first1, *first2); // do the action
++first1; // reduce first range
}
++first2; // Reduce second range because *first2 <= *first1
}
}
}
// Requires: [first1, last1) and [first2, last2) are ordered ranges according to
// comp.
// Effects: Eliminates all the elements i in the range [first1, last1) which are
// equivalent to some value in [first2, last2) or for which pred(i) returns
// true.
// Returns: A FwdIter1 E such that [first1, E) is the range of elements not
// removed by this algorithm.
// Note: This algorithm is evolved from std::remove_if and
// std::set_intersection.
template <class FwdIter1, class InputIter2, class Pred, class Comp>
FwdIter1
remove_if_intersect_or_match(
FwdIter1 first1,
FwdIter1 last1,
InputIter2 first2,
InputIter2 last2,
Pred pred,
Comp comp)
{
// [original-first1, current-first1) is the set of elements to be preserved.
// [current-first1, i) is the set of elements that have been removed.
// [i, last1) is the set of elements not tested yet.
// Test each *i in [first1, last1) against [first2, last2) and pred
for (auto i = first1; i != last1;)
{
// if (*i is not in [first2, last2)
if (first2 == last2 || comp(*i, *first2))
{
if (!pred(*i))
{
// *i should not be removed, so append it to the preserved set
if (i != first1)
*first1 = std::move(*i);
++first1;
}
// *i has been fully tested, prepare for next i by
// appending i to the removed set, whether or not
// it has been moved from above.
++i;
}
else // *i might be in [first2, last2) because *i >= *first2
{
if (!comp(*first2, *i)) // if *i == *first2
++i; // then append *i to the removed set
// All elements in [i, last1) are known to be greater than *first2,
// so reduce the second range.
++first2;
}
// Continue to test *i against [first2, last2) and pred
}
return first1;
}
} // namespace ripple
#endif

View File

@@ -0,0 +1,80 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012-2018 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.
*/
//==============================================================================
//
// 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)
//
/*
Portions from http://www.adp-gmbh.ch/cpp/common/base64.html
Copyright notice:
base64.cpp and base64.h
Copyright (C) 2004-2008 René Nyffenegger
This source code is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this source code must not be misrepresented; you must not
claim that you wrote the original source code. If you use this source code
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original source code.
3. This notice may not be removed or altered from any source distribution.
René Nyffenegger rene.nyffenegger@adp-gmbh.ch
*/
#ifndef RIPPLE_BASICS_BASE64_H_INCLUDED
#define RIPPLE_BASICS_BASE64_H_INCLUDED
#include <cstdint>
#include <string>
namespace ripple {
std::string
base64_encode(std::uint8_t const* data, std::size_t len);
inline std::string
base64_encode(std::string const& s)
{
return base64_encode(
reinterpret_cast<std::uint8_t const*>(s.data()), s.size());
}
std::string
base64_decode(std::string_view data);
} // namespace ripple
#endif

View File

@@ -0,0 +1,652 @@
//------------------------------------------------------------------------------
/*
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.
*/
//==============================================================================
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2011 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
#ifndef RIPPLE_BASICS_BASE_UINT_H_INCLUDED
#define RIPPLE_BASICS_BASE_UINT_H_INCLUDED
#include <ripple/basics/Expected.h>
#include <ripple/basics/Slice.h>
#include <ripple/basics/contract.h>
#include <ripple/basics/hardened_hash.h>
#include <ripple/basics/strHex.h>
#include <ripple/beast/utility/Zero.h>
#include <boost/endian/conversion.hpp>
#include <boost/functional/hash.hpp>
#include <algorithm>
#include <array>
#include <cstring>
#include <functional>
#include <type_traits>
namespace ripple {
namespace detail {
template <class Container, class = std::void_t<>>
struct is_contiguous_container : std::false_type
{
};
template <class Container>
struct is_contiguous_container<
Container,
std::void_t<
decltype(std::declval<Container const>().size()),
decltype(std::declval<Container const>().data()),
typename Container::value_type>> : std::true_type
{
};
template <>
struct is_contiguous_container<Slice> : std::true_type
{
};
} // namespace detail
/** Integers of any length that is a multiple of 32-bits
@note This class stores its values internally in big-endian
form and that internal representation is part of the
binary protocol of the XRP Ledger and cannot be changed
arbitrarily without causing breakage.
@tparam Bits The number of bits this integer should have; must
be at least 64 and a multiple of 32.
@tparam Tag An arbitrary type that functions as a tag and allows
the instantiation of "distinct" types that the same
number of bits.
*/
template <std::size_t Bits, class Tag = void>
class base_uint
{
static_assert(
(Bits % 32) == 0,
"The length of a base_uint in bits must be a multiple of 32.");
static_assert(
Bits >= 64,
"The length of a base_uint in bits must be at least 64.");
static constexpr std::size_t WIDTH = Bits / 32;
// This is really big-endian in byte order.
// We sometimes use std::uint32_t for speed.
std::array<std::uint32_t, WIDTH> data_;
public:
//--------------------------------------------------------------------------
//
// STL Container Interface
//
static std::size_t constexpr bytes = Bits / 8;
static_assert(sizeof(data_) == bytes, "");
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using value_type = unsigned char;
using pointer = value_type*;
using reference = value_type&;
using const_pointer = value_type const*;
using const_reference = value_type const&;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
using tag_type = Tag;
pointer
data()
{
return reinterpret_cast<pointer>(data_.data());
}
const_pointer
data() const
{
return reinterpret_cast<const_pointer>(data_.data());
}
iterator
begin()
{
return data();
}
iterator
end()
{
return data() + bytes;
}
const_iterator
begin() const
{
return data();
}
const_iterator
end() const
{
return data() + bytes;
}
const_iterator
cbegin() const
{
return data();
}
const_iterator
cend() const
{
return data() + bytes;
}
/** Value hashing function.
The seed prevents crafted inputs from causing degenerate parent
containers.
*/
using hasher = hardened_hash<>;
//--------------------------------------------------------------------------
private:
/** Construct from a raw pointer.
The buffer pointed to by `data` must be at least Bits/8 bytes.
@note the structure is used to disambiguate this from the std::uint64_t
constructor: something like base_uint(0) is ambiguous.
*/
// NIKB TODO Remove the need for this constructor.
struct VoidHelper
{
explicit VoidHelper() = default;
};
explicit base_uint(void const* data, VoidHelper)
{
memcpy(data_.data(), data, bytes);
}
// Helper function to initialize a base_uint from a std::string_view.
enum class ParseResult {
okay,
badLength,
badChar,
};
constexpr Expected<decltype(data_), ParseResult>
parseFromStringView(std::string_view sv) noexcept
{
// Local lambda that converts a single hex char to four bits and
// ORs those bits into a uint32_t.
auto hexCharToUInt = [](char c,
std::uint32_t shift,
std::uint32_t& accum) -> ParseResult {
std::uint32_t nibble = 0xFFu;
if (c < '0' || c > 'f')
return ParseResult::badChar;
if (c >= 'a')
nibble = static_cast<std::uint32_t>(c - 'a' + 0xA);
else if (c >= 'A')
nibble = static_cast<std::uint32_t>(c - 'A' + 0xA);
else if (c <= '9')
nibble = static_cast<std::uint32_t>(c - '0');
if (nibble > 0xFu)
return ParseResult::badChar;
accum |= (nibble << shift);
return ParseResult::okay;
};
decltype(data_) ret{};
if (sv == "0")
{
return ret;
}
if (sv.size() != size() * 2)
return Unexpected(ParseResult::badLength);
std::size_t i = 0u;
auto in = sv.begin();
while (in != sv.end())
{
std::uint32_t accum = {};
for (std::uint32_t shift : {4u, 0u, 12u, 8u, 20u, 16u, 28u, 24u})
{
if (auto const result = hexCharToUInt(*in++, shift, accum);
result != ParseResult::okay)
return Unexpected(result);
}
ret[i++] = accum;
}
return ret;
}
constexpr decltype(data_)
parseFromStringViewThrows(std::string_view sv) noexcept(false)
{
auto const result = parseFromStringView(sv);
if (!result)
{
if (result.error() == ParseResult::badLength)
Throw<std::invalid_argument>("invalid length for hex string");
Throw<std::range_error>("invalid hex character");
}
return *result;
}
public:
constexpr base_uint() : data_{}
{
}
constexpr base_uint(beast::Zero) : data_{}
{
}
explicit base_uint(std::uint64_t b)
{
*this = b;
}
// This constructor is intended to be used at compile time since it might
// throw at runtime. Consider declaring this constructor consteval once
// we get to C++23.
explicit constexpr base_uint(std::string_view sv) noexcept(false)
: data_(parseFromStringViewThrows(sv))
{
}
template <
class Container,
class = std::enable_if_t<
detail::is_contiguous_container<Container>::value &&
std::is_trivially_copyable<typename Container::value_type>::value>>
explicit base_uint(Container const& c)
{
assert(c.size() * sizeof(typename Container::value_type) == size());
std::memcpy(data_.data(), c.data(), size());
}
template <class Container>
std::enable_if_t<
detail::is_contiguous_container<Container>::value &&
std::is_trivially_copyable<typename Container::value_type>::value,
base_uint&>
operator=(Container const& c)
{
assert(c.size() * sizeof(typename Container::value_type) == size());
std::memcpy(data_.data(), c.data(), size());
return *this;
}
/* Construct from a raw pointer.
The buffer pointed to by `data` must be at least Bits/8 bytes.
*/
static base_uint
fromVoid(void const* data)
{
return base_uint(data, VoidHelper());
}
template <class T>
static std::optional<base_uint>
fromVoidChecked(T const& from)
{
if (from.size() != size())
return {};
return fromVoid(from.data());
}
constexpr int
signum() const
{
for (int i = 0; i < WIDTH; i++)
if (data_[i] != 0)
return 1;
return 0;
}
bool
operator!() const
{
return *this == beast::zero;
}
constexpr base_uint
operator~() const
{
base_uint ret;
for (int i = 0; i < WIDTH; i++)
ret.data_[i] = ~data_[i];
return ret;
}
base_uint&
operator=(std::uint64_t uHost)
{
*this = beast::zero;
union
{
unsigned u[2];
std::uint64_t ul;
};
// Put in least significant bits.
ul = boost::endian::native_to_big(uHost);
data_[WIDTH - 2] = u[0];
data_[WIDTH - 1] = u[1];
return *this;
}
base_uint&
operator^=(const base_uint& b)
{
for (int i = 0; i < WIDTH; i++)
data_[i] ^= b.data_[i];
return *this;
}
base_uint&
operator&=(const base_uint& b)
{
for (int i = 0; i < WIDTH; i++)
data_[i] &= b.data_[i];
return *this;
}
base_uint&
operator|=(const base_uint& b)
{
for (int i = 0; i < WIDTH; i++)
data_[i] |= b.data_[i];
return *this;
}
base_uint&
operator++()
{
// prefix operator
for (int i = WIDTH - 1; i >= 0; --i)
{
data_[i] = boost::endian::native_to_big(
boost::endian::big_to_native(data_[i]) + 1);
if (data_[i] != 0)
break;
}
return *this;
}
const base_uint
operator++(int)
{
// postfix operator
const base_uint ret = *this;
++(*this);
return ret;
}
base_uint&
operator--()
{
for (int i = WIDTH - 1; i >= 0; --i)
{
auto prev = data_[i];
data_[i] = boost::endian::native_to_big(
boost::endian::big_to_native(data_[i]) - 1);
if (prev != 0)
break;
}
return *this;
}
const base_uint
operator--(int)
{
// postfix operator
const base_uint ret = *this;
--(*this);
return ret;
}
base_uint
next() const
{
auto ret = *this;
return ++ret;
}
base_uint
prev() const
{
auto ret = *this;
return --ret;
}
base_uint&
operator+=(const base_uint& b)
{
std::uint64_t carry = 0;
for (int i = WIDTH; i--;)
{
std::uint64_t n = carry + boost::endian::big_to_native(data_[i]) +
boost::endian::big_to_native(b.data_[i]);
data_[i] =
boost::endian::native_to_big(static_cast<std::uint32_t>(n));
carry = n >> 32;
}
return *this;
}
template <class Hasher>
friend void
hash_append(Hasher& h, base_uint const& a) noexcept
{
// Do not allow any endian transformations on this memory
h(a.data_.data(), sizeof(a.data_));
}
/** Parse a hex string into a base_uint
The input must be precisely `2 * bytes` hexadecimal characters
long, with one exception: the value '0'.
@param sv A null-terminated string of hexadecimal characters
@return true if the input was parsed properly; false otherwise.
*/
[[nodiscard]] constexpr bool
parseHex(std::string_view sv)
{
auto const result = parseFromStringView(sv);
if (!result)
return false;
data_ = *result;
return true;
}
[[nodiscard]] constexpr bool
parseHex(const char* str)
{
return parseHex(std::string_view{str});
}
[[nodiscard]] bool
parseHex(std::string const& str)
{
return parseHex(std::string_view{str});
}
constexpr static std::size_t
size()
{
return bytes;
}
base_uint<Bits, Tag>& operator=(beast::Zero)
{
data_.fill(0);
return *this;
}
// Deprecated.
bool
isZero() const
{
return *this == beast::zero;
}
bool
isNonZero() const
{
return *this != beast::zero;
}
void
zero()
{
*this = beast::zero;
}
};
using uint128 = base_uint<128>;
using uint160 = base_uint<160>;
using uint256 = base_uint<256>;
template <std::size_t Bits, class Tag>
[[nodiscard]] inline constexpr std::strong_ordering
operator<=>(base_uint<Bits, Tag> const& lhs, base_uint<Bits, Tag> const& rhs)
{
// This comparison might seem wrong on a casual inspection because it
// compares data internally stored as std::uint32_t byte-by-byte. But
// note that the underlying data is stored in big endian, even if the
// plaform is little endian. This makes the comparison correct.
//
// FIXME: use std::lexicographical_compare_three_way once support is
// added to MacOS.
auto const ret = std::mismatch(lhs.cbegin(), lhs.cend(), rhs.cbegin());
// a == b
if (ret.first == lhs.cend())
return std::strong_ordering::equivalent;
return (*ret.first > *ret.second) ? std::strong_ordering::greater
: std::strong_ordering::less;
}
template <std::size_t Bits, typename Tag>
[[nodiscard]] inline constexpr bool
operator==(base_uint<Bits, Tag> const& lhs, base_uint<Bits, Tag> const& rhs)
{
return (lhs <=> rhs) == 0;
}
//------------------------------------------------------------------------------
template <std::size_t Bits, class Tag>
inline constexpr bool
operator==(base_uint<Bits, Tag> const& a, std::uint64_t b)
{
return a == base_uint<Bits, Tag>(b);
}
//------------------------------------------------------------------------------
template <std::size_t Bits, class Tag>
inline constexpr base_uint<Bits, Tag>
operator^(base_uint<Bits, Tag> const& a, base_uint<Bits, Tag> const& b)
{
return base_uint<Bits, Tag>(a) ^= b;
}
template <std::size_t Bits, class Tag>
inline constexpr base_uint<Bits, Tag>
operator&(base_uint<Bits, Tag> const& a, base_uint<Bits, Tag> const& b)
{
return base_uint<Bits, Tag>(a) &= b;
}
template <std::size_t Bits, class Tag>
inline constexpr base_uint<Bits, Tag>
operator|(base_uint<Bits, Tag> const& a, base_uint<Bits, Tag> const& b)
{
return base_uint<Bits, Tag>(a) |= b;
}
template <std::size_t Bits, class Tag>
inline constexpr base_uint<Bits, Tag>
operator+(base_uint<Bits, Tag> const& a, base_uint<Bits, Tag> const& b)
{
return base_uint<Bits, Tag>(a) += b;
}
//------------------------------------------------------------------------------
template <std::size_t Bits, class Tag>
inline std::string
to_string(base_uint<Bits, Tag> const& a)
{
return strHex(a.cbegin(), a.cend());
}
template <std::size_t Bits, class Tag>
inline std::ostream&
operator<<(std::ostream& out, base_uint<Bits, Tag> const& u)
{
return out << to_string(u);
}
#ifndef __INTELLISENSE__
static_assert(sizeof(uint128) == 128 / 8, "There should be no padding bytes");
static_assert(sizeof(uint160) == 160 / 8, "There should be no padding bytes");
static_assert(sizeof(uint256) == 256 / 8, "There should be no padding bytes");
#endif
} // namespace ripple
namespace beast {
template <std::size_t Bits, class Tag>
struct is_uniquely_represented<ripple::base_uint<Bits, Tag>>
: public std::true_type
{
explicit is_uniquely_represented() = default;
};
} // namespace beast
#endif

View File

@@ -0,0 +1,129 @@
//------------------------------------------------------------------------------
/*
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_BASICS_CHRONO_H_INCLUDED
#define RIPPLE_BASICS_CHRONO_H_INCLUDED
#include <date/date.h>
#include <ripple/beast/clock/abstract_clock.h>
#include <ripple/beast/clock/basic_seconds_clock.h>
#include <ripple/beast/clock/manual_clock.h>
#include <chrono>
#include <cstdint>
#include <ratio>
#include <string>
#include <type_traits>
namespace ripple {
// A few handy aliases
using days = std::chrono::duration<
int,
std::ratio_multiply<std::chrono::hours::period, std::ratio<24>>>;
using weeks = std::chrono::
duration<int, std::ratio_multiply<days::period, std::ratio<7>>>;
/** Clock for measuring the network time.
The epoch is January 1, 2000
epoch_offset
= date(2000-01-01) - date(1970-0-01)
= days(10957)
= seconds(946684800)
*/
constexpr static std::chrono::seconds epoch_offset =
date::sys_days{date::year{2000} / 1 / 1} -
date::sys_days{date::year{1970} / 1 / 1};
static_assert(epoch_offset.count() == 946684800);
class NetClock
{
public:
explicit NetClock() = default;
using rep = std::uint32_t;
using period = std::ratio<1>;
using duration = std::chrono::duration<rep, period>;
using time_point = std::chrono::time_point<NetClock>;
static bool const is_steady = false;
};
template <class Duration>
std::string
to_string(date::sys_time<Duration> tp)
{
return date::format("%Y-%b-%d %T %Z", tp);
}
inline std::string
to_string(NetClock::time_point tp)
{
// 2000-01-01 00:00:00 UTC is 946684800s from 1970-01-01 00:00:00 UTC
using namespace std::chrono;
return to_string(
system_clock::time_point{tp.time_since_epoch() + epoch_offset});
}
template <class Duration>
std::string
to_string_iso(date::sys_time<Duration> tp)
{
using namespace std::chrono;
return date::format("%FT%TZ", tp);
}
inline std::string
to_string_iso(NetClock::time_point tp)
{
// 2000-01-01 00:00:00 UTC is 946684800s from 1970-01-01 00:00:00 UTC
// Note, NetClock::duration is seconds, as checked by static_assert
static_assert(std::is_same_v<NetClock::duration::period, std::ratio<1>>);
return to_string_iso(date::sys_time<NetClock::duration>{
tp.time_since_epoch() + epoch_offset});
}
/** A clock for measuring elapsed time.
The epoch is unspecified.
*/
using Stopwatch = beast::abstract_clock<std::chrono::steady_clock>;
/** A manual Stopwatch for unit tests. */
using TestStopwatch = beast::manual_clock<std::chrono::steady_clock>;
/** Returns an instance of a wall clock. */
inline Stopwatch&
stopwatch()
{
using Clock = beast::basic_seconds_clock;
using Facade = Clock::Clock;
return beast::get_abstract_clock<Facade, Clock>();
}
} // namespace ripple
#endif

View File

@@ -0,0 +1,76 @@
//------------------------------------------------------------------------------
/*
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_BASICS_COMPARATORS_H_INCLUDED
#define RIPPLE_BASICS_COMPARATORS_H_INCLUDED
#include <functional>
namespace ripple {
#ifdef _MSC_VER
/*
* MSVC 2019 version 16.9.0 added [[nodiscard]] to the std comparison
* operator() functions. boost::bimap checks that the comparitor is a
* BinaryFunction, in part by calling the function and ignoring the value.
* These two things don't play well together. These wrapper classes simply
* strip [[nodiscard]] from operator() for use in boost::bimap.
*
* See also:
* https://www.boost.org/doc/libs/1_75_0/libs/bimap/doc/html/boost_bimap/the_tutorial/controlling_collection_types.html
*/
template <class T = void>
struct less
{
using result_type = bool;
constexpr bool
operator()(const T& left, const T& right) const
{
return std::less<T>()(left, right);
}
};
template <class T = void>
struct equal_to
{
using result_type = bool;
constexpr bool
operator()(const T& left, const T& right) const
{
return std::equal_to<T>()(left, right);
}
};
#else
template <class T = void>
using less = std::less<T>;
template <class T = void>
using equal_to = std::equal_to<T>;
#endif
} // namespace ripple
#endif

View File

@@ -0,0 +1,76 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2014 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_BASICS_CONTRACT_H_INCLUDED
#define RIPPLE_BASICS_CONTRACT_H_INCLUDED
#include <ripple/beast/type_name.h>
#include <exception>
#include <string>
#include <typeinfo>
#include <utility>
namespace ripple {
/* Programming By Contract
This routines are used when checking
preconditions, postconditions, and invariants.
*/
/** Generates and logs a call stack */
void
LogThrow(std::string const& title);
/** Rethrow the exception currently being handled.
When called from within a catch block, it will pass
control to the next matching exception handler, if any.
Otherwise, std::terminate will be called.
*/
[[noreturn]] inline void
Rethrow()
{
LogThrow("Re-throwing exception");
throw;
}
template <class E, class... Args>
[[noreturn]] inline void
Throw(Args&&... args)
{
static_assert(
std::is_convertible<E*, std::exception*>::value,
"Exception must derive from std::exception.");
E e(std::forward<Args>(args)...);
LogThrow(
std::string(
"Throwing exception of type " + beast::type_name<E>() + ": ") +
e.what());
throw e;
}
/** Called when faulty logic causes a broken invariant. */
[[noreturn]] void
LogicError(std::string const& how) noexcept;
} // namespace ripple
#endif

View File

@@ -0,0 +1,120 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2014 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_BASICS_HARDENED_HASH_H_INCLUDED
#define RIPPLE_BASICS_HARDENED_HASH_H_INCLUDED
#include <ripple/beast/hash/hash_append.h>
#include <ripple/beast/hash/xxhasher.h>
#include <cstdint>
#include <functional>
#include <mutex>
#include <random>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <utility>
namespace ripple {
namespace detail {
using seed_pair = std::pair<std::uint64_t, std::uint64_t>;
template <bool = true>
seed_pair
make_seed_pair() noexcept
{
struct state_t
{
std::mutex mutex;
std::random_device rng;
std::mt19937_64 gen;
std::uniform_int_distribution<std::uint64_t> dist;
state_t() : gen(rng())
{
}
// state_t(state_t const&) = delete;
// state_t& operator=(state_t const&) = delete;
};
static state_t state;
std::lock_guard lock(state.mutex);
return {state.dist(state.gen), state.dist(state.gen)};
}
} // namespace detail
/**
* Seed functor once per construction
A std compatible hash adapter that resists adversarial inputs.
For this to work, T must implement in its own namespace:
@code
template <class Hasher>
void
hash_append (Hasher& h, T const& t) noexcept
{
// hash_append each base and member that should
// participate in forming the hash
using beast::hash_append;
hash_append (h, static_cast<T::base1 const&>(t));
hash_append (h, static_cast<T::base2 const&>(t));
// ...
hash_append (h, t.member1);
hash_append (h, t.member2);
// ...
}
@endcode
Do not use any version of Murmur or CityHash for the Hasher
template parameter (the hashing algorithm). For details
see https://131002.net/siphash/#at
*/
template <class HashAlgorithm = beast::xxhasher>
class hardened_hash
{
private:
detail::seed_pair m_seeds;
public:
using result_type = typename HashAlgorithm::result_type;
hardened_hash() : m_seeds(detail::make_seed_pair<>())
{
}
template <class T>
result_type
operator()(T const& t) const noexcept
{
HashAlgorithm h(m_seeds.first, m_seeds.second);
hash_append(h, t);
return static_cast<result_type>(h);
}
};
} // namespace ripple
#endif

108
include/xrpl/basics/join.h Normal file
View File

@@ -0,0 +1,108 @@
//------------------------------------------------------------------------------
/*
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
#include <string>
namespace ripple {
template <class Stream, class Iter>
Stream&
join(Stream& s, Iter iter, Iter end, std::string const& delimiter)
{
if (iter == end)
return s;
s << *iter;
for (++iter; iter != end; ++iter)
s << delimiter << *iter;
return s;
}
template <class Collection>
class CollectionAndDelimiter
{
public:
Collection const& collection;
std::string const delimiter;
explicit CollectionAndDelimiter(Collection const& c, std::string delim)
: collection(c), delimiter(std::move(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, std::size_t N>
class CollectionAndDelimiter<Collection[N]>
{
public:
Collection const* collection;
std::string const delimiter;
explicit CollectionAndDelimiter(Collection const c[N], std::string delim)
: collection(c), delimiter(std::move(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 <std::size_t N>
class CollectionAndDelimiter<char[N]>
{
public:
char const* collection;
std::string const delimiter;
explicit CollectionAndDelimiter(char const c[N], std::string delim)
: collection(c), delimiter(std::move(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

View File

@@ -0,0 +1,42 @@
//------------------------------------------------------------------------------
/*
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_BASICS_MAKE_SSLCONTEXT_H_INCLUDED
#define RIPPLE_BASICS_MAKE_SSLCONTEXT_H_INCLUDED
#include <boost/asio/ssl/context.hpp>
#include <string>
namespace ripple {
/** Create a self-signed SSL context that allows anonymous Diffie Hellman. */
std::shared_ptr<boost::asio::ssl::context>
make_SSLContext(std::string const& cipherList);
/** Create an authenticated SSL context using the specified files. */
std::shared_ptr<boost::asio::ssl::context>
make_SSLContextAuthed(
std::string const& keyFile,
std::string const& certFile,
std::string const& chainFile,
std::string const& cipherList);
} // namespace ripple
#endif

View File

@@ -0,0 +1,46 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012-2015 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_BASICS_MULDIV_H_INCLUDED
#define RIPPLE_BASICS_MULDIV_H_INCLUDED
#include <cstdint>
#include <limits>
#include <optional>
#include <utility>
namespace ripple {
auto constexpr muldiv_max = std::numeric_limits<std::uint64_t>::max();
/** Return value*mul/div accurately.
Computes the result of the multiplication and division in
a single step, avoiding overflow and retaining precision.
Throws:
None
Returns:
`std::optional`:
`std::nullopt` if the calculation overflows. Otherwise, `value * mul
/ div`.
*/
std::optional<std::uint64_t>
mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div);
} // namespace ripple
#endif

View File

@@ -0,0 +1,409 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2021 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_BASICS_PARTITIONED_UNORDERED_MAP_H
#define RIPPLE_BASICS_PARTITIONED_UNORDERED_MAP_H
#include <cassert>
#include <functional>
#include <optional>
#include <thread>
#include <unordered_map>
#include <utility>
#include <vector>
namespace ripple {
template <typename Key>
std::size_t
partitioner(Key const& key, std::size_t const numPartitions);
template <
typename Key,
typename Value,
typename Hash,
typename Pred = std::equal_to<Key>,
typename Alloc = std::allocator<std::pair<const Key, Value>>>
class partitioned_unordered_map
{
std::size_t partitions_;
public:
using key_type = Key;
using mapped_type = Value;
using value_type = std::pair<Key const, mapped_type>;
using size_type = std::size_t;
using difference_type = std::size_t;
using hasher = Hash;
using key_equal = Pred;
using allocator_type = Alloc;
using reference = value_type&;
using const_reference = value_type const&;
using pointer = value_type*;
using const_pointer = value_type const*;
using map_type = std::
unordered_map<key_type, mapped_type, hasher, key_equal, allocator_type>;
using partition_map_type = std::vector<map_type>;
struct iterator
{
using iterator_category = std::forward_iterator_tag;
partition_map_type* map_{nullptr};
typename partition_map_type::iterator ait_;
typename map_type::iterator mit_;
iterator() = default;
iterator(partition_map_type* map) : map_(map)
{
}
reference
operator*() const
{
return *mit_;
}
pointer
operator->() const
{
return &(*mit_);
}
void
inc()
{
++mit_;
while (mit_ == ait_->end())
{
++ait_;
if (ait_ == map_->end())
return;
mit_ = ait_->begin();
}
}
// ++it
iterator&
operator++()
{
inc();
return *this;
}
// it++
iterator
operator++(int)
{
iterator tmp(*this);
inc();
return tmp;
}
friend bool
operator==(iterator const& lhs, iterator const& rhs)
{
return lhs.map_ == rhs.map_ && lhs.ait_ == rhs.ait_ &&
lhs.mit_ == rhs.mit_;
}
friend bool
operator!=(iterator const& lhs, iterator const& rhs)
{
return !(lhs == rhs);
}
};
struct const_iterator
{
using iterator_category = std::forward_iterator_tag;
partition_map_type* map_{nullptr};
typename partition_map_type::iterator ait_;
typename map_type::iterator mit_;
const_iterator() = default;
const_iterator(partition_map_type* map) : map_(map)
{
}
const_iterator(iterator const& orig)
{
map_ = orig.map_;
ait_ = orig.ait_;
mit_ = orig.mit_;
}
const_reference
operator*() const
{
return *mit_;
}
const_pointer
operator->() const
{
return &(*mit_);
}
void
inc()
{
++mit_;
while (mit_ == ait_->end())
{
++ait_;
if (ait_ == map_->end())
return;
mit_ = ait_->begin();
}
}
// ++it
const_iterator&
operator++()
{
inc();
return *this;
}
// it++
const_iterator
operator++(int)
{
const_iterator tmp(*this);
inc();
return tmp;
}
friend bool
operator==(const_iterator const& lhs, const_iterator const& rhs)
{
return lhs.map_ == rhs.map_ && lhs.ait_ == rhs.ait_ &&
lhs.mit_ == rhs.mit_;
}
friend bool
operator!=(const_iterator const& lhs, const_iterator const& rhs)
{
return !(lhs == rhs);
}
};
private:
std::size_t
partitioner(Key const& key) const
{
return ripple::partitioner(key, partitions_);
}
template <class T>
static void
end(T& it)
{
it.ait_ = it.map_->end();
it.mit_ = it.map_->back().end();
}
template <class T>
static void
begin(T& it)
{
for (it.ait_ = it.map_->begin(); it.ait_ != it.map_->end(); ++it.ait_)
{
if (it.ait_->begin() == it.ait_->end())
continue;
it.mit_ = it.ait_->begin();
return;
}
end(it);
}
public:
partitioned_unordered_map(
std::optional<std::size_t> partitions = std::nullopt)
{
// Set partitions to the number of hardware threads if the parameter
// is either empty or set to 0.
partitions_ = partitions && *partitions
? *partitions
: std::thread::hardware_concurrency();
map_.resize(partitions_);
assert(partitions_);
}
std::size_t
partitions() const
{
return partitions_;
}
partition_map_type&
map()
{
return map_;
}
iterator
begin()
{
iterator it(&map_);
begin(it);
return it;
}
const_iterator
cbegin() const
{
const_iterator it(&map_);
begin(it);
return it;
}
const_iterator
begin() const
{
return cbegin();
}
iterator
end()
{
iterator it(&map_);
end(it);
return it;
}
const_iterator
cend() const
{
const_iterator it(&map_);
end(it);
return it;
}
const_iterator
end() const
{
return cend();
}
private:
template <class T>
void
find(key_type const& key, T& it) const
{
it.ait_ = it.map_->begin() + partitioner(key);
it.mit_ = it.ait_->find(key);
if (it.mit_ == it.ait_->end())
end(it);
}
public:
iterator
find(key_type const& key)
{
iterator it(&map_);
find(key, it);
return it;
}
const_iterator
find(key_type const& key) const
{
const_iterator it(&map_);
find(key, it);
return it;
}
template <class T, class U>
std::pair<iterator, bool>
emplace(std::piecewise_construct_t const&, T&& keyTuple, U&& valueTuple)
{
auto const& key = std::get<0>(keyTuple);
iterator it(&map_);
it.ait_ = it.map_->begin() + partitioner(key);
auto [eit, inserted] = it.ait_->emplace(
std::piecewise_construct,
std::forward<T>(keyTuple),
std::forward<U>(valueTuple));
it.mit_ = eit;
return {it, inserted};
}
template <class T, class U>
std::pair<iterator, bool>
emplace(T&& key, U&& val)
{
iterator it(&map_);
it.ait_ = it.map_->begin() + partitioner(key);
auto [eit, inserted] =
it.ait_->emplace(std::forward<T>(key), std::forward<U>(val));
it.mit_ = eit;
return {it, inserted};
}
void
clear()
{
for (auto& p : map_)
p.clear();
}
iterator
erase(const_iterator position)
{
iterator it(&map_);
it.ait_ = position.ait_;
it.mit_ = position.ait_->erase(position.mit_);
while (it.mit_ == it.ait_->end())
{
++it.ait_;
if (it.ait_ == it.map_->end())
break;
it.mit_ = it.ait_->begin();
}
return it;
}
std::size_t
size() const
{
std::size_t ret = 0;
for (auto& p : map_)
ret += p.size();
return ret;
}
Value&
operator[](Key const& key)
{
return map_[partitioner(key)][key];
}
private:
mutable partition_map_type map_{};
};
} // namespace ripple
#endif // RIPPLE_BASICS_PARTITIONED_UNORDERED_MAP_H

View File

@@ -0,0 +1,210 @@
//------------------------------------------------------------------------------
/*
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_BASICS_RANDOM_H_INCLUDED
#define RIPPLE_BASICS_RANDOM_H_INCLUDED
#include <ripple/beast/xor_shift_engine.h>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <limits>
#include <mutex>
#include <random>
#include <type_traits>
namespace ripple {
#ifndef __INTELLISENSE__
static_assert(
std::is_integral<beast::xor_shift_engine::result_type>::value &&
std::is_unsigned<beast::xor_shift_engine::result_type>::value,
"The Ripple default PRNG engine must return an unsigned integral type.");
static_assert(
std::numeric_limits<beast::xor_shift_engine::result_type>::max() >=
std::numeric_limits<std::uint64_t>::max(),
"The Ripple default PRNG engine return must be at least 64 bits wide.");
#endif
namespace detail {
// Determines if a type can be called like an Engine
template <class Engine, class Result = typename Engine::result_type>
using is_engine = std::is_invocable_r<Result, Engine>;
} // namespace detail
/** Return the default random engine.
This engine is guaranteed to be deterministic, but by
default will be randomly seeded. It is NOT cryptographically
secure and MUST NOT be used to generate randomness that
will be used for keys, secure cookies, IVs, padding, etc.
Each thread gets its own instance of the engine which
will be randomly seeded.
*/
inline beast::xor_shift_engine&
default_prng()
{
// This is used to seed the thread-specific PRNGs on demand
static beast::xor_shift_engine seeder = [] {
std::random_device rng;
std::uniform_int_distribution<std::uint64_t> distribution{1};
return beast::xor_shift_engine(distribution(rng));
}();
// This protects the seeder
static std::mutex m;
// The thread-specific PRNGs:
thread_local beast::xor_shift_engine engine = [] {
std::uint64_t seed;
{
std::lock_guard lk(m);
std::uniform_int_distribution<std::uint64_t> distribution{1};
seed = distribution(seeder);
}
return beast::xor_shift_engine{seed};
}();
return engine;
}
/** Return a uniformly distributed random integer.
@param min The smallest value to return. If not specified
the value defaults to 0.
@param max The largest value to return. If not specified
the value defaults to the largest value that
can be represented.
The randomness is generated by the specified engine (or
the default engine if one is not specified). The result
is cryptographically secure only when the engine passed
into the function is cryptographically secure.
@note The range is always a closed interval, so calling
rand_int(-5, 15) can return any integer in the
closed interval [-5, 15]; similarly, calling
rand_int(7) can return any integer in the closed
interval [0, 7].
*/
/** @{ */
template <class Engine, class Integral>
std::enable_if_t<
std::is_integral<Integral>::value && detail::is_engine<Engine>::value,
Integral>
rand_int(Engine& engine, Integral min, Integral max)
{
assert(max > min);
// This should have no state and constructing it should
// be very cheap. If that turns out not to be the case
// it could be hand-optimized.
return std::uniform_int_distribution<Integral>(min, max)(engine);
}
template <class Integral>
std::enable_if_t<std::is_integral<Integral>::value, Integral>
rand_int(Integral min, Integral max)
{
return rand_int(default_prng(), min, max);
}
template <class Engine, class Integral>
std::enable_if_t<
std::is_integral<Integral>::value && detail::is_engine<Engine>::value,
Integral>
rand_int(Engine& engine, Integral max)
{
return rand_int(engine, Integral(0), max);
}
template <class Integral>
std::enable_if_t<std::is_integral<Integral>::value, Integral>
rand_int(Integral max)
{
return rand_int(default_prng(), max);
}
template <class Integral, class Engine>
std::enable_if_t<
std::is_integral<Integral>::value && detail::is_engine<Engine>::value,
Integral>
rand_int(Engine& engine)
{
return rand_int(engine, std::numeric_limits<Integral>::max());
}
template <class Integral = int>
std::enable_if_t<std::is_integral<Integral>::value, Integral>
rand_int()
{
return rand_int(default_prng(), std::numeric_limits<Integral>::max());
}
/** @} */
/** Return a random byte */
/** @{ */
template <class Byte, class Engine>
std::enable_if_t<
(std::is_same<Byte, unsigned char>::value ||
std::is_same<Byte, std::uint8_t>::value) &&
detail::is_engine<Engine>::value,
Byte>
rand_byte(Engine& engine)
{
return static_cast<Byte>(rand_int<Engine, std::uint32_t>(
engine,
std::numeric_limits<Byte>::min(),
std::numeric_limits<Byte>::max()));
}
template <class Byte = std::uint8_t>
std::enable_if_t<
(std::is_same<Byte, unsigned char>::value ||
std::is_same<Byte, std::uint8_t>::value),
Byte>
rand_byte()
{
return rand_byte<Byte>(default_prng());
}
/** @} */
/** Return a random boolean value */
/** @{ */
template <class Engine>
inline bool
rand_bool(Engine& engine)
{
return rand_int(engine, 1) == 1;
}
inline bool
rand_bool()
{
return rand_bool(default_prng());
}
/** @} */
} // namespace ripple
#endif // RIPPLE_BASICS_RANDOM_H_INCLUDED

View File

@@ -0,0 +1,105 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2018 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_BASICS_SAFE_CAST_H_INCLUDED
#define RIPPLE_BASICS_SAFE_CAST_H_INCLUDED
#include <type_traits>
namespace ripple {
// safe_cast adds compile-time checks to a static_cast to ensure that
// the destination can hold all values of the source. This is particularly
// handy when the source or destination is an enumeration type.
template <class Dest, class Src>
static constexpr bool is_safetocasttovalue_v =
(std::is_integral_v<Src> && std::is_integral_v<Dest>)&&(
std::is_signed<Src>::value || std::is_unsigned<Dest>::value) &&
(std::is_signed<Src>::value != std::is_signed<Dest>::value
? sizeof(Dest) > sizeof(Src)
: sizeof(Dest) >= sizeof(Src));
template <class Dest, class Src>
inline constexpr std::
enable_if_t<std::is_integral_v<Dest> && std::is_integral_v<Src>, Dest>
safe_cast(Src s) noexcept
{
static_assert(
std::is_signed_v<Dest> || std::is_unsigned_v<Src>,
"Cannot cast signed to unsigned");
constexpr unsigned not_same =
std::is_signed_v<Dest> != std::is_signed_v<Src>;
static_assert(
sizeof(Dest) >= sizeof(Src) + not_same,
"Destination is too small to hold all values of source");
return static_cast<Dest>(s);
}
template <class Dest, class Src>
inline constexpr std::
enable_if_t<std::is_enum_v<Dest> && std::is_integral_v<Src>, Dest>
safe_cast(Src s) noexcept
{
return static_cast<Dest>(safe_cast<std::underlying_type_t<Dest>>(s));
}
template <class Dest, class Src>
inline constexpr std::
enable_if_t<std::is_integral_v<Dest> && std::is_enum_v<Src>, Dest>
safe_cast(Src s) noexcept
{
return safe_cast<Dest>(static_cast<std::underlying_type_t<Src>>(s));
}
// unsafe_cast explicitly flags a static_cast as not necessarily able to hold
// all values of the source. It includes a compile-time check so that if
// underlying types become safe, it can be converted to a safe_cast.
template <class Dest, class Src>
inline constexpr std::
enable_if_t<std::is_integral_v<Dest> && std::is_integral_v<Src>, Dest>
unsafe_cast(Src s) noexcept
{
static_assert(
!is_safetocasttovalue_v<Dest, Src>,
"Only unsafe if casting signed to unsigned or "
"destination is too small");
return static_cast<Dest>(s);
}
template <class Dest, class Src>
inline constexpr std::
enable_if_t<std::is_enum_v<Dest> && std::is_integral_v<Src>, Dest>
unsafe_cast(Src s) noexcept
{
return static_cast<Dest>(unsafe_cast<std::underlying_type_t<Dest>>(s));
}
template <class Dest, class Src>
inline constexpr std::
enable_if_t<std::is_integral_v<Dest> && std::is_enum_v<Src>, Dest>
unsafe_cast(Src s) noexcept
{
return unsafe_cast<Dest>(static_cast<std::underlying_type_t<Src>>(s));
}
} // namespace ripple
#endif

191
include/xrpl/basics/scope.h Normal file
View File

@@ -0,0 +1,191 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2021 Ripple 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_BASICS_SCOPE_H_INCLUDED
#define RIPPLE_BASICS_SCOPE_H_INCLUDED
#include <exception>
#include <type_traits>
#include <utility>
namespace ripple {
// RAII scope helpers. As specified in Library Fundamental, Version 3
// Basic design of idea: https://www.youtube.com/watch?v=WjTrfoiB0MQ
// Specification:
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/n4873.html#scopeguard
// This implementation deviates from the spec slightly:
// The scope_exit and scope_fail constructors taking a functor are not
// permitted to throw an exception. This was done because some compilers
// did not like the superfluous try/catch in the common instantiations
// where the construction was noexcept. Instead a static_assert is used
// to enforce this restriction.
template <class EF>
class scope_exit
{
EF exit_function_;
bool execute_on_destruction_{true};
public:
~scope_exit()
{
if (execute_on_destruction_)
exit_function_();
}
scope_exit(scope_exit&& rhs) noexcept(
std::is_nothrow_move_constructible_v<EF> ||
std::is_nothrow_copy_constructible_v<EF>)
: exit_function_{std::forward<EF>(rhs.exit_function_)}
, execute_on_destruction_{rhs.execute_on_destruction_}
{
rhs.release();
}
scope_exit&
operator=(scope_exit&&) = delete;
template <class EFP>
explicit scope_exit(
EFP&& f,
std::enable_if_t<
!std::is_same_v<std::remove_cv_t<EFP>, scope_exit> &&
std::is_constructible_v<EF, EFP>>* = 0) noexcept
: exit_function_{std::forward<EFP>(f)}
{
static_assert(
std::
is_nothrow_constructible_v<EF, decltype(std::forward<EFP>(f))>);
}
void
release() noexcept
{
execute_on_destruction_ = false;
}
};
template <class EF>
scope_exit(EF) -> scope_exit<EF>;
template <class EF>
class scope_fail
{
EF exit_function_;
bool execute_on_destruction_{true};
int uncaught_on_creation_{std::uncaught_exceptions()};
public:
~scope_fail()
{
if (execute_on_destruction_ &&
std::uncaught_exceptions() > uncaught_on_creation_)
exit_function_();
}
scope_fail(scope_fail&& rhs) noexcept(
std::is_nothrow_move_constructible_v<EF> ||
std::is_nothrow_copy_constructible_v<EF>)
: exit_function_{std::forward<EF>(rhs.exit_function_)}
, execute_on_destruction_{rhs.execute_on_destruction_}
, uncaught_on_creation_{rhs.uncaught_on_creation_}
{
rhs.release();
}
scope_fail&
operator=(scope_fail&&) = delete;
template <class EFP>
explicit scope_fail(
EFP&& f,
std::enable_if_t<
!std::is_same_v<std::remove_cv_t<EFP>, scope_fail> &&
std::is_constructible_v<EF, EFP>>* = 0) noexcept
: exit_function_{std::forward<EFP>(f)}
{
static_assert(
std::
is_nothrow_constructible_v<EF, decltype(std::forward<EFP>(f))>);
}
void
release() noexcept
{
execute_on_destruction_ = false;
}
};
template <class EF>
scope_fail(EF) -> scope_fail<EF>;
template <class EF>
class scope_success
{
EF exit_function_;
bool execute_on_destruction_{true};
int uncaught_on_creation_{std::uncaught_exceptions()};
public:
~scope_success() noexcept(noexcept(exit_function_()))
{
if (execute_on_destruction_ &&
std::uncaught_exceptions() <= uncaught_on_creation_)
exit_function_();
}
scope_success(scope_success&& rhs) noexcept(
std::is_nothrow_move_constructible_v<EF> ||
std::is_nothrow_copy_constructible_v<EF>)
: exit_function_{std::forward<EF>(rhs.exit_function_)}
, execute_on_destruction_{rhs.execute_on_destruction_}
, uncaught_on_creation_{rhs.uncaught_on_creation_}
{
rhs.release();
}
scope_success&
operator=(scope_success&&) = delete;
template <class EFP>
explicit scope_success(
EFP&& f,
std::enable_if_t<
!std::is_same_v<std::remove_cv_t<EFP>, scope_success> &&
std::is_constructible_v<EF, EFP>>* =
0) noexcept(std::is_nothrow_constructible_v<EF, EFP> || std::is_nothrow_constructible_v<EF, EFP&>)
: exit_function_{std::forward<EFP>(f)}
{
}
void
release() noexcept
{
execute_on_destruction_ = false;
}
};
template <class EF>
scope_success(EF) -> scope_success<EF>;
} // namespace ripple
#endif

View File

@@ -0,0 +1,223 @@
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright 2022, Nikolaos D. Bougalis <nikb@bougalis.net>
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_BASICS_SPINLOCK_H_INCLUDED
#define RIPPLE_BASICS_SPINLOCK_H_INCLUDED
#include <atomic>
#include <cassert>
#include <limits>
#include <type_traits>
#ifndef __aarch64__
#include <immintrin.h>
#endif
namespace ripple {
namespace detail {
/** Inform the processor that we are in a tight spin-wait loop.
Spinlocks caught in tight loops can result in the processor's pipeline
filling up with comparison operations, resulting in a misprediction at
the time the lock is finally acquired, necessitating pipeline flushing
which is ridiculously expensive and results in very high latency.
This function instructs the processor to "pause" for some architecture
specific amount of time, to prevent this.
*/
inline void
spin_pause() noexcept
{
#ifdef __aarch64__
asm volatile("yield");
#else
_mm_pause();
#endif
}
} // namespace detail
/** @{ */
/** Classes to handle arrays of spinlocks packed into a single atomic integer:
Packed spinlocks allow for tremendously space-efficient lock-sharding
but they come at a cost.
First, the implementation is necessarily low-level and uses advanced
features like memory ordering and highly platform-specific tricks to
maximize performance. This imposes a significant and ongoing cost to
developers.
Second, and perhaps most important, is that the packing of multiple
locks into a single integer which, albeit space-efficient, also has
performance implications stemming from data dependencies, increased
cache-coherency traffic between processors and heavier loads on the
processor's load/store units.
To be sure, these locks can have advantages but they are definitely
not general purpose locks and should not be thought of or used that
way. The use cases for them are likely few and far between; without
a compelling reason to use them, backed by profiling data, it might
be best to use one of the standard locking primitives instead. Note
that in most common platforms, `std::mutex` is so heavily optimized
that it can, usually, outperform spinlocks.
@tparam T An unsigned integral type (e.g. std::uint16_t)
*/
/** A class that grabs a single packed spinlock from an atomic integer.
This class meets the requirements of Lockable:
https://en.cppreference.com/w/cpp/named_req/Lockable
*/
template <class T>
class packed_spinlock
{
// clang-format off
static_assert(std::is_unsigned_v<T>);
static_assert(std::atomic<T>::is_always_lock_free);
static_assert(
std::is_same_v<decltype(std::declval<std::atomic<T>&>().fetch_or(0)), T> &&
std::is_same_v<decltype(std::declval<std::atomic<T>&>().fetch_and(0)), T>,
"std::atomic<T>::fetch_and(T) and std::atomic<T>::fetch_and(T) are required by packed_spinlock");
// clang-format on
private:
std::atomic<T>& bits_;
T const mask_;
public:
packed_spinlock(packed_spinlock const&) = delete;
packed_spinlock&
operator=(packed_spinlock const&) = delete;
/** A single spinlock packed inside the specified atomic
@param lock The atomic integer inside which the spinlock is packed.
@param index The index of the spinlock this object acquires.
@note For performance reasons, you should strive to have `lock` be
on a cacheline by itself.
*/
packed_spinlock(std::atomic<T>& lock, int index)
: bits_(lock), mask_(static_cast<T>(1) << index)
{
assert(index >= 0 && (mask_ != 0));
}
[[nodiscard]] bool
try_lock()
{
return (bits_.fetch_or(mask_, std::memory_order_acquire) & mask_) == 0;
}
void
lock()
{
while (!try_lock())
{
// The use of relaxed memory ordering here is intentional and
// serves to help reduce cache coherency traffic during times
// of contention by avoiding writes that would definitely not
// result in the lock being acquired.
while ((bits_.load(std::memory_order_relaxed) & mask_) != 0)
detail::spin_pause();
}
}
void
unlock()
{
bits_.fetch_and(~mask_, std::memory_order_release);
}
};
/** A spinlock implemented on top of an atomic integer.
@note Using `packed_spinlock` and `spinlock` against the same underlying
atomic integer can result in `spinlock` not being able to actually
acquire the lock during periods of high contention, because of how
the two locks operate: `spinlock` will spin trying to grab all the
bits at once, whereas any given `packed_spinlock` will only try to
grab one bit at a time. Caveat emptor.
This class meets the requirements of Lockable:
https://en.cppreference.com/w/cpp/named_req/Lockable
*/
template <class T>
class spinlock
{
static_assert(std::is_unsigned_v<T>);
static_assert(std::atomic<T>::is_always_lock_free);
private:
std::atomic<T>& lock_;
public:
spinlock(spinlock const&) = delete;
spinlock&
operator=(spinlock const&) = delete;
/** Grabs the
@param lock The atomic integer to spin against.
@note For performance reasons, you should strive to have `lock` be
on a cacheline by itself.
*/
spinlock(std::atomic<T>& lock) : lock_(lock)
{
}
[[nodiscard]] bool
try_lock()
{
T expected = 0;
return lock_.compare_exchange_weak(
expected,
std::numeric_limits<T>::max(),
std::memory_order_acquire,
std::memory_order_relaxed);
}
void
lock()
{
while (!try_lock())
{
// The use of relaxed memory ordering here is intentional and
// serves to help reduce cache coherency traffic during times
// of contention by avoiding writes that would definitely not
// result in the lock being acquired.
while (lock_.load(std::memory_order_relaxed) != 0)
detail::spin_pause();
}
}
void
unlock()
{
lock_.store(0, std::memory_order_release);
}
};
/** @} */
} // namespace ripple
#endif

View File

@@ -0,0 +1,52 @@
//------------------------------------------------------------------------------
/*
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_BASICS_STRHEX_H_INCLUDED
#define RIPPLE_BASICS_STRHEX_H_INCLUDED
#include <boost/algorithm/hex.hpp>
#include <boost/endian/conversion.hpp>
namespace ripple {
template <class FwdIt>
std::string
strHex(FwdIt begin, FwdIt end)
{
static_assert(
std::is_convertible<
typename std::iterator_traits<FwdIt>::iterator_category,
std::forward_iterator_tag>::value,
"FwdIt must be a forward iterator");
std::string result;
result.reserve(2 * std::distance(begin, end));
boost::algorithm::hex(begin, end, std::back_inserter(result));
return result;
}
template <class T, class = decltype(std::declval<T>().begin())>
std::string
strHex(T const& from)
{
return strHex(from.begin(), from.end());
}
} // namespace ripple
#endif

View File

@@ -0,0 +1,227 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright 2014, Nikolaos D. Bougalis <nikb@bougalis.net>
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_TAGGED_INTEGER_H_INCLUDED
#define BEAST_UTILITY_TAGGED_INTEGER_H_INCLUDED
#include <ripple/beast/hash/hash_append.h>
#include <boost/operators.hpp>
#include <functional>
#include <iostream>
#include <type_traits>
#include <utility>
namespace ripple {
/** A type-safe wrap around standard integral types
The tag is used to implement type safety, catching mismatched types at
compile time. Multiple instantiations wrapping the same underlying integral
type are distinct types (distinguished by tag) and will not interoperate. A
tagged_integer supports all the usual assignment, arithmetic, comparison and
shifting operations defined for the underlying type
The tag is not meant as a unit, which would require restricting the set of
allowed arithmetic operations.
*/
template <class Int, class Tag>
class tagged_integer
: boost::totally_ordered<
tagged_integer<Int, Tag>,
boost::integer_arithmetic<
tagged_integer<Int, Tag>,
boost::bitwise<
tagged_integer<Int, Tag>,
boost::unit_steppable<
tagged_integer<Int, Tag>,
boost::shiftable<tagged_integer<Int, Tag>>>>>>
{
private:
Int m_value;
public:
using value_type = Int;
using tag_type = Tag;
tagged_integer() = default;
template <
class OtherInt,
class = typename std::enable_if<
std::is_integral<OtherInt>::value &&
sizeof(OtherInt) <= sizeof(Int)>::type>
explicit constexpr tagged_integer(OtherInt value) noexcept : m_value(value)
{
static_assert(
sizeof(tagged_integer) == sizeof(Int),
"tagged_integer is adding padding");
}
bool
operator<(const tagged_integer& rhs) const noexcept
{
return m_value < rhs.m_value;
}
bool
operator==(const tagged_integer& rhs) const noexcept
{
return m_value == rhs.m_value;
}
tagged_integer&
operator+=(tagged_integer const& rhs) noexcept
{
m_value += rhs.m_value;
return *this;
}
tagged_integer&
operator-=(tagged_integer const& rhs) noexcept
{
m_value -= rhs.m_value;
return *this;
}
tagged_integer&
operator*=(tagged_integer const& rhs) noexcept
{
m_value *= rhs.m_value;
return *this;
}
tagged_integer&
operator/=(tagged_integer const& rhs) noexcept
{
m_value /= rhs.m_value;
return *this;
}
tagged_integer&
operator%=(tagged_integer const& rhs) noexcept
{
m_value %= rhs.m_value;
return *this;
}
tagged_integer&
operator|=(tagged_integer const& rhs) noexcept
{
m_value |= rhs.m_value;
return *this;
}
tagged_integer&
operator&=(tagged_integer const& rhs) noexcept
{
m_value &= rhs.m_value;
return *this;
}
tagged_integer&
operator^=(tagged_integer const& rhs) noexcept
{
m_value ^= rhs.m_value;
return *this;
}
tagged_integer&
operator<<=(const tagged_integer& rhs) noexcept
{
m_value <<= rhs.m_value;
return *this;
}
tagged_integer&
operator>>=(const tagged_integer& rhs) noexcept
{
m_value >>= rhs.m_value;
return *this;
}
tagged_integer
operator~() const noexcept
{
return tagged_integer{~m_value};
}
tagged_integer
operator+() const noexcept
{
return *this;
}
tagged_integer
operator-() const noexcept
{
return tagged_integer{-m_value};
}
tagged_integer&
operator++() noexcept
{
++m_value;
return *this;
}
tagged_integer&
operator--() noexcept
{
--m_value;
return *this;
}
explicit operator Int() const noexcept
{
return m_value;
}
friend std::ostream&
operator<<(std::ostream& s, tagged_integer const& t)
{
s << t.m_value;
return s;
}
friend std::istream&
operator>>(std::istream& s, tagged_integer& t)
{
s >> t.m_value;
return s;
}
friend std::string
to_string(tagged_integer const& t)
{
return std::to_string(t.m_value);
}
};
} // namespace ripple
namespace beast {
template <class Int, class Tag, class HashAlgorithm>
struct is_contiguously_hashable<ripple::tagged_integer<Int, Tag>, HashAlgorithm>
: public is_contiguously_hashable<Int, HashAlgorithm>
{
explicit is_contiguously_hashable() = default;
};
} // namespace beast
#endif