mirror of
https://github.com/oxen-io/lokinet.git
synced 2024-11-17 15:25:35 +00:00
323 lines
6.5 KiB
C++
323 lines
6.5 KiB
C++
#pragma once
|
|
#include <array>
|
|
#include <cstdint>
|
|
#include <algorithm>
|
|
#include <functional>
|
|
|
|
#include "../util/meta/traits.hpp"
|
|
#include "../util/endian.hpp"
|
|
|
|
namespace llarp
|
|
{
|
|
/// 128-bit unsigned integer. Does *not* support
|
|
/// multiplication/division/modulus.
|
|
struct uint128_t
|
|
{
|
|
// Swap order on little/big endian so that the first byte of the struct is
|
|
// always most significant on big endian and least significant on little
|
|
// endian.
|
|
#ifdef __BIG_ENDIAN__
|
|
uint64_t upper, lower;
|
|
#else
|
|
uint64_t lower, upper;
|
|
#endif
|
|
|
|
// Initializes with 0s
|
|
constexpr uint128_t() : uint128_t{0, 0}
|
|
{}
|
|
|
|
// Initializes with least-significant value
|
|
constexpr uint128_t(uint64_t lower) : uint128_t{0, lower}
|
|
{}
|
|
|
|
// Initializes with upper and lower values
|
|
constexpr uint128_t(uint64_t upper, uint64_t lower)
|
|
// clang-format off
|
|
#ifdef __BIG_ENDIAN__
|
|
: upper{upper}, lower{lower}
|
|
#else
|
|
: lower{lower}, upper{upper}
|
|
#endif
|
|
// clang-format on
|
|
{}
|
|
|
|
constexpr uint128_t(const uint128_t&) = default;
|
|
constexpr uint128_t(uint128_t&&) = default;
|
|
constexpr uint128_t&
|
|
operator=(const uint128_t&) = default;
|
|
constexpr uint128_t&
|
|
operator=(uint128_t&&) = default;
|
|
|
|
// bitwise and
|
|
constexpr uint128_t&
|
|
operator&=(const uint128_t& o)
|
|
{
|
|
upper &= o.upper;
|
|
lower &= o.lower;
|
|
return *this;
|
|
}
|
|
constexpr uint128_t
|
|
operator&(const uint128_t& o) const
|
|
{
|
|
uint128_t result = *this;
|
|
result &= o;
|
|
return result;
|
|
}
|
|
|
|
// bitwise or
|
|
constexpr uint128_t&
|
|
operator|=(const uint128_t& o)
|
|
{
|
|
upper |= o.upper;
|
|
lower |= o.lower;
|
|
return *this;
|
|
}
|
|
constexpr uint128_t
|
|
operator|(const uint128_t& o) const
|
|
{
|
|
uint128_t result = *this;
|
|
result |= o;
|
|
return result;
|
|
}
|
|
|
|
// bitwise xor
|
|
constexpr uint128_t&
|
|
operator^=(const uint128_t& o)
|
|
{
|
|
upper ^= o.upper;
|
|
lower ^= o.lower;
|
|
return *this;
|
|
}
|
|
constexpr uint128_t
|
|
operator^(const uint128_t& o) const
|
|
{
|
|
uint128_t result = *this;
|
|
result ^= o;
|
|
return result;
|
|
}
|
|
|
|
// bitwise not
|
|
constexpr uint128_t
|
|
operator~() const
|
|
{
|
|
return {~upper, ~lower};
|
|
}
|
|
|
|
// bool: true if any bit set
|
|
explicit constexpr operator bool() const
|
|
{
|
|
return static_cast<bool>(lower) || static_cast<bool>(upper);
|
|
}
|
|
|
|
// Casting to basic unsigned int types: casts away upper bits
|
|
explicit constexpr operator uint8_t() const
|
|
{
|
|
return static_cast<uint8_t>(lower);
|
|
}
|
|
explicit constexpr operator uint16_t() const
|
|
{
|
|
return static_cast<uint16_t>(lower);
|
|
}
|
|
explicit constexpr operator uint32_t() const
|
|
{
|
|
return static_cast<uint32_t>(lower);
|
|
}
|
|
explicit constexpr operator uint64_t() const
|
|
{
|
|
return lower;
|
|
}
|
|
|
|
constexpr bool
|
|
operator==(const uint128_t& b) const
|
|
{
|
|
return lower == b.lower && upper == b.upper;
|
|
}
|
|
|
|
constexpr bool
|
|
operator!=(const uint128_t& b) const
|
|
{
|
|
return lower != b.lower || upper != b.upper;
|
|
}
|
|
|
|
constexpr bool
|
|
operator<(const uint128_t& b) const
|
|
{
|
|
return upper < b.upper || (upper == b.upper && lower < b.lower);
|
|
}
|
|
|
|
constexpr bool
|
|
operator<=(const uint128_t& b) const
|
|
{
|
|
return upper < b.upper || (upper == b.upper && lower <= b.lower);
|
|
}
|
|
|
|
constexpr bool
|
|
operator>(const uint128_t& b) const
|
|
{
|
|
return upper > b.upper || (upper == b.upper && lower > b.lower);
|
|
}
|
|
|
|
constexpr bool
|
|
operator>=(const uint128_t& b) const
|
|
{
|
|
return upper > b.upper || (upper == b.upper && lower >= b.lower);
|
|
}
|
|
|
|
constexpr uint128_t&
|
|
operator++()
|
|
{
|
|
if (++lower == 0)
|
|
++upper;
|
|
return *this;
|
|
}
|
|
|
|
constexpr uint128_t
|
|
operator++(int)
|
|
{
|
|
auto copy = *this;
|
|
++*this;
|
|
return copy;
|
|
}
|
|
|
|
constexpr uint128_t&
|
|
operator+=(const uint128_t& b)
|
|
{
|
|
lower += b.lower;
|
|
if (lower < b.lower)
|
|
++upper;
|
|
upper += b.upper;
|
|
return *this;
|
|
}
|
|
constexpr uint128_t
|
|
operator+(const uint128_t& b) const
|
|
{
|
|
uint128_t result = *this;
|
|
result += b;
|
|
return result;
|
|
}
|
|
|
|
constexpr uint128_t&
|
|
operator-=(const uint128_t& b)
|
|
{
|
|
if (b.lower > lower)
|
|
--upper;
|
|
lower -= b.lower;
|
|
upper -= b.upper;
|
|
return *this;
|
|
}
|
|
constexpr uint128_t
|
|
operator-(const uint128_t& b) const
|
|
{
|
|
uint128_t result = *this;
|
|
result -= b;
|
|
return result;
|
|
}
|
|
|
|
constexpr uint128_t&
|
|
operator<<=(uint64_t shift)
|
|
{
|
|
if (shift == 0)
|
|
{}
|
|
else if (shift < 64)
|
|
{
|
|
upper = upper << shift | (lower >> (64 - shift));
|
|
lower <<= shift;
|
|
}
|
|
else if (shift == 64)
|
|
{
|
|
upper = lower;
|
|
lower = 0;
|
|
}
|
|
else if (shift < 128)
|
|
{
|
|
upper = lower << (shift - 64);
|
|
lower = 0;
|
|
}
|
|
else
|
|
{
|
|
upper = lower = 0;
|
|
}
|
|
return *this;
|
|
}
|
|
constexpr uint128_t
|
|
operator<<(uint64_t shift) const
|
|
{
|
|
uint128_t result = *this;
|
|
result <<= shift;
|
|
return result;
|
|
}
|
|
|
|
constexpr uint128_t&
|
|
operator>>=(uint64_t shift)
|
|
{
|
|
if (shift == 0)
|
|
{}
|
|
else if (shift < 64)
|
|
{
|
|
lower = lower >> shift | upper << (64 - shift);
|
|
upper >>= shift;
|
|
}
|
|
else if (shift == 64)
|
|
{
|
|
lower = upper;
|
|
upper = 0;
|
|
}
|
|
else if (shift < 128)
|
|
{
|
|
lower = upper >> (shift - 64);
|
|
upper = 0;
|
|
}
|
|
else
|
|
{
|
|
upper = lower = 0;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
constexpr uint128_t
|
|
operator>>(uint64_t shift) const
|
|
{
|
|
uint128_t result = *this;
|
|
result >>= shift;
|
|
return result;
|
|
}
|
|
};
|
|
|
|
static_assert(sizeof(uint128_t) == 16, "uint128_t has unexpected size (padding?)");
|
|
|
|
} // namespace llarp
|
|
|
|
namespace std
|
|
{
|
|
// Hash function for uint128_t
|
|
template <>
|
|
struct hash<llarp::uint128_t>
|
|
{
|
|
size_t
|
|
operator()(const llarp::uint128_t& i) const
|
|
{
|
|
size_t h = std::hash<uint64_t>()(i.lower);
|
|
h ^= std::hash<uint64_t>()(i.upper) + 0x9e3779b9 + (h << 6) + (h >> 2);
|
|
return h;
|
|
}
|
|
};
|
|
} // namespace std
|
|
|
|
inline llarp::uint128_t
|
|
ntoh128(llarp::uint128_t i)
|
|
{
|
|
#ifdef __BIG_ENDIAN__
|
|
return i;
|
|
#else
|
|
const auto loSwapped = htobe64(i.lower);
|
|
const auto hiSwapped = htobe64(i.upper);
|
|
return {loSwapped, hiSwapped};
|
|
#endif
|
|
}
|
|
|
|
inline llarp::uint128_t
|
|
hton128(llarp::uint128_t i)
|
|
{
|
|
return ntoh128(i); // Same bit flipping as n-to-h
|
|
}
|