mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-10-04 21:29:36 +00:00
@@ -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
|
||||
+263
@@ -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
|
||||
+46
@@ -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
|
||||
+47
@@ -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{}->*[&]()
|
||||
@@ -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
|
||||
+22
@@ -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
|
||||
Reference in New Issue
Block a user