#pragma once #include #include #include #include #include namespace dusk { namespace detail { template struct uint_of_size; template <> struct uint_of_size<1> { using type = uint8_t; }; template <> struct uint_of_size<2> { using type = uint16_t; }; template <> struct uint_of_size<4> { using type = uint32_t; }; template <> struct uint_of_size<8> { using type = uint64_t; }; template using uint_of_size_t = uint_of_size::type; template requires(std::is_trivially_copyable_v) T unaligned_load(const void* source) noexcept { T value; std::memcpy(&value, source, sizeof(value)); return value; } template requires(std::is_trivially_copyable_v) void unaligned_store(void* destination, T value) noexcept { std::memcpy(destination, &value, sizeof(value)); } } // namespace detail template requires(std::is_unsigned_v) constexpr T bswap(T value) noexcept { if constexpr (sizeof(T) == 1) { return value; } else if constexpr (sizeof(T) == 2) { return static_cast((value << 8) | (value >> 8)); } else if constexpr (sizeof(T) == 4) { return static_cast(((value & 0x000000ffU) << 24) | ((value & 0x0000ff00U) << 8) | ((value & 0x00ff0000U) >> 8) | ((value & 0xff000000U) >> 24)); } else { static_assert(sizeof(T) == 8); return static_cast( ((value & 0x00000000000000ffULL) << 56) | ((value & 0x000000000000ff00ULL) << 40) | ((value & 0x0000000000ff0000ULL) << 24) | ((value & 0x00000000ff000000ULL) << 8) | ((value & 0x000000ff00000000ULL) >> 8) | ((value & 0x0000ff0000000000ULL) >> 24) | ((value & 0x00ff000000000000ULL) >> 40) | ((value & 0xff00000000000000ULL) >> 56)); } } /// Reads an unaligned integral value in the specified byte order. template requires(std::is_integral_v && !std::is_same_v) T read_bits(const void* source, std::endian endian = std::endian::big) noexcept { using Bits = std::make_unsigned_t; Bits value = detail::unaligned_load(source); if constexpr (sizeof(Bits) > 1) { if (endian != std::endian::native) { value = bswap(value); } } return std::bit_cast(value); } template requires(std::is_integral_v && !std::is_same_v) constexpr T read_bits(const uint8_t* source, std::endian endian = std::endian::big) noexcept { if (!std::is_constant_evaluated()) { return read_bits(static_cast(source), endian); } using Bits = std::make_unsigned_t; Bits value{}; if (endian == std::endian::big) { for (size_t i = 0; i < sizeof(Bits); ++i) { value = static_cast((value << 8) | source[i]); } } else { for (size_t i = 0; i < sizeof(Bits); ++i) { value |= static_cast(source[i]) << (i * 8); } } return std::bit_cast(value); } /// Reads an unaligned floating-point value in the specified byte order. template requires( std::is_floating_point_v && requires { typename detail::uint_of_size_t; }) T read_bits(const void* source, std::endian endian = std::endian::big) noexcept { using Bits = detail::uint_of_size_t; return std::bit_cast(read_bits(source, endian)); } template requires( std::is_floating_point_v && requires { typename detail::uint_of_size_t; }) constexpr T read_bits(const uint8_t* source, std::endian endian = std::endian::big) noexcept { using Bits = detail::uint_of_size_t; return std::bit_cast(read_bits(source, endian)); } /// Writes an unaligned integral value in the specified byte order. template requires(std::is_integral_v && !std::is_same_v) void write_bits(void* destination, T value, std::endian endian = std::endian::big) noexcept { using Bits = std::make_unsigned_t; Bits bits = std::bit_cast(value); if constexpr (sizeof(Bits) > 1) { if (endian != std::endian::native) { bits = bswap(bits); } } detail::unaligned_store(destination, bits); } template requires(std::is_integral_v && !std::is_same_v) constexpr void write_bits( uint8_t* destination, T value, std::endian endian = std::endian::big) noexcept { if (!std::is_constant_evaluated()) { write_bits(static_cast(destination), value, endian); return; } using Bits = std::make_unsigned_t; const Bits bits = std::bit_cast(value); if (endian == std::endian::big) { for (size_t i = 0; i < sizeof(Bits); ++i) { destination[sizeof(Bits) - i - 1] = static_cast(bits >> (i * 8)); } } else { for (size_t i = 0; i < sizeof(Bits); ++i) { destination[i] = static_cast(bits >> (i * 8)); } } } /// Writes an unaligned floating-point value in the specified byte order. template requires( std::is_floating_point_v && requires { typename detail::uint_of_size_t; }) void write_bits(void* destination, T value, std::endian endian = std::endian::big) noexcept { using Bits = detail::uint_of_size_t; write_bits(destination, std::bit_cast(value), endian); } template requires( std::is_floating_point_v && requires { typename detail::uint_of_size_t; }) constexpr void write_bits( uint8_t* destination, T value, std::endian endian = std::endian::big) noexcept { using Bits = detail::uint_of_size_t; write_bits(destination, std::bit_cast(value), endian); } } // namespace dusk