mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-04 18:55:49 +00:00
667 lines
17 KiB
C++
667 lines
17 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
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, class Tag>
|
|
[[nodiscard]] inline constexpr std::strong_ordering
|
|
operator<=>(base_uint<Bits, Tag> const& lhs, base_uint<Bits, Tag> const& rhs)
|
|
{
|
|
return std::lexicographical_compare_three_way(
|
|
lhs.cbegin(),
|
|
lhs.cend(),
|
|
rhs.cbegin(),
|
|
rhs.cend(),
|
|
std::compare_three_way{});
|
|
}
|
|
|
|
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
|