32-bit compilation fixes:
* Use uint64_t instead of size_t as hash return value * Expect alignof(uint64_t) <= 8 instead of == 8
This commit is contained in:
parent
7c15c1bb0d
commit
2c3565df61
19 changed files with 545 additions and 59 deletions
|
|
@ -9,7 +9,7 @@ namespace psemek::ecs::detail
|
||||||
template <bool With>
|
template <bool With>
|
||||||
struct ordered_component_hasher
|
struct ordered_component_hasher
|
||||||
{
|
{
|
||||||
std::size_t result = 0xb6113227befa663bull;
|
std::uint64_t result = 0xb6113227befa663bull;
|
||||||
|
|
||||||
void operator()(util::uuid const & uuid)
|
void operator()(util::uuid const & uuid)
|
||||||
{
|
{
|
||||||
|
|
@ -22,7 +22,7 @@ namespace psemek::ecs::detail
|
||||||
};
|
};
|
||||||
|
|
||||||
template <bool With, typename UUIDs>
|
template <bool With, typename UUIDs>
|
||||||
std::size_t ordered_component_hash(UUIDs const & uuids)
|
std::uint64_t ordered_component_hash(UUIDs const & uuids)
|
||||||
{
|
{
|
||||||
ordered_component_hasher<With> hasher;
|
ordered_component_hasher<With> hasher;
|
||||||
for (auto const & uuid : uuids)
|
for (auto const & uuid : uuids)
|
||||||
|
|
@ -31,7 +31,7 @@ namespace psemek::ecs::detail
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename WithUUIDs, typename WithoutUUIDs>
|
template <typename WithUUIDs, typename WithoutUUIDs>
|
||||||
std::size_t ordered_component_hash(WithUUIDs const & with_uuids, WithoutUUIDs const & without_uuids)
|
std::uint64_t ordered_component_hash(WithUUIDs const & with_uuids, WithoutUUIDs const & without_uuids)
|
||||||
{
|
{
|
||||||
return ordered_component_hash<true>(with_uuids) ^ ordered_component_hash<false>(without_uuids);
|
return ordered_component_hash<true>(with_uuids) ^ ordered_component_hash<false>(without_uuids);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,12 @@ namespace psemek::ecs::detail
|
||||||
|
|
||||||
struct table_hashset_hash
|
struct table_hashset_hash
|
||||||
{
|
{
|
||||||
std::size_t operator()(util::span<util::uuid const> const & uuids) const
|
std::uint64_t operator()(util::span<util::uuid const> const & uuids) const
|
||||||
{
|
{
|
||||||
return unordered_component_hash<true>(uuids);
|
return unordered_component_hash<true>(uuids);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t operator()(std::unique_ptr<table> const & table) const
|
std::uint64_t operator()(std::unique_ptr<table> const & table) const
|
||||||
{
|
{
|
||||||
return table->hash();
|
return table->hash();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ namespace psemek::ecs::detail
|
||||||
template <bool With>
|
template <bool With>
|
||||||
struct unordered_component_hasher
|
struct unordered_component_hasher
|
||||||
{
|
{
|
||||||
std::size_t result = 0xcd5694d2b3f3443eull;
|
std::uint64_t result = 0xcd5694d2b3f3443eull;
|
||||||
|
|
||||||
void operator()(util::uuid const & uuid)
|
void operator()(util::uuid const & uuid)
|
||||||
{
|
{
|
||||||
|
|
@ -21,7 +21,7 @@ namespace psemek::ecs::detail
|
||||||
};
|
};
|
||||||
|
|
||||||
template <bool With, typename UUIDs>
|
template <bool With, typename UUIDs>
|
||||||
std::size_t unordered_component_hash(UUIDs const & uuids)
|
std::uint64_t unordered_component_hash(UUIDs const & uuids)
|
||||||
{
|
{
|
||||||
unordered_component_hasher<With> hasher;
|
unordered_component_hasher<With> hasher;
|
||||||
for (auto const & uuid : uuids)
|
for (auto const & uuid : uuids)
|
||||||
|
|
@ -30,7 +30,7 @@ namespace psemek::ecs::detail
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename WithUUIDs, typename WithoutUUIDs>
|
template <typename WithUUIDs, typename WithoutUUIDs>
|
||||||
std::size_t unordered_component_hash(WithUUIDs const & with_uuids, WithoutUUIDs const & without_uuids)
|
std::uint64_t unordered_component_hash(WithUUIDs const & with_uuids, WithoutUUIDs const & without_uuids)
|
||||||
{
|
{
|
||||||
return unordered_component_hash<true>(with_uuids) ^ unordered_component_hash<false>(without_uuids);
|
return unordered_component_hash<true>(with_uuids) ^ unordered_component_hash<false>(without_uuids);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,9 +40,9 @@ namespace std
|
||||||
template <>
|
template <>
|
||||||
struct hash<::psemek::ecs::handle>
|
struct hash<::psemek::ecs::handle>
|
||||||
{
|
{
|
||||||
size_t operator()(::psemek::ecs::handle const & h) const noexcept
|
uint64_t operator()(::psemek::ecs::handle const & h) const noexcept
|
||||||
{
|
{
|
||||||
return (static_cast<size_t>(h.id) << 32) | h.epoch;
|
return (static_cast<uint64_t>(h.id) << 32) | h.epoch;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,7 @@
|
||||||
#include <psemek/math/matrix.hpp>
|
#include <psemek/math/matrix.hpp>
|
||||||
#include <psemek/math/interval.hpp>
|
#include <psemek/math/interval.hpp>
|
||||||
#include <psemek/math/quaternion.hpp>
|
#include <psemek/math/quaternion.hpp>
|
||||||
|
#include <psemek/util/hash_table.hpp>
|
||||||
#include <boost/container/flat_map.hpp>
|
|
||||||
|
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
|
|
@ -121,8 +120,8 @@ namespace psemek::gfx
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GLuint program_ = 0;
|
GLuint program_ = 0;
|
||||||
mutable boost::container::flat_map<std::string, GLint, std::less<>> uniforms_;
|
mutable util::hash_map<std::string, GLint, util::any_hash, std::equal_to<>> uniforms_;
|
||||||
mutable boost::container::flat_map<std::string, GLuint, std::less<>> uniform_blocks_;
|
mutable util::hash_map<std::string, GLuint, util::any_hash, std::equal_to<>> uniform_blocks_;
|
||||||
|
|
||||||
program(std::nullptr_t)
|
program(std::nullptr_t)
|
||||||
{}
|
{}
|
||||||
|
|
|
||||||
|
|
@ -222,10 +222,10 @@ namespace std
|
||||||
template <typename T, std::size_t N>
|
template <typename T, std::size_t N>
|
||||||
struct hash<::psemek::math::point<T, N>>
|
struct hash<::psemek::math::point<T, N>>
|
||||||
{
|
{
|
||||||
std::size_t operator()(::psemek::math::point<T, N> const & v) const noexcept
|
std::uint64_t operator()(::psemek::math::point<T, N> const & v) const noexcept
|
||||||
{
|
{
|
||||||
std::hash<T> h;
|
hash<T> h;
|
||||||
std::size_t r = 0;
|
std::uint64_t r = 0;
|
||||||
for (std::size_t i = 0; i < N; ++i)
|
for (std::size_t i = 0; i < N; ++i)
|
||||||
::psemek::util::hash_combine(r, h(v[i]));
|
::psemek::util::hash_combine(r, h(v[i]));
|
||||||
return r;
|
return r;
|
||||||
|
|
|
||||||
|
|
@ -139,9 +139,9 @@ namespace std
|
||||||
template <typename Point, std::size_t K>
|
template <typename Point, std::size_t K>
|
||||||
struct hash<::psemek::math::simplex<Point, K>>
|
struct hash<::psemek::math::simplex<Point, K>>
|
||||||
{
|
{
|
||||||
std::size_t operator()(::psemek::math::simplex<Point, K> const & simplex) const
|
std::uint64_t operator()(::psemek::math::simplex<Point, K> const & simplex) const
|
||||||
{
|
{
|
||||||
std::size_t result = 0;
|
std::uint64_t result = 0;
|
||||||
std::hash<Point> h;
|
std::hash<Point> h;
|
||||||
for (auto const & p : simplex.points)
|
for (auto const & p : simplex.points)
|
||||||
::psemek::util::hash_combine(result, h(p));
|
::psemek::util::hash_combine(result, h(p));
|
||||||
|
|
|
||||||
|
|
@ -472,10 +472,10 @@ namespace std
|
||||||
template <typename T, std::size_t N>
|
template <typename T, std::size_t N>
|
||||||
struct hash<::psemek::math::vector<T, N>>
|
struct hash<::psemek::math::vector<T, N>>
|
||||||
{
|
{
|
||||||
std::size_t operator()(::psemek::math::vector<T, N> const & v) const noexcept
|
std::uint64_t operator()(::psemek::math::vector<T, N> const & v) const noexcept
|
||||||
{
|
{
|
||||||
std::hash<T> h;
|
std::hash<T> h;
|
||||||
std::size_t r = 0;
|
std::uint64_t r = 0;
|
||||||
for (std::size_t i = 0; i < N; ++i)
|
for (std::size_t i = 0; i < N; ++i)
|
||||||
::psemek::util::hash_combine(r, h(v[i]));
|
::psemek::util::hash_combine(r, h(v[i]));
|
||||||
return r;
|
return r;
|
||||||
|
|
|
||||||
|
|
@ -19,14 +19,14 @@ namespace psemek::sir
|
||||||
static_assert(alignof(std::uint8_t) == 1);
|
static_assert(alignof(std::uint8_t) == 1);
|
||||||
static_assert(alignof(std::uint16_t) == 2);
|
static_assert(alignof(std::uint16_t) == 2);
|
||||||
static_assert(alignof(std::uint32_t) == 4);
|
static_assert(alignof(std::uint32_t) == 4);
|
||||||
static_assert(alignof(std::uint64_t) == 8);
|
static_assert(alignof(std::uint64_t) <= 8);
|
||||||
static_assert(alignof(std::int8_t) == 1);
|
static_assert(alignof(std::int8_t) == 1);
|
||||||
static_assert(alignof(std::int16_t) == 2);
|
static_assert(alignof(std::int16_t) == 2);
|
||||||
static_assert(alignof(std::int32_t) == 4);
|
static_assert(alignof(std::int32_t) == 4);
|
||||||
static_assert(alignof(std::int64_t) == 8);
|
static_assert(alignof(std::int64_t) <= 8);
|
||||||
static_assert(sizeof(float) == 4);
|
static_assert(sizeof(float) == 4);
|
||||||
static_assert(alignof(float) == 4);
|
static_assert(alignof(float) == 4);
|
||||||
static_assert(sizeof(double) == 8);
|
static_assert(sizeof(double) == 8);
|
||||||
static_assert(alignof(double) == 8);
|
static_assert(alignof(double) <= 8);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
197
libs/util/include/psemek/util/big_int.hpp
Normal file
197
libs/util/include/psemek/util/big_int.hpp
Normal file
|
|
@ -0,0 +1,197 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <psemek/util/span.hpp>
|
||||||
|
#include <psemek/util/range.hpp>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <memory>
|
||||||
|
#include <concepts>
|
||||||
|
|
||||||
|
namespace psemek::util
|
||||||
|
{
|
||||||
|
|
||||||
|
struct big_int
|
||||||
|
{
|
||||||
|
using digit = std::uint32_t;
|
||||||
|
|
||||||
|
big_int() = default;
|
||||||
|
|
||||||
|
template <std::integral T>
|
||||||
|
big_int(T value);
|
||||||
|
|
||||||
|
big_int(big_int const &) = delete;
|
||||||
|
|
||||||
|
big_int(big_int && other);
|
||||||
|
|
||||||
|
static big_int from_digits(std::unique_ptr<digit[]> digits, std::uint32_t size, bool negative = false);
|
||||||
|
|
||||||
|
template <typename Iterator>
|
||||||
|
static big_int from_digits(Iterator begin, Iterator end, bool negative = false);
|
||||||
|
|
||||||
|
template <typename Container>
|
||||||
|
static big_int from_digits(Container && container, bool negative = false);
|
||||||
|
|
||||||
|
big_int & operator = (big_int const &) = delete;
|
||||||
|
|
||||||
|
big_int & operator = (big_int && other);
|
||||||
|
|
||||||
|
util::span<digit> digits();
|
||||||
|
util::span<digit const> digits() const;
|
||||||
|
|
||||||
|
bool negative() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr std::uint32_t sign_bit = std::uint32_t(1) << 31;
|
||||||
|
static constexpr std::uint32_t digit_size = sizeof(digit);
|
||||||
|
static constexpr std::uint32_t digit_bits = digit_size * 8;
|
||||||
|
static constexpr std::uint32_t digit_mask = digit(-1);
|
||||||
|
|
||||||
|
std::unique_ptr<digit[]> digits_ = nullptr;
|
||||||
|
// Highest bit of size stores the overall big int sign
|
||||||
|
std::uint32_t size_ = 0;
|
||||||
|
std::uint32_t capacity_ = 0;
|
||||||
|
|
||||||
|
void set_negative(bool negative)
|
||||||
|
{
|
||||||
|
size_ = size() | (negative ? sign_bit : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t size() const
|
||||||
|
{
|
||||||
|
return size_ & (~sign_bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_size(std::uint32_t new_size)
|
||||||
|
{
|
||||||
|
size_ = new_size | (size_ & sign_bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void canonicalize();
|
||||||
|
|
||||||
|
void ensure_capacity(std::uint32_t new_capacity);
|
||||||
|
|
||||||
|
template <std::integral T>
|
||||||
|
static T shift_by_digit(T value);
|
||||||
|
|
||||||
|
template <std::integral T>
|
||||||
|
static std::uint32_t size_for(T value);
|
||||||
|
|
||||||
|
void add_positive(span<digit const> digits);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::integral T>
|
||||||
|
big_int::big_int(T value)
|
||||||
|
{
|
||||||
|
auto size = size_for(value);
|
||||||
|
ensure_capacity(size);
|
||||||
|
set_size(size);
|
||||||
|
set_negative(value < 0);
|
||||||
|
for (digit & d : digits())
|
||||||
|
{
|
||||||
|
d = value & digit_mask;
|
||||||
|
value = shift_by_digit(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (negative())
|
||||||
|
{
|
||||||
|
for (digit & d : digits())
|
||||||
|
d = ~d;
|
||||||
|
digit const one = 1;
|
||||||
|
add_positive(make_singleton_span(one));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline big_int::big_int(big_int && other)
|
||||||
|
: digits_(std::move(other.digits_))
|
||||||
|
, size_(other.size_)
|
||||||
|
, capacity_(other.capacity_)
|
||||||
|
{
|
||||||
|
other.size_ = 0;
|
||||||
|
other.capacity_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline big_int big_int::from_digits(std::unique_ptr<digit[]> digits, std::uint32_t size, bool negative)
|
||||||
|
{
|
||||||
|
big_int result;
|
||||||
|
result.digits_ = std::move(digits);
|
||||||
|
result.size_ = size;
|
||||||
|
result.capacity_ = size;
|
||||||
|
result.set_negative(negative);
|
||||||
|
result.canonicalize();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Iterator>
|
||||||
|
big_int big_int::from_digits(Iterator begin, Iterator end, bool negative)
|
||||||
|
{
|
||||||
|
auto size = std::distance(begin, end);
|
||||||
|
auto digits = std::make_unique<digit[]>(size);
|
||||||
|
std::copy(begin, end, digits.get());
|
||||||
|
return from_digits(std::move(digits), size, negative);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Container>
|
||||||
|
big_int big_int::from_digits(Container && container, bool negative)
|
||||||
|
{
|
||||||
|
return from_digits(util::xbegin(container), util::xend(container), negative);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline util::span<big_int::digit> big_int::digits()
|
||||||
|
{
|
||||||
|
return {digits_.get(), size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline util::span<big_int::digit const> big_int::digits() const
|
||||||
|
{
|
||||||
|
return {digits_.get(), size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool big_int::negative() const
|
||||||
|
{
|
||||||
|
return (size_ & sign_bit) == sign_bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::integral T>
|
||||||
|
T big_int::shift_by_digit(T value)
|
||||||
|
{
|
||||||
|
if constexpr (sizeof(value) <= digit_size)
|
||||||
|
{
|
||||||
|
if (value < 0)
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return value >> digit_bits;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::integral T>
|
||||||
|
std::uint32_t big_int::size_for(T value)
|
||||||
|
{
|
||||||
|
std::uint32_t result_bits = 0;
|
||||||
|
|
||||||
|
if (value >= 0)
|
||||||
|
{
|
||||||
|
while (value > 0)
|
||||||
|
{
|
||||||
|
value = shift_by_digit(value);
|
||||||
|
result_bits += digit_bits;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result_bits = 1;
|
||||||
|
while ((value >> (digit_bits - 1)) != T(-1))
|
||||||
|
{
|
||||||
|
value = shift_by_digit(value);
|
||||||
|
result_bits += digit_bits;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (result_bits + (digit_bits - 1)) / digit_bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -97,9 +97,9 @@ namespace std
|
||||||
template <>
|
template <>
|
||||||
struct hash<::psemek::util::dynamic_bitset>
|
struct hash<::psemek::util::dynamic_bitset>
|
||||||
{
|
{
|
||||||
std::size_t operator()(::psemek::util::dynamic_bitset const & b) const
|
std::uint64_t operator()(::psemek::util::dynamic_bitset const & b) const
|
||||||
{
|
{
|
||||||
std::size_t result = 0x23d2ef655094f9aeull;
|
std::uint64_t result = static_cast<std::size_t>(0x23d2ef655094f9aeull);
|
||||||
for (auto value : b.storage())
|
for (auto value : b.storage())
|
||||||
::psemek::util::hash_combine(result, value);
|
::psemek::util::hash_combine(result, value);
|
||||||
return result;
|
return result;
|
||||||
|
|
|
||||||
|
|
@ -962,7 +962,7 @@ namespace std
|
||||||
template <>
|
template <>
|
||||||
struct hash<::psemek::util::ecs::species_handle>
|
struct hash<::psemek::util::ecs::species_handle>
|
||||||
{
|
{
|
||||||
std::size_t operator()(::psemek::util::ecs::species_handle h) const
|
std::uint64_t operator()(::psemek::util::ecs::species_handle h) const
|
||||||
{
|
{
|
||||||
return std::hash<::psemek::util::ecs_detail::species_handle>()(h.value);
|
return std::hash<::psemek::util::ecs_detail::species_handle>()(h.value);
|
||||||
}
|
}
|
||||||
|
|
@ -971,7 +971,7 @@ namespace std
|
||||||
template <>
|
template <>
|
||||||
struct hash<::psemek::util::ecs::entity_handle>
|
struct hash<::psemek::util::ecs::entity_handle>
|
||||||
{
|
{
|
||||||
std::size_t operator()(::psemek::util::ecs::entity_handle h) const
|
std::uint64_t operator()(::psemek::util::ecs::entity_handle h) const
|
||||||
{
|
{
|
||||||
return std::hash<::psemek::util::ecs_detail::entity_handle>()(h.value);
|
return std::hash<::psemek::util::ecs_detail::entity_handle>()(h.value);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,22 +2,23 @@
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
namespace psemek::util
|
namespace psemek::util
|
||||||
{
|
{
|
||||||
|
|
||||||
constexpr void hash_combine(std::size_t & seed, std::size_t value)
|
constexpr void hash_combine(std::uint64_t & seed, std::uint64_t value)
|
||||||
{
|
{
|
||||||
constexpr std::size_t k = 0x9ddfea08eb382d69ULL;
|
constexpr std::uint64_t k = 0x9ddfea08eb382d69ULL;
|
||||||
std::size_t a = (value ^ seed) * k;
|
std::uint64_t a = (value ^ seed) * k;
|
||||||
a ^= (a >> 47);
|
a ^= (a >> 47);
|
||||||
std::size_t b = (seed ^ a) * k;
|
std::uint64_t b = (seed ^ a) * k;
|
||||||
b ^= (b >> 47);
|
b ^= (b >> 47);
|
||||||
seed = b * k;
|
seed = b * k;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Iterator, typename Hash = std::hash<std::decay_t<decltype(*std::declval<Iterator>())>>>
|
template <typename Iterator, typename Hash = std::hash<std::decay_t<decltype(*std::declval<Iterator>())>>>
|
||||||
constexpr void hash_sequence(std::size_t & seed, Iterator begin, Iterator end, Hash hash = Hash{})
|
constexpr void hash_sequence(std::uint64_t & seed, Iterator begin, Iterator end, Hash hash = Hash{})
|
||||||
{
|
{
|
||||||
for (; begin != end; ++begin)
|
for (; begin != end; ++begin)
|
||||||
{
|
{
|
||||||
|
|
@ -30,17 +31,17 @@ namespace psemek::util
|
||||||
struct is_transparent{};
|
struct is_transparent{};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::size_t operator() (T const & x) const
|
std::uint64_t operator() (T const & x) const
|
||||||
{
|
{
|
||||||
return std::hash<T>{}(x);
|
return std::hash<T>{}(x);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename ... T>
|
template <typename ... T>
|
||||||
constexpr std::size_t hash_all(T const & ... x)
|
constexpr std::uint64_t hash_all(T const & ... x)
|
||||||
{
|
{
|
||||||
any_hash hash;
|
any_hash hash;
|
||||||
std::size_t seed = 0;
|
std::uint64_t seed = 0;
|
||||||
(hash_combine(seed, hash(x)), ...);
|
(hash_combine(seed, hash(x)), ...);
|
||||||
return seed;
|
return seed;
|
||||||
}
|
}
|
||||||
|
|
@ -54,9 +55,9 @@ namespace std
|
||||||
struct hash<std::pair<T, H>>
|
struct hash<std::pair<T, H>>
|
||||||
: std::pair<std::hash<T>, std::hash<H>>
|
: std::pair<std::hash<T>, std::hash<H>>
|
||||||
{
|
{
|
||||||
std::size_t operator()(std::pair<T, H> const & x) const
|
std::uint64_t operator()(std::pair<T, H> const & x) const
|
||||||
{
|
{
|
||||||
std::size_t seed = 0;
|
std::uint64_t seed = 0;
|
||||||
::psemek::util::hash_combine(seed, this->first(x.first));
|
::psemek::util::hash_combine(seed, this->first(x.first));
|
||||||
::psemek::util::hash_combine(seed, this->second(x.second));
|
::psemek::util::hash_combine(seed, this->second(x.second));
|
||||||
return seed;
|
return seed;
|
||||||
|
|
@ -67,16 +68,16 @@ namespace std
|
||||||
struct hash<std::tuple<Ts...>>
|
struct hash<std::tuple<Ts...>>
|
||||||
: std::tuple<std::hash<Ts>...>
|
: std::tuple<std::hash<Ts>...>
|
||||||
{
|
{
|
||||||
std::size_t operator()(std::tuple<Ts...> const & t) const
|
std::uint64_t operator()(std::tuple<Ts...> const & t) const
|
||||||
{
|
{
|
||||||
return hash_impl(t, std::make_index_sequence<sizeof...(Ts)>{});
|
return hash_impl(t, std::make_index_sequence<sizeof...(Ts)>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <std::size_t ... Is>
|
template <std::size_t ... Is>
|
||||||
std::size_t hash_impl(std::tuple<Ts...> const & t, std::index_sequence<Is...>) const
|
std::uint64_t hash_impl(std::tuple<Ts...> const & t, std::index_sequence<Is...>) const
|
||||||
{
|
{
|
||||||
std::size_t result = 0;
|
std::uint64_t result = 0;
|
||||||
(::psemek::util::hash_combine(result, std::get<Is>(*this)(std::get<Is>(t))), ...);
|
(::psemek::util::hash_combine(result, std::get<Is>(*this)(std::get<Is>(t))), ...);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,14 +13,14 @@ namespace psemek::util
|
||||||
namespace detail
|
namespace detail
|
||||||
{
|
{
|
||||||
|
|
||||||
constexpr std::size_t stored_value_mask = 1ull << 63;
|
constexpr std::uint64_t stored_value_mask = 1ull << 63;
|
||||||
constexpr std::size_t tombstone_mask = 1ull << 62;
|
constexpr std::uint64_t tombstone_mask = 1ull << 62;
|
||||||
constexpr std::size_t hash_value_mask = ~(stored_value_mask | tombstone_mask);
|
constexpr std::uint64_t hash_value_mask = ~(stored_value_mask | tombstone_mask);
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct hash_table_entry
|
struct hash_table_entry
|
||||||
{
|
{
|
||||||
std::size_t hash = 0;
|
std::uint64_t hash = 0;
|
||||||
alignas(T) char storage[sizeof(T)] = {0};
|
alignas(T) char storage[sizeof(T)] = {0};
|
||||||
|
|
||||||
bool has_value() const
|
bool has_value() const
|
||||||
|
|
@ -44,13 +44,13 @@ namespace psemek::util
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename H>
|
template <typename H>
|
||||||
void set_value(H && value, std::size_t hash)
|
void set_value(H && value, std::uint64_t hash)
|
||||||
{
|
{
|
||||||
new (storage_ptr()) T{std::forward<H>(value)};
|
new (storage_ptr()) T{std::forward<H>(value)};
|
||||||
this->hash = (hash & hash_value_mask) | stored_value_mask;
|
this->hash = (hash & hash_value_mask) | stored_value_mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hash_equal(std::size_t hash) const
|
bool hash_equal(std::uint64_t hash) const
|
||||||
{
|
{
|
||||||
return (hash & hash_value_mask) == (this->hash & hash_value_mask);
|
return (hash & hash_value_mask) == (this->hash & hash_value_mask);
|
||||||
}
|
}
|
||||||
|
|
@ -211,7 +211,7 @@ namespace psemek::util
|
||||||
std::pair<hash_table_iterator<T>, bool> insert(H && value)
|
std::pair<hash_table_iterator<T>, bool> insert(H && value)
|
||||||
{
|
{
|
||||||
ensure_capacity_for(size_ + 1);
|
ensure_capacity_for(size_ + 1);
|
||||||
std::size_t hash = this->hash()(this->key_projector()(value));
|
std::uint64_t hash = this->hash()(this->key_projector()(value));
|
||||||
return insert_impl(std::forward<H>(value), hash);
|
return insert_impl(std::forward<H>(value), hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -220,7 +220,7 @@ namespace psemek::util
|
||||||
{
|
{
|
||||||
if (size_ == 0)
|
if (size_ == 0)
|
||||||
return end();
|
return end();
|
||||||
std::size_t hash = this->hash()(key);
|
std::uint64_t hash = this->hash()(key);
|
||||||
return find_impl(key, hash);
|
return find_impl(key, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -320,13 +320,13 @@ namespace psemek::util
|
||||||
reallocate(capacity());
|
reallocate(capacity());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t probe_index(std::size_t hash, std::size_t i) const
|
std::size_t probe_index(std::uint64_t hash, std::size_t i) const
|
||||||
{
|
{
|
||||||
return (hash + (i * (i + 1)) / 2) % storage_.capacity;
|
return (static_cast<std::size_t>(hash) + (i * (i + 1)) / 2) % storage_.capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename H>
|
template <typename H>
|
||||||
std::pair<hash_table_iterator<T>, bool> insert_impl(H && value, std::size_t hash)
|
std::pair<hash_table_iterator<T>, bool> insert_impl(H && value, std::uint64_t hash)
|
||||||
{
|
{
|
||||||
std::size_t i = 0;
|
std::size_t i = 0;
|
||||||
while (true)
|
while (true)
|
||||||
|
|
@ -349,7 +349,7 @@ namespace psemek::util
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Key>
|
template <typename Key>
|
||||||
hash_table_iterator<T> find_impl(Key const & key, std::size_t hash) const
|
hash_table_iterator<T> find_impl(Key const & key, std::uint64_t hash) const
|
||||||
{
|
{
|
||||||
std::size_t i = 0;
|
std::size_t i = 0;
|
||||||
while (true)
|
while (true)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
namespace psemek::util
|
namespace psemek::util
|
||||||
{
|
{
|
||||||
|
|
@ -87,7 +88,7 @@ namespace std
|
||||||
template <>
|
template <>
|
||||||
struct hash<::psemek::util::hstring>
|
struct hash<::psemek::util::hstring>
|
||||||
{
|
{
|
||||||
std::size_t operator()(::psemek::util::hstring const & str) const
|
std::uint64_t operator()(::psemek::util::hstring const & str) const
|
||||||
{
|
{
|
||||||
return std::hash<std::string_view>()(str.view());
|
return std::hash<std::string_view>()(str.view());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
#include <psemek/util/hash.hpp>
|
#include <psemek/util/hash.hpp>
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
@ -29,7 +28,7 @@ namespace psemek::util
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(uuid) == 16);
|
static_assert(sizeof(uuid) == 16);
|
||||||
static_assert(alignof(uuid) == 8);
|
static_assert(alignof(uuid) <= 8);
|
||||||
|
|
||||||
constexpr bool is_rfc_4122(uuid const & uuid)
|
constexpr bool is_rfc_4122(uuid const & uuid)
|
||||||
{
|
{
|
||||||
|
|
@ -72,9 +71,9 @@ namespace std
|
||||||
template <>
|
template <>
|
||||||
struct hash<::psemek::util::uuid>
|
struct hash<::psemek::util::uuid>
|
||||||
{
|
{
|
||||||
std::size_t operator()(::psemek::util::uuid const & uuid) const noexcept
|
std::uint64_t operator()(::psemek::util::uuid const & uuid) const noexcept
|
||||||
{
|
{
|
||||||
std::size_t result = uuid[0];
|
std::uint64_t result = uuid[0];
|
||||||
::psemek::util::hash_combine(result, uuid[1]);
|
::psemek::util::hash_combine(result, uuid[1]);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
110
libs/util/source/big_int.cpp
Normal file
110
libs/util/source/big_int.cpp
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
#include <psemek/util/big_int.hpp>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace psemek::util
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
struct adder
|
||||||
|
{
|
||||||
|
std::uint32_t sum = 0;
|
||||||
|
std::uint32_t carry = 0;
|
||||||
|
|
||||||
|
void add(std::uint32_t x)
|
||||||
|
{
|
||||||
|
auto result = static_cast<std::uint64_t>(sum) + x;
|
||||||
|
sum = result & std::uint32_t(-1);
|
||||||
|
carry += result >> 32;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void big_int::canonicalize()
|
||||||
|
{
|
||||||
|
auto size = this->size();
|
||||||
|
auto negative = this->negative();
|
||||||
|
|
||||||
|
while (size > 0 && digits_[size - 1] == 0)
|
||||||
|
--size;
|
||||||
|
|
||||||
|
set_size(size);
|
||||||
|
set_negative(size > 0 && negative);
|
||||||
|
}
|
||||||
|
|
||||||
|
void big_int::ensure_capacity(std::uint32_t new_capacity)
|
||||||
|
{
|
||||||
|
if (new_capacity == 0)
|
||||||
|
{
|
||||||
|
digits_ = nullptr;
|
||||||
|
size_ = 0;
|
||||||
|
capacity_ = 0;
|
||||||
|
}
|
||||||
|
else if (new_capacity > capacity_)
|
||||||
|
{
|
||||||
|
new_capacity = std::max({static_cast<std::uint32_t>(1.6 * size()), static_cast<std::uint32_t>(16), new_capacity});
|
||||||
|
|
||||||
|
auto new_digits = std::make_unique<digit[]>(new_capacity);
|
||||||
|
|
||||||
|
std::size_t i = 0;
|
||||||
|
for (; i < size(); ++i)
|
||||||
|
new_digits[i] = digits_[i];
|
||||||
|
for (; i < new_capacity; ++i)
|
||||||
|
new_digits[i] = 0;
|
||||||
|
|
||||||
|
digits_ = std::move(new_digits);
|
||||||
|
capacity_ = new_capacity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void big_int::add_positive(span<digit const> digits)
|
||||||
|
{
|
||||||
|
digit carry = 0;
|
||||||
|
|
||||||
|
std::size_t i = 0;
|
||||||
|
std::size_t min_size = std::min<std::size_t>(size(), digits.size());
|
||||||
|
|
||||||
|
for (; i < min_size; ++i)
|
||||||
|
{
|
||||||
|
adder adder{digits_[i]};
|
||||||
|
adder.add(digits[i]);
|
||||||
|
adder.add(carry);
|
||||||
|
std::tie(digits_[i], carry) = {adder.sum, adder.carry};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < digits.size())
|
||||||
|
{
|
||||||
|
ensure_capacity(digits.size());
|
||||||
|
for (; i < digits.size(); ++i)
|
||||||
|
{
|
||||||
|
adder adder{digits[i]};
|
||||||
|
adder.add(carry);
|
||||||
|
std::tie(digits_[i], carry) = {adder.sum, adder.carry};
|
||||||
|
}
|
||||||
|
set_size(digits.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < size())
|
||||||
|
{
|
||||||
|
for (; i < size() && carry != 0; ++i)
|
||||||
|
{
|
||||||
|
adder adder{digits_[i]};
|
||||||
|
adder.add(carry);
|
||||||
|
std::tie(digits_[i], carry) = {adder.sum, adder.carry};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (carry != 0)
|
||||||
|
{
|
||||||
|
ensure_capacity(size() + 1);
|
||||||
|
digits_[size()] = carry;
|
||||||
|
set_size(size() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
canonicalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
179
libs/util/tests/big_int.cpp
Normal file
179
libs/util/tests/big_int.cpp
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
#include <psemek/test/test.hpp>
|
||||||
|
|
||||||
|
#include <psemek/util/big_int.hpp>
|
||||||
|
#include <psemek/random/generator.hpp>
|
||||||
|
#include <psemek/random/uniform.hpp>
|
||||||
|
|
||||||
|
using namespace psemek;
|
||||||
|
using namespace psemek::util;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
void check_digits(big_int const & bi, std::vector<big_int::digit> const & digits_expected)
|
||||||
|
{
|
||||||
|
span<big_int::digit const> digits_actual = bi.digits();
|
||||||
|
expect_equal(digits_actual.size(), digits_expected.size());
|
||||||
|
for (std::size_t i = 0; i < digits_actual.size(); ++i)
|
||||||
|
expect_equal(digits_actual[i], digits_expected[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
test_case(util_big__int_init_small)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
big_int x;
|
||||||
|
expect(!x.negative());
|
||||||
|
check_digits(x, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
big_int x(1);
|
||||||
|
expect(!x.negative());
|
||||||
|
check_digits(x, {1});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
big_int x(173);
|
||||||
|
expect(!x.negative());
|
||||||
|
check_digits(x, {173});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
big_int x(1u << 31);
|
||||||
|
expect(!x.negative());
|
||||||
|
check_digits(x, {1u << 31});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
big_int x(1ull << 32);
|
||||||
|
expect(!x.negative());
|
||||||
|
check_digits(x, {0, 1});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
big_int x(1ull << 63);
|
||||||
|
expect(!x.negative());
|
||||||
|
check_digits(x, {0, 1u << 31});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
big_int x(static_cast<std::uint64_t>(-1));
|
||||||
|
expect(!x.negative());
|
||||||
|
check_digits(x, {-1, -1});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
big_int x(-1);
|
||||||
|
expect(x.negative());
|
||||||
|
check_digits(x, {1});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
big_int x(-279);
|
||||||
|
expect(x.negative());
|
||||||
|
check_digits(x, {279});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
big_int x((-1) << 31);
|
||||||
|
expect(x.negative());
|
||||||
|
check_digits(x, {1u << 31});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
big_int x((-1ll) << 32);
|
||||||
|
expect(x.negative());
|
||||||
|
check_digits(x, {0, 1u});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
big_int x((-1ll) << 63);
|
||||||
|
expect(x.negative());
|
||||||
|
check_digits(x, {0, 1u << 31});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
big_int x(((-1ll) << 62) + ((-1ll) << 15));
|
||||||
|
expect(x.negative());
|
||||||
|
check_digits(x, {1u << 15, 1u << 30});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test_case(util_big__int_init_from__digits)
|
||||||
|
{
|
||||||
|
random::generator rng;
|
||||||
|
|
||||||
|
{
|
||||||
|
big_int x = big_int::from_digits(std::vector<int>{});
|
||||||
|
expect(!x.negative());
|
||||||
|
check_digits(x, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
big_int x = big_int::from_digits(std::vector{5724});
|
||||||
|
expect(!x.negative());
|
||||||
|
check_digits(x, {5724});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
big_int x = big_int::from_digits(std::vector{1234, 5678});
|
||||||
|
expect(!x.negative());
|
||||||
|
check_digits(x, {1234, 5678});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
big_int x = big_int::from_digits(std::vector{1234, 5678, 0, 0});
|
||||||
|
expect(!x.negative());
|
||||||
|
check_digits(x, {1234, 5678});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::vector<big_int::digit> digits(64);
|
||||||
|
for (auto & digit : digits)
|
||||||
|
digit = random::uniform<big_int::digit>(rng);
|
||||||
|
if (digits.back() == 0)
|
||||||
|
digits.back() = 1;
|
||||||
|
|
||||||
|
big_int x = big_int::from_digits(digits);
|
||||||
|
expect(!x.negative());
|
||||||
|
check_digits(x, digits);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
big_int x = big_int::from_digits(std::vector<int>{}, true);
|
||||||
|
expect(!x.negative());
|
||||||
|
check_digits(x, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
big_int x = big_int::from_digits(std::vector{5724}, true);
|
||||||
|
expect(x.negative());
|
||||||
|
check_digits(x, {5724});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
big_int x = big_int::from_digits(std::vector{1234, 5678}, true);
|
||||||
|
expect(x.negative());
|
||||||
|
check_digits(x, {1234, 5678});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
big_int x = big_int::from_digits(std::vector{1234, 5678, 0, 0}, true);
|
||||||
|
expect(x.negative());
|
||||||
|
check_digits(x, {1234, 5678});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::vector<big_int::digit> digits(64);
|
||||||
|
for (auto & digit : digits)
|
||||||
|
digit = random::uniform<big_int::digit>(rng);
|
||||||
|
if (digits.back() == 0)
|
||||||
|
digits.back() = 1;
|
||||||
|
|
||||||
|
big_int x = big_int::from_digits(digits, true);
|
||||||
|
expect(x.negative());
|
||||||
|
check_digits(x, digits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -166,7 +166,7 @@ std::optional<std::uint64_t> parse_seed(std::string const & str, int base)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::uint64_t unused;
|
std::size_t unused;
|
||||||
return std::stoull(str, &unused, base);
|
return std::stoull(str, &unused, base);
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue