Move dead submodules in-tree

Signed-off-by: swurl <swurl@swurl.xyz>
This commit is contained in:
swurl
2025-05-31 02:33:02 -04:00
parent c0cceff365
commit 6c655321e6
4081 changed files with 1185566 additions and 45 deletions
+62
View File
@@ -0,0 +1,62 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include <stdexcept>
#include <type_traits>
#include <fmt/format.h>
#include "mcl/hint/assume.hpp"
namespace mcl::detail {
[[noreturn]] void assert_terminate_impl(const char* expr_str, fmt::string_view msg, fmt::format_args args);
template<typename... Ts>
[[noreturn]] void assert_terminate(const char* expr_str, fmt::string_view msg, Ts... args)
{
assert_terminate_impl(expr_str, msg, fmt::make_format_args(args...));
}
} // namespace mcl::detail
#define UNREACHABLE() ASSERT_FALSE("Unreachable code!")
#define ASSERT(expr) \
[&] { \
if (std::is_constant_evaluated()) { \
if (!(expr)) { \
throw std::logic_error{"ASSERT failed at compile time"}; \
} \
} else { \
if (!(expr)) [[unlikely]] { \
::mcl::detail::assert_terminate(#expr, "(none)"); \
} \
} \
}()
#define ASSERT_MSG(expr, ...) \
[&] { \
if (std::is_constant_evaluated()) { \
if (!(expr)) { \
throw std::logic_error{"ASSERT_MSG failed at compile time"}; \
} \
} else { \
if (!(expr)) [[unlikely]] { \
::mcl::detail::assert_terminate(#expr, __VA_ARGS__); \
} \
} \
}()
#define ASSERT_FALSE(...) ::mcl::detail::assert_terminate("false", __VA_ARGS__)
#if defined(NDEBUG) || defined(MCL_IGNORE_ASSERTS)
# define DEBUG_ASSERT(expr) ASSUME(expr)
# define DEBUG_ASSERT_MSG(expr, ...) ASSUME(expr)
#else
# define DEBUG_ASSERT(expr) ASSERT(expr)
# define DEBUG_ASSERT_MSG(expr, ...) ASSERT_MSG(expr, __VA_ARGS__)
#endif
@@ -0,0 +1,58 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include <bitset>
#include "mcl/bitsizeof.hpp"
#include "mcl/concepts/bit_integral.hpp"
#include "mcl/stdint.hpp"
namespace mcl::bit {
template<BitIntegral T>
inline size_t count_ones(T x)
{
return std::bitset<bitsizeof<T>>(x).count();
}
template<BitIntegral T>
constexpr size_t count_leading_zeros(T x)
{
size_t result = bitsizeof<T>;
while (x != 0) {
x >>= 1;
result--;
}
return result;
}
template<BitIntegral T>
constexpr int highest_set_bit(T x)
{
int result = -1;
while (x != 0) {
x >>= 1;
result++;
}
return result;
}
template<BitIntegral T>
constexpr size_t lowest_set_bit(T x)
{
if (x == 0) {
return bitsizeof<T>;
}
size_t result = 0;
while ((x & 1) == 0) {
x >>= 1;
result++;
}
return result;
}
} // namespace mcl::bit
@@ -0,0 +1,221 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/assert.hpp"
#include "mcl/bitsizeof.hpp"
#include "mcl/concepts/bit_integral.hpp"
#include "mcl/stdint.hpp"
namespace mcl::bit {
/// Create a mask with `count` number of one bits.
template<size_t count, BitIntegral T>
constexpr T ones()
{
static_assert(count <= bitsizeof<T>, "count larger than bitsize of T");
if constexpr (count == 0) {
return 0;
} else {
return static_cast<T>(~static_cast<T>(0)) >> (bitsizeof<T> - count);
}
}
/// Create a mask with `count` number of one bits.
template<BitIntegral T>
constexpr T ones(size_t count)
{
ASSERT_MSG(count <= bitsizeof<T>, "count larger than bitsize of T");
if (count == 0) {
return 0;
}
return static_cast<T>(~static_cast<T>(0)) >> (bitsizeof<T> - count);
}
/// Create a mask of type T for bits [begin_bit, end_bit] inclusive.
template<size_t begin_bit, size_t end_bit, BitIntegral T>
constexpr T mask()
{
static_assert(begin_bit <= end_bit, "invalid bit range (position of beginning bit cannot be greater than that of end bit)");
static_assert(begin_bit < bitsizeof<T>, "begin_bit must be smaller than size of T");
static_assert(end_bit < bitsizeof<T>, "end_bit must be smaller than size of T");
return ones<end_bit - begin_bit + 1, T>() << begin_bit;
}
/// Create a mask of type T for bits [begin_bit, end_bit] inclusive.
template<BitIntegral T>
constexpr T mask(size_t begin_bit, size_t end_bit)
{
ASSERT_MSG(begin_bit <= end_bit, "invalid bit range (position of beginning bit cannot be greater than that of end bit)");
ASSERT_MSG(begin_bit < bitsizeof<T>, "begin_bit must be smaller than size of T");
ASSERT_MSG(end_bit < bitsizeof<T>, "end_bit must be smaller than size of T");
return ones<T>(end_bit - begin_bit + 1) << begin_bit;
}
/// Extract bits [begin_bit, end_bit] inclusive from value of type T.
template<size_t begin_bit, size_t end_bit, BitIntegral T>
constexpr T get_bits(T value)
{
constexpr T m = mask<begin_bit, end_bit, T>();
return (value & m) >> begin_bit;
}
/// Extract bits [begin_bit, end_bit] inclusive from value of type T.
template<BitIntegral T>
constexpr T get_bits(size_t begin_bit, size_t end_bit, T value)
{
const T m = mask<T>(begin_bit, end_bit);
return (value & m) >> begin_bit;
}
/// Clears bits [begin_bit, end_bit] inclusive of value of type T.
template<size_t begin_bit, size_t end_bit, BitIntegral T>
constexpr T clear_bits(T value)
{
constexpr T m = mask<begin_bit, end_bit, T>();
return value & ~m;
}
/// Clears bits [begin_bit, end_bit] inclusive of value of type T.
template<BitIntegral T>
constexpr T clear_bits(size_t begin_bit, size_t end_bit, T value)
{
const T m = mask<T>(begin_bit, end_bit);
return value & ~m;
}
/// Modifies bits [begin_bit, end_bit] inclusive of value of type T.
template<size_t begin_bit, size_t end_bit, BitIntegral T>
constexpr T set_bits(T value, T new_bits)
{
constexpr T m = mask<begin_bit, end_bit, T>();
return (value & ~m) | ((new_bits << begin_bit) & m);
}
/// Modifies bits [begin_bit, end_bit] inclusive of value of type T.
template<BitIntegral T>
constexpr T set_bits(size_t begin_bit, size_t end_bit, T value, T new_bits)
{
const T m = mask<T>(begin_bit, end_bit);
return (value & ~m) | ((new_bits << begin_bit) & m);
}
/// Extract bit at bit_position from value of type T.
template<size_t bit_position, BitIntegral T>
constexpr bool get_bit(T value)
{
constexpr T m = mask<bit_position, bit_position, T>();
return (value & m) != 0;
}
/// Extract bit at bit_position from value of type T.
template<BitIntegral T>
constexpr bool get_bit(size_t bit_position, T value)
{
const T m = mask<T>(bit_position, bit_position);
return (value & m) != 0;
}
/// Clears bit at bit_position of value of type T.
template<size_t bit_position, BitIntegral T>
constexpr T clear_bit(T value)
{
constexpr T m = mask<bit_position, bit_position, T>();
return value & ~m;
}
/// Clears bit at bit_position of value of type T.
template<BitIntegral T>
constexpr T clear_bit(size_t bit_position, T value)
{
const T m = mask<T>(bit_position, bit_position);
return value & ~m;
}
/// Modifies bit at bit_position of value of type T.
template<size_t bit_position, BitIntegral T>
constexpr T set_bit(T value, bool new_bit)
{
constexpr T m = mask<bit_position, bit_position, T>();
return (value & ~m) | (new_bit ? m : static_cast<T>(0));
}
/// Modifies bit at bit_position of value of type T.
template<BitIntegral T>
constexpr T set_bit(size_t bit_position, T value, bool new_bit)
{
const T m = mask<T>(bit_position, bit_position);
return (value & ~m) | (new_bit ? m : static_cast<T>(0));
}
/// Sign-extends a value that has bit_count bits to the full bitwidth of type T.
template<size_t bit_count, BitIntegral T>
constexpr T sign_extend(T value)
{
static_assert(bit_count != 0, "cannot sign-extend zero-sized value");
using S = std::make_signed_t<T>;
constexpr size_t shift_amount = bitsizeof<T> - bit_count;
return static_cast<T>(static_cast<S>(value << shift_amount) >> shift_amount);
}
/// Sign-extends a value that has bit_count bits to the full bitwidth of type T.
template<BitIntegral T>
constexpr T sign_extend(size_t bit_count, T value)
{
ASSERT_MSG(bit_count != 0, "cannot sign-extend zero-sized value");
using S = std::make_signed_t<T>;
const size_t shift_amount = bitsizeof<T> - bit_count;
return static_cast<T>(static_cast<S>(value << shift_amount) >> shift_amount);
}
/// Replicate an element across a value of type T.
template<size_t element_size, BitIntegral T>
constexpr T replicate_element(T value)
{
static_assert(element_size <= bitsizeof<T>, "element_size is too large");
static_assert(bitsizeof<T> % element_size == 0, "bitsize of T not divisible by element_size");
if constexpr (element_size == bitsizeof<T>) {
return value;
} else {
return replicate_element<element_size * 2, T>(static_cast<T>(value | (value << element_size)));
}
}
/// Replicate an element of type U across a value of type T.
template<BitIntegral U, BitIntegral T>
constexpr T replicate_element(T value)
{
static_assert(bitsizeof<U> <= bitsizeof<T>, "element_size is too large");
return replicate_element<bitsizeof<U>, T>(value);
}
/// Replicate an element across a value of type T.
template<BitIntegral T>
constexpr T replicate_element(size_t element_size, T value)
{
ASSERT_MSG(element_size <= bitsizeof<T>, "element_size is too large");
ASSERT_MSG(bitsizeof<T> % element_size == 0, "bitsize of T not divisible by element_size");
if (element_size == bitsizeof<T>) {
return value;
}
return replicate_element<T>(element_size * 2, static_cast<T>(value | (value << element_size)));
}
template<BitIntegral T>
constexpr bool most_significant_bit(T value)
{
return get_bit<bitsizeof<T> - 1, T>(value);
}
} // namespace mcl::bit
@@ -0,0 +1,33 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/bitsizeof.hpp"
#include "mcl/concepts/bit_integral.hpp"
#include "mcl/stdint.hpp"
namespace mcl::bit {
template<BitIntegral T>
constexpr T rotate_right(T x, size_t amount)
{
amount %= bitsizeof<T>;
if (amount == 0) {
return x;
}
return static_cast<T>((x >> amount) | (x << (bitsizeof<T> - amount)));
}
template<BitIntegral T>
constexpr T rotate_left(T x, size_t amount)
{
amount %= bitsizeof<T>;
if (amount == 0) {
return x;
}
return static_cast<T>((x << amount) | (x >> (bitsizeof<T> - amount)));
}
} // namespace mcl::bit
@@ -0,0 +1,56 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/concepts/bit_integral.hpp"
namespace mcl::bit {
constexpr u16 swap_bytes_16(u16 value)
{
return static_cast<u16>(u32{value} >> 8 | u32{value} << 8);
}
constexpr u32 swap_bytes_32(u32 value)
{
return ((value & 0xff000000u) >> 24)
| ((value & 0x00ff0000u) >> 8)
| ((value & 0x0000ff00u) << 8)
| ((value & 0x000000ffu) << 24);
}
constexpr u64 swap_bytes_64(u64 value)
{
return ((value & 0xff00000000000000ull) >> 56)
| ((value & 0x00ff000000000000ull) >> 40)
| ((value & 0x0000ff0000000000ull) >> 24)
| ((value & 0x000000ff00000000ull) >> 8)
| ((value & 0x00000000ff000000ull) << 8)
| ((value & 0x0000000000ff0000ull) << 24)
| ((value & 0x000000000000ff00ull) << 40)
| ((value & 0x00000000000000ffull) << 56);
}
constexpr u32 swap_halves_32(u32 value)
{
return ((value & 0xffff0000u) >> 16)
| ((value & 0x0000ffffu) << 16);
}
constexpr u64 swap_halves_64(u64 value)
{
return ((value & 0xffff000000000000ull) >> 48)
| ((value & 0x0000ffff00000000ull) >> 16)
| ((value & 0x00000000ffff0000ull) << 16)
| ((value & 0x000000000000ffffull) << 48);
}
constexpr u64 swap_words_64(u64 value)
{
return ((value & 0xffffffff00000000ull) >> 32)
| ((value & 0x00000000ffffffffull) << 32);
}
} // namespace mcl::bit
@@ -0,0 +1,38 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include <cstring>
#include <type_traits>
namespace mcl {
/// Reinterpret objects of one type as another by bit-casting between object representations.
template<class Dest, class Source>
inline Dest bit_cast(const Source& source) noexcept
{
static_assert(sizeof(Dest) == sizeof(Source), "size of destination and source objects must be equal");
static_assert(std::is_trivially_copyable_v<Dest>, "destination type must be trivially copyable.");
static_assert(std::is_trivially_copyable_v<Source>, "source type must be trivially copyable");
std::aligned_storage_t<sizeof(Dest), alignof(Dest)> dest;
std::memcpy(&dest, &source, sizeof(dest));
return reinterpret_cast<Dest&>(dest);
}
/// Reinterpret objects of any arbitrary type as another type by bit-casting between object representations.
/// Note that here we do not verify if source pointed to by source_ptr has enough bytes to read from.
template<class Dest, class SourcePtr>
inline Dest bit_cast_pointee(const SourcePtr source_ptr) noexcept
{
static_assert(sizeof(SourcePtr) == sizeof(void*), "source pointer must have size of a pointer");
static_assert(std::is_trivially_copyable_v<Dest>, "destination type must be trivially copyable.");
std::aligned_storage_t<sizeof(Dest), alignof(Dest)> dest;
std::memcpy(&dest, bit_cast<void*>(source_ptr), sizeof(dest));
return reinterpret_cast<Dest&>(dest);
}
} // namespace mcl
@@ -0,0 +1,15 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include <climits>
#include <cstddef>
namespace mcl {
template<typename T>
constexpr std::size_t bitsizeof = CHAR_BIT * sizeof(T);
} // namespace mcl
@@ -0,0 +1,16 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/concepts/is_any_of.hpp"
#include "mcl/stdint.hpp"
namespace mcl {
/// Integral upon which bit operations can be safely performed.
template<typename T>
concept BitIntegral = IsAnyOf<T, u8, u16, u32, u64, uptr, size_t>;
} // namespace mcl
@@ -0,0 +1,14 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/concepts/same_as.hpp"
namespace mcl {
template<typename T, typename... U>
concept IsAnyOf = (SameAs<T, U> || ...);
} // namespace mcl
@@ -0,0 +1,19 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
namespace mcl {
namespace detail {
template<typename T, typename U>
concept SameHelper = std::is_same_v<T, U>;
} // namespace detail
template<typename T, typename U>
concept SameAs = detail::SameHelper<T, U> && detail::SameHelper<U, T>;
} // namespace mcl
@@ -0,0 +1,35 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/bitsizeof.hpp"
#include "mcl/stdint.hpp"
namespace mcl::detail {
/// if MSB is 0, this is a full slot. remaining 7 bits is a partial hash of the key.
/// if MSB is 1, this is a non-full slot.
enum class meta_byte : u8 {
empty = 0xff,
tombstone = 0x80,
end_sentinel = 0x88,
};
inline bool is_full(meta_byte mb)
{
return (static_cast<u8>(mb) & 0x80) == 0;
}
inline meta_byte meta_byte_from_hash(size_t hash)
{
return static_cast<meta_byte>(hash >> (bitsizeof<size_t> - 7));
}
inline size_t group_index_from_hash(size_t hash, size_t group_index_mask)
{
return hash & group_index_mask;
}
} // namespace mcl::detail
@@ -0,0 +1,263 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include <array>
#include <bit>
#include "mcl/assert.hpp"
#include "mcl/container/detail/meta_byte.hpp"
#include "mcl/macro/architecture.hpp"
#include "mcl/stdint.hpp"
#if defined(MCL_ARCHITECTURE_ARM64)
# include <arm_neon.h>
#elif defined(MCL_ARCHITECTURE_X86_64)
# include <emmintrin.h>
# include "mcl/bit_cast.hpp"
#else
# include <cstring>
#endif
namespace mcl::detail {
#if defined(MCL_ARCHITECTURE_ARM64)
struct meta_byte_group {
static constexpr size_t max_group_size{16};
explicit meta_byte_group(meta_byte* ptr)
: data{vld1q_u8(reinterpret_cast<u8*>(ptr))}
{}
explicit meta_byte_group(const std::array<meta_byte, 16>& array)
: data{vld1q_u8(reinterpret_cast<const u8*>(array.data()))}
{}
uint64x2_t match(meta_byte cmp) const
{
return vreinterpretq_u64_u8(vandq_u8(vceqq_u8(data,
vdupq_n_u8(static_cast<u8>(cmp))),
vdupq_n_u8(0x80)));
}
uint64x2_t match_empty_or_tombstone() const
{
return vreinterpretq_u64_u8(vandq_u8(data,
vdupq_n_u8(0x80)));
}
bool is_any_empty() const
{
static_assert(meta_byte::empty == static_cast<meta_byte>(0xff), "empty must be maximal u8 value");
return vmaxvq_u8(data) == 0xff;
}
bool is_all_empty_or_tombstone() const
{
return vminvq_u8(vandq_u8(data, vdupq_n_u8(0x80))) == 0x80;
}
meta_byte get(size_t index) const
{
return static_cast<meta_byte>(data[index]);
}
void set(size_t index, meta_byte value)
{
data[index] = static_cast<u8>(value);
}
uint8x16_t data;
};
# define MCL_HMAP_MATCH_META_BYTE_GROUP(MATCH, ...) \
{ \
const uint64x2_t match_result{MATCH}; \
\
for (u64 match_result_v{match_result[0]}; match_result_v != 0; match_result_v &= match_result_v - 1) { \
const size_t match_index{static_cast<size_t>(std::countr_zero(match_result_v) / 8)}; \
__VA_ARGS__ \
} \
\
for (u64 match_result_v{match_result[1]}; match_result_v != 0; match_result_v &= match_result_v - 1) { \
const size_t match_index{static_cast<size_t>(8 + std::countr_zero(match_result_v) / 8)}; \
__VA_ARGS__ \
} \
}
# define MCL_HMAP_MATCH_META_BYTE_GROUP_EXCEPT_LAST(MATCH, ...) \
{ \
const uint64x2_t match_result{MATCH}; \
\
for (u64 match_result_v{match_result[0]}; match_result_v != 0; match_result_v &= match_result_v - 1) { \
const size_t match_index{static_cast<size_t>(std::countr_zero(match_result_v) / 8)}; \
__VA_ARGS__ \
} \
\
for (u64 match_result_v{match_result[1] & 0x00ffffffffffffff}; match_result_v != 0; match_result_v &= match_result_v - 1) { \
const size_t match_index{static_cast<size_t>(8 + std::countr_zero(match_result_v) / 8)}; \
__VA_ARGS__ \
} \
}
#elif defined(MCL_ARCHITECTURE_X86_64)
struct meta_byte_group {
static constexpr size_t max_group_size{16};
explicit meta_byte_group(meta_byte* ptr)
: data{_mm_load_si128(reinterpret_cast<__m128i const*>(ptr))}
{}
explicit meta_byte_group(const std::array<meta_byte, 16>& array)
: data{_mm_loadu_si128(reinterpret_cast<__m128i const*>(array.data()))}
{}
u16 match(meta_byte cmp) const
{
return _mm_movemask_epi8(_mm_cmpeq_epi8(data, _mm_set1_epi8(static_cast<u8>(cmp))));
}
u16 match_empty_or_tombstone() const
{
return _mm_movemask_epi8(data);
}
bool is_any_empty() const
{
return match(meta_byte::empty);
}
bool is_all_empty_or_tombstone() const
{
return match_empty_or_tombstone() == 0xffff;
}
meta_byte get(size_t index) const
{
return mcl::bit_cast<std::array<meta_byte, max_group_size>>(data)[index];
}
void set(size_t index, meta_byte value)
{
auto array = mcl::bit_cast<std::array<meta_byte, max_group_size>>(data);
array[index] = value;
data = mcl::bit_cast<__m128i>(array);
}
__m128i data;
};
# define MCL_HMAP_MATCH_META_BYTE_GROUP(MATCH, ...) \
{ \
for (u16 match_result{MATCH}; match_result != 0; match_result &= match_result - 1) { \
const size_t match_index{static_cast<size_t>(std::countr_zero(match_result))}; \
__VA_ARGS__ \
} \
}
# define MCL_HMAP_MATCH_META_BYTE_GROUP_EXCEPT_LAST(MATCH, ...) \
{ \
for (u16 match_result{static_cast<u16>((MATCH) & (0x7fff))}; match_result != 0; match_result &= match_result - 1) { \
const size_t match_index{static_cast<size_t>(std::countr_zero(match_result))}; \
__VA_ARGS__ \
} \
}
#else
struct meta_byte_group {
static constexpr size_t max_group_size{16};
static constexpr u64 msb{0x8080808080808080};
static constexpr u64 lsb{0x0101010101010101};
static constexpr u64 not_msb{0x7f7f7f7f7f7f7f7f};
static constexpr u64 not_lsb{0xfefefefefefefefe};
explicit meta_byte_group(meta_byte* ptr)
{
std::memcpy(data.data(), ptr, sizeof(data));
}
explicit meta_byte_group(const std::array<meta_byte, 16>& array)
: data{array}
{}
std::array<u64, 2> match(meta_byte cmp) const
{
DEBUG_ASSERT(is_full(cmp));
const u64 vcmp{lsb * static_cast<u64>(cmp)};
return {(msb - ((data[0] ^ vcmp) & not_msb)) & ~data[0] & msb, (msb - ((data[1] ^ vcmp) & not_msb)) & ~data[1] & msb};
}
std::array<u64, 2> match_empty_or_tombstone() const
{
return {data[0] & msb, data[1] & msb};
}
bool is_any_empty() const
{
static_assert((static_cast<u8>(meta_byte::empty) & 0xc0) == 0xc0);
static_assert((static_cast<u8>(meta_byte::tombstone) & 0xc0) == 0x80);
return (data[0] & (data[0] << 1) & msb) || (data[1] & (data[1] << 1) & msb);
}
bool is_all_empty_or_tombstone() const
{
return (data[0] & data[1] & msb) == msb;
}
meta_byte get(size_t index) const
{
return mcl::bit_cast<std::array<meta_byte, max_group_size>>(data)[index];
}
void set(size_t index, meta_byte value)
{
auto array = mcl::bit_cast<std::array<meta_byte, max_group_size>>(data);
array[index] = value;
data = mcl::bit_cast<std::array<u64, 2>>(array);
}
std::array<u64, 2> data;
};
# define MCL_HMAP_MATCH_META_BYTE_GROUP(MATCH, ...) \
{ \
const std::array<u64, 2> match_result{MATCH}; \
\
for (u64 match_result_v{match_result[0]}; match_result_v != 0; match_result_v &= match_result_v - 1) { \
const size_t match_index{static_cast<size_t>(std::countr_zero(match_result_v) / 8)}; \
__VA_ARGS__ \
} \
\
for (u64 match_result_v{match_result[1]}; match_result_v != 0; match_result_v &= match_result_v - 1) { \
const size_t match_index{static_cast<size_t>(8 + std::countr_zero(match_result_v) / 8)}; \
__VA_ARGS__ \
} \
}
# define MCL_HMAP_MATCH_META_BYTE_GROUP_EXCEPT_LAST(MATCH, ...) \
{ \
const std::array<u64, 2> match_result{MATCH}; \
\
for (u64 match_result_v{match_result[0]}; match_result_v != 0; match_result_v &= match_result_v - 1) { \
const size_t match_index{static_cast<size_t>(std::countr_zero(match_result_v) / 8)}; \
__VA_ARGS__ \
} \
\
for (u64 match_result_v{match_result[1] & 0x00ffffffffffffff}; match_result_v != 0; match_result_v &= match_result_v - 1) { \
const size_t match_index{static_cast<size_t>(8 + std::countr_zero(match_result_v) / 8)}; \
__VA_ARGS__ \
} \
}
#endif
} // namespace mcl::detail
@@ -0,0 +1,16 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
namespace mcl::detail {
template<typename ValueType>
union slot_union {
slot_union() {}
~slot_union() {}
ValueType value;
};
} // namespace mcl::detail
@@ -0,0 +1,532 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include <cstddef>
#include <functional>
#include <limits>
#include <type_traits>
#include <utility>
#include "mcl/assert.hpp"
#include "mcl/container/detail/meta_byte.hpp"
#include "mcl/container/detail/meta_byte_group.hpp"
#include "mcl/container/detail/slot_union.hpp"
#include "mcl/hash/xmrx.hpp"
#include "mcl/hint/assume.hpp"
#include "mcl/memory/overaligned_unique_ptr.hpp"
namespace mcl {
template<typename KeyType, typename MappedType, typename Hash, typename Pred>
class hmap;
template<bool IsConst, typename KeyType, typename MappedType, typename Hash, typename Pred>
class hmap_iterator {
using base_value_type = std::pair<const KeyType, MappedType>;
using slot_type = detail::slot_union<base_value_type>;
public:
using key_type = KeyType;
using mapped_type = MappedType;
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = std::conditional_t<IsConst, std::add_const_t<base_value_type>, base_value_type>;
using pointer = value_type*;
using const_pointer = const value_type*;
using reference = value_type&;
using const_reference = const value_type&;
hmap_iterator() = default;
hmap_iterator(const hmap_iterator& other) = default;
hmap_iterator& operator=(const hmap_iterator& other) = default;
hmap_iterator& operator++()
{
if (mb_ptr == nullptr)
return *this;
++mb_ptr;
++slot_ptr;
skip_empty_or_tombstone();
return *this;
}
hmap_iterator operator++(int)
{
hmap_iterator it(*this);
++*this;
return it;
}
bool operator==(const hmap_iterator& other) const
{
return std::tie(mb_ptr, slot_ptr) == std::tie(other.mb_ptr, other.slot_ptr);
}
bool operator!=(const hmap_iterator& other) const
{
return !operator==(other);
}
reference operator*() const
{
return static_cast<reference>(slot_ptr->value);
}
pointer operator->() const
{
return std::addressof(operator*());
}
private:
friend class hmap<KeyType, MappedType, Hash, Pred>;
hmap_iterator(detail::meta_byte* mb_ptr, slot_type* slot_ptr)
: mb_ptr{mb_ptr}, slot_ptr{slot_ptr}
{
ASSUME(mb_ptr != nullptr);
ASSUME(slot_ptr != nullptr);
}
void skip_empty_or_tombstone()
{
if (!mb_ptr)
return;
while (*mb_ptr == detail::meta_byte::empty || *mb_ptr == detail::meta_byte::tombstone) {
++mb_ptr;
++slot_ptr;
}
if (*mb_ptr == detail::meta_byte::end_sentinel) {
mb_ptr = nullptr;
slot_ptr = nullptr;
}
}
detail::meta_byte* mb_ptr{nullptr};
slot_type* slot_ptr{nullptr};
};
template<typename KeyType, typename MappedType, typename Hash = hash::avalanche_xmrx<KeyType>, typename Pred = std::equal_to<KeyType>>
class hmap {
public:
using key_type = KeyType;
using mapped_type = MappedType;
using hasher = Hash;
using key_equal = Pred;
using value_type = std::pair<const key_type, mapped_type>;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = value_type*;
using const_pointer = const value_type*;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using iterator = hmap_iterator<false, key_type, mapped_type, hasher, key_equal>;
using const_iterator = hmap_iterator<true, key_type, mapped_type, hasher, key_equal>;
private:
static constexpr size_t group_size{detail::meta_byte_group::max_group_size};
static constexpr size_t average_max_group_load{group_size - 2};
using slot_type = detail::slot_union<value_type>;
using slot_ptr = std::unique_ptr<slot_type[]>;
using meta_byte_ptr = overaligned_unique_ptr<group_size, detail::meta_byte[]>;
static_assert(!std::is_reference_v<key_type>);
static_assert(!std::is_reference_v<mapped_type>);
public:
hmap()
{
initialize_members(1);
}
hmap(const hmap& other)
{
deep_copy(other);
}
hmap(hmap&& other)
: group_index_mask{std::exchange(other.group_index_mask, 0)}
, empty_slots{std::exchange(other.empty_slots, 0)}
, full_slots{std::exchange(other.full_slots, 0)}
, mbs{std::move(other.mbs)}
, slots{std::move(other.slots)}
{
}
hmap& operator=(const hmap& other)
{
deep_copy(other);
return *this;
}
hmap& operator=(hmap&& other)
{
group_index_mask = std::exchange(other.group_index_mask, 0);
empty_slots = std::exchange(other.empty_slots, 0);
full_slots = std::exchange(other.full_slots, 0);
mbs = std::move(other.mbs);
slots = std::move(other.slots);
return *this;
}
~hmap()
{
if (!mbs)
return;
clear();
}
[[nodiscard]] bool empty() const noexcept { return full_slots == 0; }
size_type size() const noexcept { return full_slots; }
size_type max_size() const noexcept { return static_cast<size_type>(std::numeric_limits<difference_type>::max()); }
iterator begin()
{
iterator result{iterator_at(0)};
result.skip_empty_or_tombstone();
return result;
}
iterator end()
{
return {};
}
const_iterator cbegin() const
{
const_iterator result{const_iterator_at(0)};
result.skip_empty_or_tombstone();
return result;
}
const_iterator cend() const
{
return {};
}
const_iterator begin() const
{
return cbegin();
}
const_iterator end() const
{
return cend();
}
template<typename K = key_type, typename... Args>
std::pair<iterator, bool> try_emplace(K&& k, Args&&... args)
{
auto [item_index, item_found] = find_key_or_empty_slot(k);
if (!item_found) {
new (&slots[item_index].value) value_type(
std::piecewise_construct,
std::forward_as_tuple(std::forward<K>(k)),
std::forward_as_tuple(std::forward<Args>(args)...));
}
return {iterator_at(item_index), !item_found};
}
template<typename K = key_type, typename V = mapped_type>
std::pair<iterator, bool> insert_or_assign(K&& k, V&& v)
{
auto [item_index, item_found] = find_key_or_empty_slot(k);
if (item_found) {
slots[item_index].value.second = std::forward<V>(v);
} else {
new (&slots[item_index].value) value_type(
std::forward<K>(k),
std::forward<V>(v));
}
return {iterator_at(item_index), !item_found};
}
void erase(const_iterator position)
{
if (position == cend()) {
return;
}
const std::size_t item_index{static_cast<std::size_t>(std::distance(mbs.get(), position.mb_ptr))};
const std::size_t group_index{item_index / group_size};
const detail::meta_byte_group g{mbs.get() + group_index * group_size};
erase_impl(item_index, std::move(g));
}
void erase(iterator position)
{
if (position == end()) {
return;
}
const std::size_t item_index{static_cast<std::size_t>(std::distance(mbs.get(), position.mb_ptr))};
const std::size_t group_index{item_index / group_size};
const detail::meta_byte_group g{mbs.get() + group_index * group_size};
erase_impl(item_index, std::move(g));
}
template<typename K = key_type>
size_t erase(const K& key)
{
const std::size_t hash{hasher{}(key)};
const detail::meta_byte mb{detail::meta_byte_from_hash(hash)};
size_t group_index{detail::group_index_from_hash(hash, group_index_mask)};
while (true) {
detail::meta_byte_group g{mbs.get() + group_index * group_size};
MCL_HMAP_MATCH_META_BYTE_GROUP(g.match(mb), {
const std::size_t item_index{group_index * group_size + match_index};
if (key_equal{}(slots[item_index].value.first, key)) [[likely]] {
erase_impl(item_index, std::move(g));
return 1;
}
});
if (g.is_any_empty()) [[likely]] {
return 0;
}
group_index = (group_index + 1) & group_index_mask;
}
}
template<typename K = key_type>
iterator find(const K& key)
{
const std::size_t hash{hasher{}(key)};
const detail::meta_byte mb{detail::meta_byte_from_hash(hash)};
size_t group_index{detail::group_index_from_hash(hash, group_index_mask)};
while (true) {
detail::meta_byte_group g{mbs.get() + group_index * group_size};
MCL_HMAP_MATCH_META_BYTE_GROUP(g.match(mb), {
const std::size_t item_index{group_index * group_size + match_index};
if (key_equal{}(slots[item_index].value.first, key)) [[likely]] {
return iterator_at(item_index);
}
});
if (g.is_any_empty()) [[likely]] {
return {};
}
group_index = (group_index + 1) & group_index_mask;
}
}
template<typename K = key_type>
const_iterator find(const K& key) const
{
const std::size_t hash{hasher{}(key)};
const detail::meta_byte mb{detail::meta_byte_from_hash(hash)};
size_t group_index{detail::group_index_from_hash(hash, group_index_mask)};
while (true) {
detail::meta_byte_group g{mbs.get() + group_index * group_size};
MCL_HMAP_MATCH_META_BYTE_GROUP(g.match(mb), {
const std::size_t item_index{group_index * group_size + match_index};
if (key_equal{}(slots[item_index].value.first, key)) [[likely]] {
return const_iterator_at(item_index);
}
});
if (g.is_any_empty()) [[likely]] {
return {};
}
group_index = (group_index + 1) & group_index_mask;
}
}
template<typename K = key_type>
bool contains(const K& key) const
{
return find(key) != end();
}
template<typename K = key_type>
size_t count(const K& key) const
{
return contains(key) ? 1 : 0;
}
template<typename K = key_type>
mapped_type& operator[](K&& k)
{
return try_emplace(std::forward<K>(k)).first->second;
}
template<typename K = key_type>
mapped_type& at(K&& k)
{
const auto iter{find(k)};
if (iter == end()) {
throw std::out_of_range("hmap::at: key not found");
}
return iter->second;
}
template<typename K = key_type>
const mapped_type& at(K&& k) const
{
const auto iter{find(k)};
if (iter == end()) {
throw std::out_of_range("hmap::at: key not found");
}
return iter->second;
}
void clear()
{
for (auto iter{begin()}; iter != end(); ++iter) {
iter->~value_type();
}
clear_metadata();
}
private:
iterator iterator_at(std::size_t item_index)
{
return {mbs.get() + item_index, slots.get() + item_index};
}
const_iterator const_iterator_at(std::size_t item_index) const
{
return {mbs.get() + item_index, slots.get() + item_index};
}
std::pair<std::size_t, bool> find_key_or_empty_slot(const key_type& key)
{
const std::size_t hash{hasher{}(key)};
const detail::meta_byte mb{detail::meta_byte_from_hash(hash)};
std::size_t group_index{detail::group_index_from_hash(hash, group_index_mask)};
while (true) {
detail::meta_byte_group g{mbs.get() + group_index * group_size};
MCL_HMAP_MATCH_META_BYTE_GROUP(g.match(mb), {
const std::size_t item_index{group_index * group_size + match_index};
if (key_equal{}(slots[item_index].value.first, key)) [[likely]] {
return {item_index, true};
}
});
if (g.is_any_empty()) [[likely]] {
return {find_empty_slot_to_insert(hash), false};
}
group_index = (group_index + 1) & group_index_mask;
}
}
std::size_t find_empty_slot_to_insert(const std::size_t hash)
{
if (empty_slots == 0) [[unlikely]] {
grow_and_rehash();
}
std::size_t group_index{detail::group_index_from_hash(hash, group_index_mask)};
while (true) {
detail::meta_byte_group g{mbs.get() + group_index * group_size};
MCL_HMAP_MATCH_META_BYTE_GROUP(g.match_empty_or_tombstone(), {
const std::size_t item_index{group_index * group_size + match_index};
if (mbs[item_index] == detail::meta_byte::empty) [[likely]] {
--empty_slots;
}
++full_slots;
mbs[item_index] = detail::meta_byte_from_hash(hash);
return item_index;
});
group_index = (group_index + 1) & group_index_mask;
}
}
void erase_impl(std::size_t item_index, detail::meta_byte_group&& g)
{
slots[item_index].value->~value_type();
--full_slots;
if (g.is_any_empty()) {
mbs[item_index] = detail::meta_byte::empty;
++empty_slots;
} else {
mbs[item_index] = detail::meta_byte::tombstone;
}
}
void grow_and_rehash()
{
const std::size_t new_group_count{2 * (group_index_mask + 1)};
pow2_resize(new_group_count);
}
void pow2_resize(std::size_t new_group_count)
{
auto iter{begin()};
const auto old_mbs{std::move(mbs)};
const auto old_slots{std::move(slots)};
initialize_members(new_group_count);
for (; iter != end(); ++iter) {
const std::size_t hash{hasher{}(iter->first)};
const std::size_t item_index{find_empty_slot_to_insert(hash)};
new (&slots[item_index].value) value_type(std::move(iter.slot_ptr->value));
iter.slot_ptr->value.~value_type();
}
}
void deep_copy(const hmap& other)
{
initialize_members(other.group_index_mask + 1);
for (auto iter = other.begin(); iter != other.end(); ++iter) {
const std::size_t hash{hasher{}(iter->first)};
const std::size_t item_index{find_empty_slot_to_insert(hash)};
new (&slots[item_index].value) value_type(iter.slot_ptr->value);
}
}
void initialize_members(std::size_t group_count)
{
// DEBUG_ASSERT(group_count != 0 && std::ispow2(group_count));
group_index_mask = group_count - 1;
mbs = make_overaligned_unique_ptr_array<group_size, detail::meta_byte>(group_count * group_size + 1);
slots = slot_ptr{new slot_type[group_count * group_size]};
clear_metadata();
}
void clear_metadata()
{
const std::size_t group_count{group_index_mask + 1};
empty_slots = group_count * average_max_group_load;
full_slots = 0;
std::memset(mbs.get(), static_cast<int>(detail::meta_byte::empty), group_count * group_size);
mbs[group_count * group_size] = detail::meta_byte::end_sentinel;
}
std::size_t group_index_mask;
std::size_t empty_slots;
std::size_t full_slots;
meta_byte_ptr mbs;
slot_ptr slots;
};
} // namespace mcl
@@ -0,0 +1,549 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include <array>
#include <cstddef>
#include <functional>
#include <limits>
#include <type_traits>
#include <utility>
#include "mcl/assert.hpp"
#include "mcl/container/detail/meta_byte.hpp"
#include "mcl/container/detail/meta_byte_group.hpp"
#include "mcl/container/detail/slot_union.hpp"
#include "mcl/hash/xmrx.hpp"
#include "mcl/hint/assume.hpp"
namespace mcl {
template<typename KeyType, typename MappedType, typename Hash, typename Pred>
class ihmap;
namespace detail {
constexpr std::array<meta_byte, 16> ihmap_default_meta{
meta_byte::empty, meta_byte::empty, meta_byte::empty, meta_byte::empty,
meta_byte::empty, meta_byte::empty, meta_byte::empty, meta_byte::empty,
meta_byte::empty, meta_byte::empty, meta_byte::empty, meta_byte::empty,
meta_byte::empty, meta_byte::empty, meta_byte::empty, meta_byte::tombstone};
template<typename KeyType, typename MappedType>
struct ihmap_group {
using base_value_type = std::pair<const KeyType, MappedType>;
using slot_type = detail::slot_union<base_value_type>;
static constexpr std::size_t group_size{meta_byte_group::max_group_size - 1};
meta_byte_group meta{ihmap_default_meta};
std::array<slot_type, group_size> slots{};
};
} // namespace detail
template<bool IsConst, typename KeyType, typename MappedType, typename Hash, typename Pred>
class ihmap_iterator {
using group_type = detail::ihmap_group<KeyType, MappedType>;
using base_value_type = typename group_type::base_value_type;
public:
using key_type = KeyType;
using mapped_type = MappedType;
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = std::conditional_t<IsConst, std::add_const_t<base_value_type>, base_value_type>;
using pointer = value_type*;
using const_pointer = const value_type*;
using reference = value_type&;
using const_reference = const value_type&;
ihmap_iterator() = default;
ihmap_iterator(const ihmap_iterator& other) = default;
ihmap_iterator& operator=(const ihmap_iterator& other) = default;
ihmap_iterator& operator++()
{
if (group_ptr == nullptr)
return *this;
++slot_index;
skip_empty_or_tombstone();
return *this;
}
ihmap_iterator operator++(int)
{
ihmap_iterator it(*this);
++*this;
return it;
}
bool operator==(const ihmap_iterator& other) const
{
return std::tie(group_ptr, slot_index) == std::tie(other.group_ptr, other.slot_index);
}
bool operator!=(const ihmap_iterator& other) const
{
return !operator==(other);
}
reference operator*() const
{
return static_cast<reference>(group_ptr->slots[slot_index].value);
}
pointer operator->() const
{
return std::addressof(operator*());
}
private:
friend class ihmap<KeyType, MappedType, Hash, Pred>;
ihmap_iterator(group_type* group_ptr, size_t slot_index)
: group_ptr{group_ptr}, slot_index{slot_index}
{
ASSUME(group_ptr != nullptr);
}
void skip_empty_or_tombstone()
{
if (!group_ptr)
return;
while (true) {
const detail::meta_byte mb = group_ptr->meta.get(slot_index);
if (slot_index == group_type::group_size) {
slot_index = 0;
++group_ptr;
if (mb == detail::meta_byte::end_sentinel) {
group_ptr = nullptr;
return;
}
continue;
}
if (is_full(mb)) {
break;
}
++slot_index;
}
}
group_type* group_ptr{nullptr};
std::size_t slot_index{0};
};
template<typename KeyType, typename MappedType, typename Hash = hash::avalanche_xmrx<KeyType>, typename Pred = std::equal_to<KeyType>>
class ihmap {
using group_type = detail::ihmap_group<KeyType, MappedType>;
public:
using key_type = KeyType;
using mapped_type = MappedType;
using hasher = Hash;
using key_equal = Pred;
using value_type = typename group_type::base_value_type;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = value_type*;
using const_pointer = const value_type*;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using iterator = ihmap_iterator<false, key_type, mapped_type, hasher, key_equal>;
using const_iterator = ihmap_iterator<true, key_type, mapped_type, hasher, key_equal>;
private:
static_assert(!std::is_reference_v<key_type>);
static_assert(!std::is_reference_v<mapped_type>);
static constexpr std::size_t group_size{group_type::group_size};
static constexpr std::size_t average_max_group_load{group_size - 2};
struct position {
std::size_t group_index;
std::size_t slot_index;
};
public:
ihmap()
{
initialize_members(1);
}
ihmap(const ihmap& other)
{
deep_copy(other);
}
ihmap(ihmap&& other)
: group_index_mask{std::exchange(other.group_index_mask, 0)}
, empty_slots{std::exchange(other.empty_slots, 0)}
, full_slots{std::exchange(other.full_slots, 0)}
, groups{std::move(other.groups)}
{
}
ihmap& operator=(const ihmap& other)
{
deep_copy(other);
return *this;
}
ihmap& operator=(ihmap&& other)
{
group_index_mask = std::exchange(other.group_index_mask, 0);
empty_slots = std::exchange(other.empty_slots, 0);
full_slots = std::exchange(other.full_slots, 0);
groups = std::move(other.groups);
return *this;
}
~ihmap()
{
if (!groups)
return;
clear();
}
[[nodiscard]] bool empty() const noexcept { return full_slots == 0; }
size_type size() const noexcept { return full_slots; }
size_type max_size() const noexcept { return static_cast<size_type>(std::numeric_limits<difference_type>::max()); }
iterator begin()
{
iterator result{iterator_at({0, 0})};
result.skip_empty_or_tombstone();
return result;
}
iterator end()
{
return {};
}
const_iterator cbegin() const
{
const_iterator result{const_iterator_at({0, 0})};
result.skip_empty_or_tombstone();
return result;
}
const_iterator cend() const
{
return {};
}
const_iterator begin() const
{
return cbegin();
}
const_iterator end() const
{
return cend();
}
template<typename K = key_type, typename... Args>
std::pair<iterator, bool> try_emplace(K&& k, Args&&... args)
{
auto [pos, item_found] = find_key_or_empty_slot(k);
if (!item_found) {
new (&groups[pos.group_index].slots[pos.slot_index].value) value_type(
std::piecewise_construct,
std::forward_as_tuple(std::forward<K>(k)),
std::forward_as_tuple(std::forward<Args>(args)...));
}
return {iterator_at(pos), !item_found};
}
template<typename K = key_type, typename V = mapped_type>
std::pair<iterator, bool> insert_or_assign(K&& k, V&& v)
{
auto [pos, item_found] = find_key_or_empty_slot(k);
if (item_found) {
groups[pos.group_index].slots[pos.slot_index].value.second = std::forward<V>(v);
} else {
new (&groups[pos.group_index].slots[pos.slot_index].value) value_type(
std::forward<K>(k),
std::forward<V>(v));
}
return {iterator_at(pos), !item_found};
}
void erase(const_iterator iter)
{
if (iter == cend()) {
return;
}
const std::size_t group_index{static_cast<std::size_t>(std::distance(groups.get(), iter.group_ptr))};
erase_impl({group_index, iter.slot_index});
}
void erase(iterator iter)
{
if (iter == end()) {
return;
}
const std::size_t group_index{static_cast<std::size_t>(std::distance(groups.get(), iter.group_ptr))};
erase_impl({group_index, iter.slot_index});
}
template<typename K = key_type>
std::size_t erase(const K& key)
{
const std::size_t hash{hasher{}(key)};
const detail::meta_byte mb{detail::meta_byte_from_hash(hash)};
std::size_t group_index{detail::group_index_from_hash(hash, group_index_mask)};
while (true) {
const group_type& g{groups[group_index]};
MCL_HMAP_MATCH_META_BYTE_GROUP_EXCEPT_LAST(g.meta.match(mb), {
if (key_equal{}(g.slots[match_index].value.first, key)) [[likely]] {
erase_impl({group_index, match_index});
return 1;
}
});
if (g.meta.is_any_empty()) [[likely]] {
return 0;
}
group_index = (group_index + 1) & group_index_mask;
}
}
template<typename K = key_type>
iterator find(const K& key)
{
const std::size_t hash{hasher{}(key)};
const detail::meta_byte mb{detail::meta_byte_from_hash(hash)};
std::size_t group_index{detail::group_index_from_hash(hash, group_index_mask)};
while (true) {
const group_type& g{groups[group_index]};
MCL_HMAP_MATCH_META_BYTE_GROUP_EXCEPT_LAST(g.meta.match(mb), {
if (key_equal{}(g.slots[match_index].value.first, key)) [[likely]] {
return iterator_at({group_index, match_index});
}
});
if (g.meta.is_any_empty()) [[likely]] {
return {};
}
group_index = (group_index + 1) & group_index_mask;
}
}
template<typename K = key_type>
const_iterator find(const K& key) const
{
const std::size_t hash{hasher{}(key)};
const detail::meta_byte mb{detail::meta_byte_from_hash(hash)};
std::size_t group_index{detail::group_index_from_hash(hash, group_index_mask)};
while (true) {
const group_type& g{groups[group_index]};
MCL_HMAP_MATCH_META_BYTE_GROUP_EXCEPT_LAST(g.meta.match(mb), {
if (key_equal{}(g.slots[match_index].value.first, key)) [[likely]] {
return const_iterator_at({group_index, match_index});
}
});
if (g.meta.is_any_empty()) [[likely]] {
return {};
}
group_index = (group_index + 1) & group_index_mask;
}
}
template<typename K = key_type>
bool contains(const K& key) const
{
return find(key) != end();
}
template<typename K = key_type>
std::size_t count(const K& key) const
{
return contains(key) ? 1 : 0;
}
template<typename K = key_type>
mapped_type& operator[](K&& k)
{
return try_emplace(std::forward<K>(k)).first->second;
}
template<typename K = key_type>
mapped_type& at(K&& k)
{
const auto iter{find(k)};
if (iter == end()) {
throw std::out_of_range("ihmap::at: key not found");
}
return iter->second;
}
template<typename K = key_type>
const mapped_type& at(K&& k) const
{
const auto iter{find(k)};
if (iter == end()) {
throw std::out_of_range("ihmap::at: key not found");
}
return iter->second;
}
void clear()
{
for (auto iter{begin()}; iter != end(); ++iter) {
iter->~value_type();
}
clear_metadata();
}
private:
iterator iterator_at(position pos)
{
return {groups.get() + pos.group_index, pos.slot_index};
}
const_iterator const_iterator_at(position pos) const
{
return {groups.get() + pos.group_index, pos.slot_index};
}
std::pair<position, bool> find_key_or_empty_slot(const key_type& key)
{
const std::size_t hash{hasher{}(key)};
const detail::meta_byte mb{detail::meta_byte_from_hash(hash)};
std::size_t group_index{detail::group_index_from_hash(hash, group_index_mask)};
while (true) {
const group_type& g{groups[group_index]};
MCL_HMAP_MATCH_META_BYTE_GROUP_EXCEPT_LAST(g.meta.match(mb), {
if (key_equal{}(g.slots[match_index].value.first, key)) [[likely]] {
return {{group_index, match_index}, true};
}
});
if (g.meta.is_any_empty()) [[likely]] {
return {find_empty_slot_to_insert(hash), false};
}
group_index = (group_index + 1) & group_index_mask;
}
}
position find_empty_slot_to_insert(const std::size_t hash)
{
if (empty_slots == 0) [[unlikely]] {
grow_and_rehash();
}
std::size_t group_index{detail::group_index_from_hash(hash, group_index_mask)};
while (true) {
group_type& g{groups[group_index]};
MCL_HMAP_MATCH_META_BYTE_GROUP_EXCEPT_LAST(g.meta.match_empty_or_tombstone(), {
if (g.meta.get(match_index) == detail::meta_byte::empty) [[likely]] {
--empty_slots;
}
++full_slots;
g.meta.set(match_index, detail::meta_byte_from_hash(hash));
return {group_index, match_index};
});
group_index = (group_index + 1) & group_index_mask;
}
}
void erase_impl(position pos)
{
group_type& g{groups[pos.group_index]};
g.slots[pos.slot_index].value.~value_type();
--full_slots;
if (g.meta.is_any_empty()) {
g.meta.set(pos.slot_index, detail::meta_byte::empty);
++empty_slots;
} else {
g.meta.set(pos.slot_index, detail::meta_byte::tombstone);
}
}
void grow_and_rehash()
{
const std::size_t new_group_count{2 * (group_index_mask + 1)};
pow2_resize(new_group_count);
}
void pow2_resize(std::size_t new_group_count)
{
auto iter{begin()};
const auto old_groups{std::move(groups)};
initialize_members(new_group_count);
for (; iter != end(); ++iter) {
const std::size_t hash{hasher{}(iter->first)};
const position pos{find_empty_slot_to_insert(hash)};
new (&groups[pos.group_index].slots[pos.slot_index].value) value_type(std::move(iter.group_ptr->slots[iter.slot_index].value));
iter.group_ptr->slots[iter.slot_index].value.~value_type();
}
}
void deep_copy(const ihmap& other)
{
initialize_members(other.group_index_mask + 1);
for (auto iter = other.begin(); iter != other.end(); ++iter) {
const std::size_t hash{hasher{}(iter->first)};
const position pos{find_empty_slot_to_insert(hash)};
new (&groups[pos.group_index].slots[pos.slot_index].value) value_type(iter.group_ptr->slots[iter.slot_index].value);
}
}
void initialize_members(std::size_t group_count)
{
// DEBUG_ASSERT(group_count != 0 && std::ispow2(group_count));
group_index_mask = group_count - 1;
groups = std::unique_ptr<group_type[]>{new group_type[group_count]};
clear_metadata();
}
void clear_metadata()
{
const std::size_t group_count{group_index_mask + 1};
empty_slots = group_count * average_max_group_load;
full_slots = 0;
for (size_t i{0}; i < group_count; ++i) {
groups[i].meta = detail::meta_byte_group{detail::ihmap_default_meta};
}
groups[group_count - 1].meta.set(group_size, detail::meta_byte::end_sentinel);
}
std::size_t group_index_mask;
std::size_t empty_slots;
std::size_t full_slots;
std::unique_ptr<group_type[]> groups;
};
} // namespace mcl
@@ -0,0 +1,408 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include <cstddef>
#include <iterator>
#include <memory>
#include <type_traits>
#include "mcl/assert.hpp"
namespace mcl {
template<typename T>
class intrusive_list;
template<typename T>
class intrusive_list_iterator;
template<typename T>
class intrusive_list_node {
public:
bool is_sentinel() const
{
return is_sentinel_;
}
protected:
intrusive_list_node* next = nullptr;
intrusive_list_node* prev = nullptr;
bool is_sentinel_ = false;
friend class intrusive_list<T>;
friend class intrusive_list_iterator<T>;
friend class intrusive_list_iterator<const T>;
};
template<typename T>
class intrusive_list_sentinel final : public intrusive_list_node<T> {
using intrusive_list_node<T>::next;
using intrusive_list_node<T>::prev;
using intrusive_list_node<T>::is_sentinel_;
public:
intrusive_list_sentinel()
{
next = this;
prev = this;
is_sentinel_ = true;
}
};
template<typename T>
class intrusive_list_iterator {
public:
using iterator_category = std::bidirectional_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = value_type*;
using const_pointer = const value_type*;
using reference = value_type&;
using const_reference = const value_type&;
// If value_type is const, we want "const intrusive_list_node<value_type>", not "intrusive_list_node<const value_type>"
using node_type = std::conditional_t<std::is_const<value_type>::value,
const intrusive_list_node<std::remove_const_t<value_type>>,
intrusive_list_node<value_type>>;
using node_pointer = node_type*;
using node_reference = node_type&;
intrusive_list_iterator() = default;
intrusive_list_iterator(const intrusive_list_iterator& other) = default;
intrusive_list_iterator& operator=(const intrusive_list_iterator& other) = default;
explicit intrusive_list_iterator(node_pointer list_node)
: node(list_node) {}
explicit intrusive_list_iterator(pointer data)
: node(data) {}
explicit intrusive_list_iterator(reference data)
: node(&data) {}
intrusive_list_iterator& operator++()
{
node = node->next;
return *this;
}
intrusive_list_iterator& operator--()
{
node = node->prev;
return *this;
}
intrusive_list_iterator operator++(int)
{
intrusive_list_iterator it(*this);
++*this;
return it;
}
intrusive_list_iterator operator--(int)
{
intrusive_list_iterator it(*this);
--*this;
return it;
}
bool operator==(const intrusive_list_iterator& other) const
{
return node == other.node;
}
bool operator!=(const intrusive_list_iterator& other) const
{
return !operator==(other);
}
reference operator*() const
{
DEBUG_ASSERT(!node->is_sentinel());
return static_cast<reference>(*node);
}
pointer operator->() const
{
return std::addressof(operator*());
}
node_pointer AsNodePointer() const
{
return node;
}
private:
friend class intrusive_list<T>;
node_pointer node = nullptr;
};
template<typename T>
class intrusive_list {
public:
using difference_type = std::ptrdiff_t;
using size_type = std::size_t;
using value_type = T;
using pointer = value_type*;
using const_pointer = const value_type*;
using reference = value_type&;
using const_reference = const value_type&;
using iterator = intrusive_list_iterator<value_type>;
using const_iterator = intrusive_list_iterator<const value_type>;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
/**
* Inserts a node at the given location indicated by an iterator.
*
* @param location The location to insert the node.
* @param new_node The node to add.
*/
iterator insert(iterator location, pointer new_node)
{
return insert_before(location, new_node);
}
/**
* Inserts a node at the given location, moving the previous
* node occupant ahead of the one inserted.
*
* @param location The location to insert the new node.
* @param new_node The node to insert into the list.
*/
iterator insert_before(iterator location, pointer new_node)
{
auto existing_node = location.AsNodePointer();
new_node->next = existing_node;
new_node->prev = existing_node->prev;
existing_node->prev->next = new_node;
existing_node->prev = new_node;
return iterator(new_node);
}
/**
* Inserts a new node into the list ahead of the position indicated.
*
* @param position Location to insert the node in front of.
* @param new_node The node to be inserted into the list.
*/
iterator insert_after(iterator position, pointer new_node)
{
if (empty())
return insert(begin(), new_node);
return insert(++position, new_node);
}
/**
* Add an entry to the start of the list.
* @param node Node to add to the list.
*/
void push_front(pointer node)
{
insert(begin(), node);
}
/**
* Add an entry to the end of the list
* @param node Node to add to the list.
*/
void push_back(pointer node)
{
insert(end(), node);
}
/**
* Erases the node at the front of the list.
* @note Must not be called on an empty list.
*/
void pop_front()
{
DEBUG_ASSERT(!empty());
erase(begin());
}
/**
* Erases the node at the back of the list.
* @note Must not be called on an empty list.
*/
void pop_back()
{
DEBUG_ASSERT(!empty());
erase(--end());
}
/**
* Removes a node from this list
* @param it An iterator that points to the node to remove from list.
*/
pointer remove(iterator& it)
{
DEBUG_ASSERT(it != end());
pointer node = &*it++;
node->prev->next = node->next;
node->next->prev = node->prev;
#if !defined(NDEBUG)
node->next = nullptr;
node->prev = nullptr;
#endif
return node;
}
/**
* Removes a node from this list
* @param it A constant iterator that points to the node to remove from list.
*/
pointer remove(const iterator& it)
{
iterator copy = it;
return remove(copy);
}
/**
* Removes a node from this list.
* @param node A pointer to the node to remove.
*/
pointer remove(pointer node)
{
return remove(iterator(node));
}
/**
* Removes a node from this list.
* @param node A reference to the node to remove.
*/
pointer remove(reference node)
{
return remove(iterator(node));
}
/**
* Is this list empty?
* @returns true if there are no nodes in this list.
*/
bool empty() const
{
return root->next == root.get();
}
/**
* Gets the total number of elements within this list.
* @return the number of elements in this list.
*/
size_type size() const
{
return static_cast<size_type>(std::distance(begin(), end()));
}
/**
* Retrieves a reference to the node at the front of the list.
* @note Must not be called on an empty list.
*/
reference front()
{
DEBUG_ASSERT(!empty());
return *begin();
}
/**
* Retrieves a constant reference to the node at the front of the list.
* @note Must not be called on an empty list.
*/
const_reference front() const
{
DEBUG_ASSERT(!empty());
return *begin();
}
/**
* Retrieves a reference to the node at the back of the list.
* @note Must not be called on an empty list.
*/
reference back()
{
DEBUG_ASSERT(!empty());
return *--end();
}
/**
* Retrieves a constant reference to the node at the back of the list.
* @note Must not be called on an empty list.
*/
const_reference back() const
{
DEBUG_ASSERT(!empty());
return *--end();
}
// Iterator interface
iterator begin() { return iterator(root->next); }
const_iterator begin() const { return const_iterator(root->next); }
const_iterator cbegin() const { return begin(); }
iterator end() { return iterator(root.get()); }
const_iterator end() const { return const_iterator(root.get()); }
const_iterator cend() const { return end(); }
reverse_iterator rbegin() { return reverse_iterator(end()); }
const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
const_reverse_iterator crbegin() const { return rbegin(); }
reverse_iterator rend() { return reverse_iterator(begin()); }
const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
const_reverse_iterator crend() const { return rend(); }
/**
* Erases a node from the list, indicated by an iterator.
* @param it The iterator that points to the node to erase.
*/
iterator erase(iterator it)
{
remove(it);
return it;
}
/**
* Erases a node from this list.
* @param node A pointer to the node to erase from this list.
*/
iterator erase(pointer node)
{
return erase(iterator(node));
}
/**
* Erases a node from this list.
* @param node A reference to the node to erase from this list.
*/
iterator erase(reference node)
{
return erase(iterator(node));
}
/**
* Exchanges contents of this list with another list instance.
* @param other The other list to swap with.
*/
void swap(intrusive_list& other) noexcept
{
root.swap(other.root);
}
private:
std::shared_ptr<intrusive_list_node<T>> root = std::make_shared<intrusive_list_sentinel<T>>();
};
/**
* Exchanges contents of an intrusive list with another intrusive list.
* @tparam T The type of data being kept track of by the lists.
* @param lhs The first list.
* @param rhs The second list.
*/
template<typename T>
void swap(intrusive_list<T>& lhs, intrusive_list<T>& rhs) noexcept
{
lhs.swap(rhs);
}
} // namespace mcl
@@ -0,0 +1,32 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
// Reference: http://jonkagstrom.com/bit-mixer-construction/
#pragma once
#include <functional>
#include "mcl/bit/rotate.hpp"
#include "mcl/stdint.hpp"
namespace mcl::hash {
constexpr size_t xmrx(size_t x)
{
x ^= x >> 32;
x *= 0xff51afd7ed558ccd;
x ^= bit::rotate_right(x, 47) ^ bit::rotate_right(x, 23);
return x;
}
template<typename T>
struct avalanche_xmrx {
size_t operator()(const T& value) const
{
return xmrx(std::hash<T>{}(value));
}
};
} // namespace mcl::hash
@@ -0,0 +1,13 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#if defined(__clang) || defined(__GNUC__)
# define ASSUME(expr) [&] { if (!(expr)) __builtin_unreachable(); }()
#elif defined(_MSC_VER)
# define ASSUME(expr) __assume(expr)
#else
# define ASSUME(expr)
#endif
@@ -0,0 +1,37 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include <iterator>
namespace mcl::iterator {
namespace detail {
template<typename T>
struct reverse_adapter {
T& iterable;
constexpr auto begin()
{
using namespace std;
return rbegin(iterable);
}
constexpr auto end()
{
using namespace std;
return rend(iterable);
}
};
} // namespace detail
template<typename T>
constexpr detail::reverse_adapter<T> reverse(T&& iterable)
{
return detail::reverse_adapter<T>{iterable};
}
} // namespace mcl::iterator
@@ -0,0 +1,13 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include <mcl/macro/concatenate_tokens.hpp>
#ifdef __COUNTER__
# define ANONYMOUS_VARIABLE(str) CONCATENATE_TOKENS(str, __COUNTER__)
#else
# define ANONYMOUS_VARIABLE(str) CONCATENATE_TOKENS(str, __LINE__)
#endif
@@ -0,0 +1,40 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#if defined(__ARM64__) || defined(__aarch64__) || defined(_M_ARM64)
# define MCL_ARCHITECTURE arm64
# define MCL_ARCHITECTURE_ARM64 1
#elif defined(__arm__) || defined(__TARGET_ARCH_ARM) || defined(_M_ARM)
# define MCL_ARCHITECTURE arm32
# define MCL_ARCHITECTURE_ARM32 1
#elif defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(_M_X64)
# define MCL_ARCHITECTURE x86_64
# define MCL_ARCHITECTURE_X86_64 1
#elif defined(__i386) || defined(__i386__) || defined(_M_IX86)
# define MCL_ARCHITECTURE x86_32
# define MCL_ARCHITECTURE_X86_32 1
#elif defined(__ia64) || defined(__ia64__) || defined(_M_IA64)
# define MCL_ARCHITECTURE ia64
# define MCL_ARCHITECTURE_IA64 1
#elif defined(__mips) || defined(__mips__) || defined(_M_MRX000)
# define MCL_ARCHITECTURE mips
# define MCL_ARCHITECTURE_MIPS 1
#elif defined(__ppc64__) || defined(__powerpc64__)
# define MCL_ARCHITECTURE ppc64
# define MCL_ARCHITECTURE_PPC64 1
#elif defined(__ppc__) || defined(__ppc) || defined(__powerpc__) || defined(_ARCH_COM) || defined(_ARCH_PWR) || defined(_ARCH_PPC) || defined(_M_MPPC) || defined(_M_PPC)
# define MCL_ARCHITECTURE ppc32
# define MCL_ARCHITECTURE_PPC32 1
#elif defined(__riscv)
# define MCL_ARCHITECTURE riscv
# define MCL_ARCHITECTURE_RISCV 1
#elif defined(__EMSCRIPTEN__)
# define MCL_ARCHITECTURE wasm
# define MCL_ARCHITECTURE_WASM 1
#else
# define MCL_ARCHITECTURE generic
# define MCL_ARCHITECTURE_GENERIC 1
#endif
@@ -0,0 +1,8 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#define CONCATENATE_TOKENS(x, y) CONCATENATE_TOKENS_IMPL(x, y)
#define CONCATENATE_TOKENS_IMPL(x, y) x##y
@@ -0,0 +1,36 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/stdint.hpp"
namespace mcl::memory::literals {
constexpr u64 operator""_KiB(unsigned long long int x)
{
return 1024ULL * x;
}
constexpr u64 operator""_MiB(unsigned long long int x)
{
return 1024_KiB * x;
}
constexpr u64 operator""_GiB(unsigned long long int x)
{
return 1024_MiB * x;
}
constexpr u64 operator""_TiB(unsigned long long int x)
{
return 1024_GiB * x;
}
constexpr u64 operator""_PiB(unsigned long long int x)
{
return 1024_TiB * x;
}
} // namespace mcl::memory::literals
@@ -0,0 +1,46 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include <type_traits>
#ifdef _MSC_VER
# include <malloc.h>
#else
# include <cstdlib>
#endif
namespace mcl {
namespace detail {
struct aligned_alloc_deleter {
template<typename T>
void operator()(T* p) const
{
#ifdef _MSC_VER
_aligned_free(const_cast<std::remove_const_t<T>*>(p));
#else
std::free(const_cast<std::remove_const_t<T>*>(p));
#endif
}
};
} // namespace detail
template<size_t, typename T>
using overaligned_unique_ptr = std::unique_ptr<T, detail::aligned_alloc_deleter>;
template<size_t alignment, typename T>
auto make_overaligned_unique_ptr_array(size_t element_count)
{
const size_t min_size = element_count * sizeof(T);
const size_t alloc_size = (min_size + alignment - 1) / alignment * alignment;
#ifdef _MSC_VER
return overaligned_unique_ptr<alignment, T[]>{static_cast<T*>(_aligned_malloc(alloc_size, alignment))};
#else
return overaligned_unique_ptr<alignment, T[]>{static_cast<T*>(std::aligned_alloc(alignment, alloc_size))};
#endif
}
} // namespace mcl
@@ -0,0 +1,25 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
namespace mcl::mp {
namespace detail {
template<template<class...> class F, class L>
struct apply_impl;
template<template<class...> class F, template<class...> class LT, class... Es>
struct apply_impl<F, LT<Es...>> {
using type = F<Es...>;
};
} // namespace detail
/// Invokes metafunction F where the arguments are all the members of list L
template<template<class...> class F, class L>
using apply = typename detail::apply_impl<F, L>::type;
} // namespace mcl::mp
@@ -0,0 +1,18 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
namespace mcl::mp {
/// Binds the first sizeof...(A) arguments of metafunction F with arguments A
template<template<class...> class F, class... As>
struct bind {
template<class... Rs>
using type = F<As..., Rs...>;
};
} // namespace mcl::mp
#define MCL_MP_BIND(...) ::mcl::mp::bind<__VA_ARGS__>::template type
@@ -0,0 +1,22 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
namespace mcl::mp {
namespace detail {
template<class T>
struct identity_impl {
using type = T;
};
} // namespace detail
/// Identity metafunction
template<class T>
using identity = typename identity_impl<T>::type;
} // namespace mcl::mp
@@ -0,0 +1,25 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
namespace mcl::mp {
namespace detail {
template<template<class...> class F, class L>
struct map_impl;
template<template<class...> class F, template<class...> class LT, class... Es>
struct map_impl<F, LT<Es...>> {
using type = LT<F<Es>...>;
};
} // namespace detail
/// Applies each element of list L to metafunction F
template<template<class...> class F, class L>
using map = typename detail::map_impl<F, L>::type;
} // namespace mcl::mp
@@ -0,0 +1,19 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/mp/metavalue/lift_value.hpp"
namespace mcl::mp {
/// Bitwise and of metavalues Vs
template<class... Vs>
using bit_and = lift_value<(Vs::value & ...)>;
/// Bitwise and of metavalues Vs
template<class... Vs>
constexpr auto bit_and_v = (Vs::value & ...);
} // namespace mcl::mp
@@ -0,0 +1,19 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/mp/metavalue/lift_value.hpp"
namespace mcl::mp {
/// Bitwise not of metavalue V
template<class V>
using bit_not = lift_value<~V::value>;
/// Bitwise not of metavalue V
template<class V>
constexpr auto bit_not_v = ~V::value;
} // namespace mcl::mp
@@ -0,0 +1,19 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/mp/metavalue/lift_value.hpp"
namespace mcl::mp {
/// Bitwise or of metavalues Vs
template<class... Vs>
using bit_or = lift_value<(Vs::value | ...)>;
/// Bitwise or of metavalues Vs
template<class... Vs>
constexpr auto bit_or_v = (Vs::value | ...);
} // namespace mcl::mp
@@ -0,0 +1,19 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/mp/metavalue/lift_value.hpp"
namespace mcl::mp {
/// Bitwise xor of metavalues Vs
template<class... Vs>
using bit_xor = lift_value<(Vs::value ^ ...)>;
/// Bitwise xor of metavalues Vs
template<class... Vs>
constexpr auto bit_xor_v = (Vs::value ^ ...);
} // namespace mcl::mp
@@ -0,0 +1,42 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/mp/metavalue/logic_if.hpp"
#include "mcl/mp/metavalue/value.hpp"
namespace mcl::mp {
namespace detail {
template<class...>
struct conjunction_impl;
template<>
struct conjunction_impl<> {
using type = false_type;
};
template<class V>
struct conjunction_impl<V> {
using type = V;
};
template<class V1, class... Vs>
struct conjunction_impl<V1, Vs...> {
using type = logic_if<V1, typename conjunction_impl<Vs...>::type, V1>;
};
} // namespace detail
/// Conjunction of metavalues Vs with short-circuiting and type preservation.
template<class... Vs>
using conjunction = typename detail::conjunction_impl<Vs...>::type;
/// Conjunction of metavalues Vs with short-circuiting and type preservation.
template<class... Vs>
constexpr auto conjunction_v = conjunction<Vs...>::value;
} // namespace mcl::mp
@@ -0,0 +1,42 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/mp/metavalue/logic_if.hpp"
#include "mcl/mp/metavalue/value.hpp"
namespace mcl::mp {
namespace detail {
template<class...>
struct disjunction_impl;
template<>
struct disjunction_impl<> {
using type = false_type;
};
template<class V>
struct disjunction_impl<V> {
using type = V;
};
template<class V1, class... Vs>
struct disjunction_impl<V1, Vs...> {
using type = logic_if<V1, V1, typename disjunction_impl<Vs...>::type>;
};
} // namespace detail
/// Disjunction of metavalues Vs with short-circuiting and type preservation.
template<class... Vs>
using disjunction = typename detail::disjunction_impl<Vs...>::type;
/// Disjunction of metavalues Vs with short-circuiting and type preservation.
template<class... Vs>
constexpr auto disjunction_v = disjunction<Vs...>::value;
} // namespace mcl::mp
@@ -0,0 +1,15 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include <type_traits>
namespace mcl::mp {
/// Lifts a value into a type (a metavalue)
template<auto V>
using lift_value = std::integral_constant<decltype(V), V>;
} // namespace mcl::mp
@@ -0,0 +1,19 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/mp/metavalue/value.hpp"
namespace mcl::mp {
/// Logical conjunction of metavalues Vs without short-circuiting or type presevation.
template<class... Vs>
using logic_and = bool_value<(true && ... && Vs::value)>;
/// Logical conjunction of metavalues Vs without short-circuiting or type presevation.
template<class... Vs>
constexpr bool logic_and_v = (true && ... && Vs::value);
} // namespace mcl::mp
@@ -0,0 +1,21 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include <type_traits>
#include "mcl/mp/metavalue/value.hpp"
namespace mcl::mp {
/// Conditionally select between types T and F based on boolean metavalue V
template<class V, class T, class F>
using logic_if = std::conditional_t<bool(V::value), T, F>;
/// Conditionally select between metavalues T and F based on boolean metavalue V
template<class V, class TV, class FV>
constexpr auto logic_if_v = logic_if<V, TV, FV>::value;
} // namespace mcl::mp
@@ -0,0 +1,19 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/mp/metavalue/value.hpp"
namespace mcl::mp {
/// Logical negation of metavalue V.
template<class V>
using logic_not = bool_value<!bool(V::value)>;
/// Logical negation of metavalue V.
template<class V>
constexpr bool logic_not_v = !bool(V::value);
} // namespace mcl::mp
@@ -0,0 +1,19 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/mp/metavalue/value.hpp"
namespace mcl::mp {
/// Logical disjunction of metavalues Vs without short-circuiting or type presevation.
template<class... Vs>
using logic_or = bool_value<(false || ... || Vs::value)>;
/// Logical disjunction of metavalues Vs without short-circuiting or type presevation.
template<class... Vs>
constexpr bool logic_or_v = (false || ... || Vs::value);
} // namespace mcl::mp
@@ -0,0 +1,19 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/mp/metavalue/lift_value.hpp"
namespace mcl::mp {
/// Product of metavalues Vs
template<class... Vs>
using product = lift_value<(Vs::value * ...)>;
/// Product of metavalues Vs
template<class... Vs>
constexpr auto product_v = (Vs::value * ...);
} // namespace mcl::mp
@@ -0,0 +1,19 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/mp/metavalue/lift_value.hpp"
namespace mcl::mp {
/// Sum of metavalues Vs
template<class... Vs>
using sum = lift_value<(Vs::value + ...)>;
/// Sum of metavalues Vs
template<class... Vs>
constexpr auto sum_v = (Vs::value + ...);
} // namespace mcl::mp
@@ -0,0 +1,30 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include <cstddef>
#include <type_traits>
namespace mcl::mp {
/// A metavalue (of type VT and value v).
template<class VT, VT v>
using value = std::integral_constant<VT, v>;
/// A metavalue of type size_t (and value v).
template<size_t v>
using size_value = value<size_t, v>;
/// A metavalue of type bool (and value v). (Aliases to std::bool_constant.)
template<bool v>
using bool_value = value<bool, v>;
/// true metavalue (Aliases to std::true_type).
using true_type = bool_value<true>;
/// false metavalue (Aliases to std::false_type).
using false_type = bool_value<false>;
} // namespace mcl::mp
@@ -0,0 +1,15 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include <type_traits>
namespace mcl::mp {
/// Casts a metavalue from one type to another
template<class T, class V>
using value_cast = std::integral_constant<T, static_cast<T>(V::value)>;
} // namespace mcl::mp
@@ -0,0 +1,15 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include <type_traits>
namespace mcl::mp {
/// Do two metavalues contain the same value?
template<class V1, class V2>
using value_equal = std::bool_constant<V1::value == V2::value>;
} // namespace mcl::mp
@@ -0,0 +1,19 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/mp/metavalue/lift_value.hpp"
namespace mcl::mp {
/// Metafunction that returns the number of arguments it has
template<typename... Ts>
using argument_count = lift_value<sizeof...(Ts)>;
/// Metafunction that returns the number of arguments it has
template<typename... Ts>
constexpr auto argument_count_v = sizeof...(Ts);
} // namespace mcl::mp
@@ -0,0 +1,25 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
namespace mcl::mp {
namespace detail {
template<class... L>
struct append_impl;
template<template<class...> class LT, class... E1s, class... E2s>
struct append_impl<LT<E1s...>, E2s...> {
using type = LT<E1s..., E2s...>;
};
} // namespace detail
/// Append items E to list L
template<class L, class... Es>
using append = typename detail::append_impl<L, Es...>::type;
} // namespace mcl::mp
@@ -0,0 +1,47 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/mp/metafunction/bind.hpp"
#include "mcl/mp/metafunction/map.hpp"
#include "mcl/mp/typelist/append.hpp"
#include "mcl/mp/typelist/concat.hpp"
#include "mcl/mp/typelist/list.hpp"
namespace mcl::mp {
namespace detail {
template<class... Ls>
struct cartesian_product_impl;
template<class RL>
struct cartesian_product_impl<RL> {
using type = RL;
};
template<template<class...> class LT, class... REs, class... E2s>
struct cartesian_product_impl<LT<REs...>, LT<E2s...>> {
using type = concat<
map<MCL_MP_BIND(append, REs), list<E2s...>>...>;
};
template<class RL, class L2, class L3, class... Ls>
struct cartesian_product_impl<RL, L2, L3, Ls...> {
using type = typename cartesian_product_impl<
typename cartesian_product_impl<RL, L2>::type,
L3,
Ls...>::type;
};
} // namespace detail
/// Produces the cartesian product of a set of lists
/// For example:
/// cartesian_product<list<A, B>, list<D, E>> == list<list<A, D>, list<A, E>, list<B, D>, list<B, E>
template<typename L1, typename... Ls>
using cartesian_product = typename detail::cartesian_product_impl<map<list, L1>, Ls...>::type;
} // namespace mcl::mp
@@ -0,0 +1,94 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/mp/typelist/list.hpp"
namespace mcl::mp {
namespace detail {
template<class... Ls>
struct concat_impl;
template<>
struct concat_impl<> {
using type = list<>;
};
template<class L>
struct concat_impl<L> {
using type = L;
};
template<template<class...> class LT, class... E1s, class... E2s, class... Ls>
struct concat_impl<LT<E1s...>, LT<E2s...>, Ls...> {
using type = typename concat_impl<LT<E1s..., E2s...>, Ls...>::type;
};
template<template<class...> class LT,
class... E1s,
class... E2s,
class... E3s,
class... E4s,
class... E5s,
class... E6s,
class... E7s,
class... E8s,
class... E9s,
class... E10s,
class... E11s,
class... E12s,
class... E13s,
class... E14s,
class... E15s,
class... E16s,
class... Ls>
struct concat_impl<
LT<E1s...>,
LT<E2s...>,
LT<E3s...>,
LT<E4s...>,
LT<E5s...>,
LT<E6s...>,
LT<E7s...>,
LT<E8s...>,
LT<E9s...>,
LT<E10s...>,
LT<E11s...>,
LT<E12s...>,
LT<E13s...>,
LT<E14s...>,
LT<E15s...>,
LT<E16s...>,
Ls...> {
using type = typename concat_impl<
LT<
E1s...,
E2s...,
E3s...,
E4s...,
E5s...,
E6s...,
E7s...,
E8s...,
E9s...,
E10s...,
E11s...,
E12s...,
E13s...,
E14s...,
E15s...,
E16s...>,
Ls...>::type;
};
} // namespace detail
/// Concatenate lists together
template<class... Ls>
using concat = typename detail::concat_impl<Ls...>::type;
} // namespace mcl::mp
@@ -0,0 +1,23 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/mp/metavalue/value.hpp"
namespace mcl::mp {
/// Does list L contain an element which is same as type T?
template<class L, class T>
struct contains;
template<template<class...> class LT, class... Ts, class T>
struct contains<LT<Ts...>, T>
: bool_value<(false || ... || std::is_same_v<Ts, T>)> {};
/// Does list L contain an element which is same as type T?
template<class L, class T>
constexpr bool contains_v = contains<L, T>::value;
} // namespace mcl::mp
@@ -0,0 +1,33 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include <cstddef>
#include <type_traits>
namespace mcl::mp {
namespace detail {
template<size_t N, class L>
struct drop_impl;
template<size_t N, template<class...> class LT>
struct drop_impl<N, LT<>> {
using type = LT<>;
};
template<size_t N, template<class...> class LT, class E1, class... Es>
struct drop_impl<N, LT<E1, Es...>> {
using type = std::conditional_t<N == 0, LT<E1, Es...>, typename drop_impl<N - 1, LT<Es...>>::type>;
};
} // namespace detail
/// Drops the first N elements of list L
template<std::size_t N, class L>
using drop = typename detail::drop_impl<N, L>::type;
} // namespace mcl::mp
@@ -0,0 +1,18 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include <cstddef>
#include <tuple>
#include "mcl/mp/metafunction/apply.hpp"
namespace mcl::mp {
/// Get element I from list L
template<std::size_t I, class L>
using get = std::tuple_element_t<I, apply<std::tuple, L>>;
} // namespace mcl::mp
@@ -0,0 +1,25 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
namespace mcl::mp {
namespace detail {
template<class L>
struct head_impl;
template<template<class...> class LT, class E1, class... Es>
struct head_impl<LT<E1, Es...>> {
using type = E1;
};
} // namespace detail
/// Gets the tail/cdr/all-but-the-first-element of list L
template<class L>
using head = typename detail::head_impl<L>::type;
} // namespace mcl::mp
@@ -0,0 +1,20 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/mp/metafunction/apply.hpp"
#include "mcl/mp/misc/argument_count.hpp"
namespace mcl::mp {
/// Length of list L
template<class L>
using length = apply<argument_count, L>;
/// Length of list L
template<class L>
constexpr auto length_v = length<L>::value;
} // namespace mcl::mp
@@ -0,0 +1,35 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include <type_traits>
#include <utility>
#include "mcl/mp/typelist/list.hpp"
namespace mcl::mp {
namespace detail {
template<class VL>
struct lift_sequence_impl;
template<class T, template<class, T...> class VLT, T... values>
struct lift_sequence_impl<VLT<T, values...>> {
using type = list<std::integral_constant<T, values>...>;
};
template<class T, T... values>
struct lift_sequence_impl<std::integer_sequence<T, values...>> {
using type = list<std::integral_constant<T, values>...>;
};
} // namespace detail
/// Lifts values in value list VL to create a type list.
template<class VL>
using lift_sequence = typename detail::lift_sequence_impl<VL>::type;
} // namespace mcl::mp
@@ -0,0 +1,13 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
namespace mcl::mp {
/// Contains a list of types
template<class... E>
struct list {};
} // namespace mcl::mp
@@ -0,0 +1,24 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include <tuple>
namespace mcl::mp {
/// Converts a list of metavalues to a tuple.
template<class L>
struct lower_to_tuple;
template<template<class...> class LT, class... Es>
struct lower_to_tuple<LT<Es...>> {
static constexpr auto value = std::make_tuple(static_cast<typename Es::value_type>(Es::value)...);
};
/// Converts a list of metavalues to a tuple.
template<class L>
constexpr auto lower_to_tuple_v = lower_to_tuple<L>::value;
} // namespace mcl::mp
@@ -0,0 +1,25 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
namespace mcl::mp {
namespace detail {
template<class... L>
struct prepend_impl;
template<template<class...> class LT, class... E1s, class... E2s>
struct prepend_impl<LT<E1s...>, E2s...> {
using type = LT<E2s..., E1s...>;
};
} // namespace detail
/// Prepend items E to list L
template<class L, class... Es>
using prepend = typename detail::prepend_impl<L, Es...>::type;
} // namespace mcl::mp
@@ -0,0 +1,25 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
namespace mcl::mp {
namespace detail {
template<class L>
struct tail_impl;
template<template<class...> class LT, class E1, class... Es>
struct tail_impl<LT<E1, Es...>> {
using type = LT<Es...>;
};
} // namespace detail
/// Gets the first type of list L
template<class L>
using tail = typename detail::tail_impl<L>::type;
} // namespace mcl::mp
@@ -0,0 +1,94 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include <exception>
#include <type_traits>
#include <utility>
#include <mcl/macro/anonymous_variable.hpp>
namespace mcl::detail {
struct scope_exit_tag {};
struct scope_fail_tag {};
struct scope_success_tag {};
template<typename Function>
class scope_exit final {
public:
explicit scope_exit(Function&& fn)
: function(std::move(fn)) {}
~scope_exit() noexcept
{
function();
}
private:
Function function;
};
template<typename Function>
class scope_fail final {
public:
explicit scope_fail(Function&& fn)
: function(std::move(fn)), exception_count(std::uncaught_exceptions()) {}
~scope_fail() noexcept
{
if (std::uncaught_exceptions() > exception_count) {
function();
}
}
private:
Function function;
int exception_count;
};
template<typename Function>
class scope_success final {
public:
explicit scope_success(Function&& fn)
: function(std::move(fn)), exception_count(std::uncaught_exceptions()) {}
~scope_success()
{
if (std::uncaught_exceptions() <= exception_count) {
function();
}
}
private:
Function function;
int exception_count;
};
// We use ->* here as it has the highest precedence of the operators we can use.
template<typename Function>
auto operator->*(scope_exit_tag, Function&& function)
{
return scope_exit<std::decay_t<Function>>{std::forward<Function>(function)};
}
template<typename Function>
auto operator->*(scope_fail_tag, Function&& function)
{
return scope_fail<std::decay_t<Function>>{std::forward<Function>(function)};
}
template<typename Function>
auto operator->*(scope_success_tag, Function&& function)
{
return scope_success<std::decay_t<Function>>{std::forward<Function>(function)};
}
} // namespace mcl::detail
#define SCOPE_EXIT auto ANONYMOUS_VARIABLE(MCL_SCOPE_EXIT_VAR_) = ::mcl::detail::scope_exit_tag{}->*[&]() noexcept
#define SCOPE_FAIL auto ANONYMOUS_VARIABLE(MCL_SCOPE_FAIL_VAR_) = ::mcl::detail::scope_fail_tag{}->*[&]() noexcept
#define SCOPE_SUCCESS auto ANONYMOUS_VARIABLE(MCL_SCOPE_FAIL_VAR_) = ::mcl::detail::scope_success_tag{}->*[&]()
+27
View File
@@ -0,0 +1,27 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include <cstddef>
#include <cstdint>
using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;
using uptr = std::uintptr_t;
using s8 = std::int8_t;
using s16 = std::int16_t;
using s32 = std::int32_t;
using s64 = std::int64_t;
using sptr = std::intptr_t;
using size_t = std::size_t;
using f32 = float;
using f64 = double;
static_assert(sizeof(f32) == sizeof(u32), "f32 must be 32 bits wide");
static_assert(sizeof(f64) == sizeof(u64), "f64 must be 64 bits wide");
@@ -0,0 +1,70 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include <cstddef>
#include <tuple>
#include "mcl/mp/typelist/list.hpp"
namespace mcl {
template<class F>
struct function_info : function_info<decltype(&F::operator())> {};
template<class R, class... As>
struct function_info<R(As...)> {
using return_type = R;
using parameter_list = mp::list<As...>;
static constexpr std::size_t parameter_count = sizeof...(As);
using equivalent_function_type = R(As...);
template<std::size_t I>
struct parameter {
static_assert(I < parameter_count, "Non-existent parameter");
using type = std::tuple_element_t<I, std::tuple<As...>>;
};
};
template<class R, class... As>
struct function_info<R (*)(As...)> : function_info<R(As...)> {};
template<class C, class R, class... As>
struct function_info<R (C::*)(As...)> : function_info<R(As...)> {
using class_type = C;
using equivalent_function_type_with_class = R(C*, As...);
};
template<class C, class R, class... As>
struct function_info<R (C::*)(As...) const> : function_info<R(As...)> {
using class_type = C;
using equivalent_function_type_with_class = R(C*, As...);
};
template<class F>
constexpr size_t parameter_count_v = function_info<F>::parameter_count;
template<class F>
using parameter_list = typename function_info<F>::parameter_list;
template<class F, std::size_t I>
using get_parameter = typename function_info<F>::template parameter<I>::type;
template<class F>
using equivalent_function_type = typename function_info<F>::equivalent_function_type;
template<class F>
using equivalent_function_type_with_class = typename function_info<F>::equivalent_function_type_with_class;
template<class F>
using return_type = typename function_info<F>::return_type;
template<class F>
using class_type = typename function_info<F>::class_type;
} // namespace mcl
@@ -0,0 +1,48 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/stdint.hpp"
namespace mcl {
namespace detail {
template<size_t size>
struct integer_of_size_impl {};
template<>
struct integer_of_size_impl<8> {
using unsigned_type = u8;
using signed_type = s8;
};
template<>
struct integer_of_size_impl<16> {
using unsigned_type = u16;
using signed_type = s16;
};
template<>
struct integer_of_size_impl<32> {
using unsigned_type = u32;
using signed_type = s32;
};
template<>
struct integer_of_size_impl<64> {
using unsigned_type = u64;
using signed_type = s64;
};
} // namespace detail
template<size_t size>
using unsigned_integer_of_size = typename detail::integer_of_size_impl<size>::unsigned_type;
template<size_t size>
using signed_integer_of_size = typename detail::integer_of_size_impl<size>::signed_type;
} // namespace mcl
@@ -0,0 +1,22 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/mp/metavalue/value.hpp"
namespace mcl {
/// Is type T an instance of template class C?
template<template<class...> class, class>
struct is_instance_of_template : mp::false_type {};
template<template<class...> class C, class... As>
struct is_instance_of_template<C, C<As...>> : mp::true_type {};
/// Is type T an instance of template class C?
template<template<class...> class C, class T>
constexpr bool is_instance_of_template_v = is_instance_of_template<C, T>::value;
} // namespace mcl