mirror of https://github.com/oxen-io/lokinet
parent
5ce6c01476
commit
0839c16f19
@ -1 +0,0 @@
|
||||
Subproject commit aa844899c937bde5d2b24f276b59997e5b668bde
|
@ -0,0 +1,267 @@
|
||||
#pragma once
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
#include "../util/meta/traits.hpp"
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
/// 128-bit unsigned integer. Does *not* support
|
||||
/// multiplication/division/modulus.
|
||||
struct uint128_t
|
||||
{
|
||||
uint64_t lower, upper;
|
||||
|
||||
private:
|
||||
template < typename BinaryOperator,
|
||||
typename = traits::void_t< decltype(BinaryOperator{}(0, 0)) > >
|
||||
constexpr uint128_t(const uint128_t& a, const uint128_t& b,
|
||||
BinaryOperator op)
|
||||
{
|
||||
lower = op(a.lower, b.lower);
|
||||
upper = op(a.upper, b.upper);
|
||||
}
|
||||
|
||||
template < typename UnaryOperator,
|
||||
typename = traits::void_t< decltype(UnaryOperator{}(0)) > >
|
||||
constexpr uint128_t(const uint128_t& a, UnaryOperator op)
|
||||
{
|
||||
lower = op(a.lower);
|
||||
upper = op(a.upper);
|
||||
}
|
||||
|
||||
public:
|
||||
// Initializes with 0s
|
||||
constexpr uint128_t() : lower{0}, upper{0}
|
||||
{
|
||||
}
|
||||
|
||||
// Initializes with least-significant value
|
||||
constexpr uint128_t(uint64_t lower) : lower{lower}, upper{0}
|
||||
{
|
||||
}
|
||||
|
||||
// Initializes with upper and lower values
|
||||
constexpr uint128_t(uint64_t upper, uint64_t lower)
|
||||
: lower{lower}, upper{upper}
|
||||
{
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
constexpr uint128_t operator&(const uint128_t& o) const
|
||||
{
|
||||
return {*this, o, std::bit_and< uint64_t >{}};
|
||||
}
|
||||
|
||||
/*
|
||||
template <typename T, typename = std::enable_if_t<std::is_integral<T>::value
|
||||
&& std::is_unsigned<T>::value && sizeof(T) <= sizeof(uint64_t)>> constexpr T
|
||||
operator&(T o) const
|
||||
{
|
||||
return lower & o;
|
||||
}
|
||||
*/
|
||||
|
||||
constexpr uint128_t
|
||||
operator|(const uint128_t& o) const
|
||||
{
|
||||
return {*this, o, std::bit_or< uint64_t >{}};
|
||||
}
|
||||
|
||||
constexpr uint128_t
|
||||
operator^(const uint128_t& o) const
|
||||
{
|
||||
return {*this, o, std::bit_xor< uint64_t >{}};
|
||||
}
|
||||
|
||||
constexpr uint128_t
|
||||
operator~() const
|
||||
{
|
||||
return {*this, std::bit_not< uint64_t >{}};
|
||||
}
|
||||
|
||||
explicit constexpr operator bool() const
|
||||
{
|
||||
return static_cast< bool >(lower) || static_cast< bool >(upper);
|
||||
}
|
||||
|
||||
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) const
|
||||
{
|
||||
uint128_t sum{upper + b.upper, lower + b.lower};
|
||||
if(sum.lower < lower)
|
||||
++sum.upper;
|
||||
return sum;
|
||||
}
|
||||
|
||||
constexpr uint128_t
|
||||
operator-(const uint128_t& b) const
|
||||
{
|
||||
uint128_t diff{upper - b.upper, lower - b.lower};
|
||||
if(diff.lower > lower)
|
||||
--diff.upper;
|
||||
return diff;
|
||||
}
|
||||
|
||||
constexpr uint128_t
|
||||
operator<<(uint64_t shift) const
|
||||
{
|
||||
if(shift == 0)
|
||||
return *this;
|
||||
else if(shift < 64)
|
||||
return {upper << shift | lower >> (64 - shift), lower << shift};
|
||||
else if(shift == 64)
|
||||
return {lower, 0};
|
||||
else if(shift < 128)
|
||||
return {lower << (shift - 64), 0};
|
||||
else
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
constexpr uint128_t
|
||||
operator>>(uint64_t shift) const
|
||||
{
|
||||
if(shift == 0)
|
||||
return *this;
|
||||
else if(shift < 64)
|
||||
return {upper >> shift, lower >> shift | upper << (64 - shift)};
|
||||
else if(shift == 64)
|
||||
return {0, upper};
|
||||
else if(shift < 128)
|
||||
return {0, upper >> (shift - 64)};
|
||||
else
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
constexpr uint128_t&
|
||||
operator&=(const uint128_t& o)
|
||||
{
|
||||
return *this = *this & o;
|
||||
}
|
||||
constexpr uint128_t&
|
||||
operator|=(const uint128_t& o)
|
||||
{
|
||||
return *this = *this | o;
|
||||
}
|
||||
constexpr uint128_t&
|
||||
operator^=(const uint128_t& o)
|
||||
{
|
||||
return *this = *this ^ o;
|
||||
}
|
||||
constexpr uint128_t&
|
||||
operator<<=(uint64_t shift)
|
||||
{
|
||||
return *this = *this << shift;
|
||||
}
|
||||
constexpr uint128_t&
|
||||
operator>>=(uint64_t shift)
|
||||
{
|
||||
return *this = *this >> shift;
|
||||
}
|
||||
constexpr uint128_t&
|
||||
operator+=(const uint128_t& b)
|
||||
{
|
||||
return *this = *this + b;
|
||||
}
|
||||
|
||||
constexpr uint128_t&
|
||||
operator-=(const uint128_t& b)
|
||||
{
|
||||
return *this = *this - b;
|
||||
}
|
||||
};
|
||||
|
||||
} // 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
|
@ -1,66 +1,59 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <array>
|
||||
#include <absl/types/variant.h>
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <util/bits.hpp>
|
||||
|
||||
using ArrayUC1 = std::array< unsigned char, 1 >;
|
||||
using ArrayUC20 = std::array< unsigned char, 20 >;
|
||||
using ArrayU1 = std::array< unsigned int, 1 >;
|
||||
using ArrayULL1 = std::array< unsigned long long, 1 >;
|
||||
|
||||
using TestType = absl::variant< ArrayUC1, ArrayUC20, ArrayU1, ArrayULL1 >;
|
||||
|
||||
struct InputData
|
||||
{
|
||||
TestType data;
|
||||
size_t result;
|
||||
};
|
||||
|
||||
struct TestBits : public ::testing::TestWithParam< InputData >
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(TestBits, bitcount)
|
||||
{
|
||||
auto d = GetParam();
|
||||
ASSERT_EQ(d.result,
|
||||
absl::visit(
|
||||
[](const auto& v) { return llarp::bits::count_array_bits(v); },
|
||||
d.data));
|
||||
TEST_CASE("test bit counting, 8-bit", "[bits]") {
|
||||
auto x = GENERATE(table<std::array<unsigned char, 1>, size_t>({
|
||||
{{{0b00000000}}, 0},
|
||||
{{{0b00000001}}, 1},
|
||||
{{{0b00000010}}, 1},
|
||||
{{{0b00000100}}, 1},
|
||||
{{{0b00001000}}, 1},
|
||||
{{{0b00010000}}, 1},
|
||||
{{{0b00100000}}, 1},
|
||||
{{{0b01000000}}, 1},
|
||||
{{{0b10000000}}, 1},
|
||||
{{{0b11111111}}, 8},
|
||||
}));
|
||||
REQUIRE( llarp::bits::count_array_bits(std::get<0>(x)) == std::get<1>(x) );
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
static const InputData inputData[] = {
|
||||
{ArrayUC1{{0b00000000}}, 0},
|
||||
{ArrayUC1{{0b00000001}}, 1},
|
||||
{ArrayUC1{{0b00000010}}, 1},
|
||||
{ArrayUC1{{0b00000100}}, 1},
|
||||
{ArrayUC1{{0b00001000}}, 1},
|
||||
{ArrayUC1{{0b00010000}}, 1},
|
||||
{ArrayUC1{{0b00100000}}, 1},
|
||||
{ArrayUC1{{0b01000000}}, 1},
|
||||
{ArrayUC1{{0b10000000}}, 1},
|
||||
{ArrayUC1{{0b11111111}}, 8},
|
||||
{ArrayUC20{{0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
|
||||
TEST_CASE("test bit counting, 20 x 8-bit", "[bits]") {
|
||||
auto x = GENERATE(table<std::array<unsigned char, 20>, size_t>({
|
||||
{{{0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
|
||||
0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
|
||||
0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
|
||||
0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000}}, 0},
|
||||
{ArrayUC20{{0b11111111, 0b00000100, 0b00000100, 0b00000100, 0b00000100,
|
||||
0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000}},
|
||||
0},
|
||||
{{{0b11111111, 0b00000100, 0b00000100, 0b00000100, 0b00000100,
|
||||
0b11111111, 0b00000100, 0b00000100, 0b00000100, 0b00000100,
|
||||
0b11111111, 0b00000100, 0b00000100, 0b00000100, 0b00000100,
|
||||
0b11111111, 0b00000100, 0b00000100, 0b00000100, 0b00000100}}, 48},
|
||||
{ArrayU1{{0b00000000000000000000000000000000}}, 0},
|
||||
{ArrayU1{{0b00101010101010101010101010101010}}, 15},
|
||||
{ArrayU1{{0b10101010101010101010101010101010}}, 16},
|
||||
{ArrayU1{{0b01010101010101010101010101010101}}, 16},
|
||||
{ArrayU1{{0b11111111111111111111111111111111}}, 32},
|
||||
{ArrayULL1{{0b0000000000000000000000000000000000000000000000000000000000000000}}, 0},
|
||||
{ArrayULL1{{0b0010101010101010101010101010101000101010101010101010101010101010}}, 30},
|
||||
{ArrayULL1{{0b1010101010101010101010101010101010101010101010101010101010101010}}, 32},
|
||||
{ArrayULL1{{0b0101010101010101010101010101010101010101010101010101010101010101}}, 32},
|
||||
{ArrayULL1{{0b1111111111111111111111111111111111111111111111111111111111111111}}, 64},
|
||||
};
|
||||
// clang-format on
|
||||
0b11111111, 0b00000100, 0b00000100, 0b00000100, 0b00000100}},
|
||||
48},
|
||||
}));
|
||||
|
||||
REQUIRE( llarp::bits::count_array_bits(std::get<0>(x)) == std::get<1>(x) );
|
||||
}
|
||||
|
||||
TEST_CASE("test bit counting, unsigned int", "[bits]") {
|
||||
auto x = GENERATE(table<std::array<unsigned int, 1>, size_t>({
|
||||
{{{0b00000000000000000000000000000000}}, 0},
|
||||
{{{0b00101010101010101010101010101010}}, 15},
|
||||
{{{0b10101010101010101010101010101010}}, 16},
|
||||
{{{0b01010101010101010101010101010101}}, 16},
|
||||
{{{0b11111111111111111111111111111111}}, 32},
|
||||
}));
|
||||
|
||||
REQUIRE( llarp::bits::count_array_bits(std::get<0>(x)) == std::get<1>(x) );
|
||||
}
|
||||
|
||||
TEST_CASE("test bit counting, unsigned long long", "[bits]") {
|
||||
auto x = GENERATE(table<std::array<unsigned long long, 1>, size_t>({
|
||||
{{{0b0000000000000000000000000000000000000000000000000000000000000000}}, 0},
|
||||
{{{0b0010101010101010101010101010101000101010101010101010101010101010}}, 30},
|
||||
{{{0b1010101010101010101010101010101010101010101010101010101010101010}}, 32},
|
||||
{{{0b0101010101010101010101010101010101010101010101010101010101010101}}, 32},
|
||||
{{{0b1111111111111111111111111111111111111111111111111111111111111111}}, 64},
|
||||
}));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(TestBits, TestBits, ::testing::ValuesIn(inputData));
|
||||
REQUIRE( llarp::bits::count_array_bits(std::get<0>(x)) == std::get<1>(x) );
|
||||
}
|
||||
|
Loading…
Reference in New Issue