/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_MEMORY_HPP #define MPT_BASE_MEMORY_HPP #include "mpt/base/integer.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/macros.hpp" #include "mpt/base/span.hpp" #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { using byte_span = mpt::span; using const_byte_span = mpt::span; // Tell which types are safe for mpt::byte_cast. // signed char is actually not allowed to alias into an object representation, // which means that, if the actual type is not itself signed char but char or // unsigned char instead, dereferencing the signed char pointer is undefined // behaviour. template struct is_byte_castable : public std::false_type { }; template <> struct is_byte_castable : public std::true_type { }; template <> struct is_byte_castable : public std::true_type { }; template <> struct is_byte_castable : public std::true_type { }; template <> struct is_byte_castable : public std::true_type { }; template <> struct is_byte_castable : public std::true_type { }; template <> struct is_byte_castable : public std::true_type { }; template struct is_byte : public std::false_type { }; template <> struct is_byte : public std::true_type { }; template <> struct is_byte : public std::true_type { }; template constexpr bool declare_binary_safe(const T &) noexcept { return false; } constexpr bool declare_binary_safe(const char &) noexcept { return true; } constexpr bool declare_binary_safe(const uint8 &) noexcept { return true; } constexpr bool declare_binary_safe(const int8 &) noexcept { return true; } constexpr bool declare_binary_safe(const std::byte &) noexcept { return true; } // Tell which types are safe to binary write into files. // By default, no types are safe. // When a safe type gets defined, // also specialize this template so that IO functions will work. template struct is_binary_safe : public std::conditional::type { }; // Generic Specialization for arrays. template struct is_binary_safe : public is_binary_safe { }; template struct is_binary_safe : public is_binary_safe { }; template struct is_binary_safe> : public is_binary_safe { }; template struct is_binary_safe> : public is_binary_safe { }; template constexpr bool check_binary_size(std::size_t size) noexcept { return true && (sizeof(T) == size) && (alignof(T) == 1) && std::is_standard_layout::value && std::has_unique_object_representations::value && mpt::is_binary_safe::value; } template struct byte_cast_impl { inline Tdst operator()(Tsrc src) const noexcept { static_assert(sizeof(Tsrc) == sizeof(std::byte)); static_assert(sizeof(Tdst) == sizeof(std::byte)); // not checking is_byte_castable here because we are actually // doing a static_cast and converting the value static_assert(std::is_integral::value || mpt::is_byte::value); static_assert(std::is_integral::value || mpt::is_byte::value); return static_cast(src); } }; template struct byte_cast_impl, mpt::span> { inline mpt::span operator()(mpt::span src) const noexcept { static_assert(sizeof(Tsrc) == sizeof(std::byte)); static_assert(sizeof(Tdst) == sizeof(std::byte)); static_assert(mpt::is_byte_castable::value); static_assert(mpt::is_byte_castable::value); static_assert(std::is_integral::value || mpt::is_byte::value); static_assert(std::is_integral::value || mpt::is_byte::value); return mpt::as_span(mpt::byte_cast_impl()(src.data()), mpt::byte_cast_impl()(src.data() + src.size())); } }; template struct byte_cast_impl { inline Tdst * operator()(Tsrc * src) const noexcept { static_assert(sizeof(Tsrc) == sizeof(std::byte)); static_assert(sizeof(Tdst) == sizeof(std::byte)); static_assert(mpt::is_byte_castable::value); static_assert(mpt::is_byte_castable::value); static_assert(std::is_integral::value || mpt::is_byte::value); static_assert(std::is_integral::value || mpt::is_byte::value); return reinterpret_cast(src); } }; template struct void_cast_impl; template struct void_cast_impl { inline Tdst * operator()(void * src) const noexcept { static_assert(sizeof(Tdst) == sizeof(std::byte)); static_assert(mpt::is_byte_castable::value); static_assert(std::is_integral::value || mpt::is_byte::value); return reinterpret_cast(src); } }; template struct void_cast_impl { inline Tdst * operator()(const void * src) const noexcept { static_assert(sizeof(Tdst) == sizeof(std::byte)); static_assert(mpt::is_byte_castable::value); static_assert(std::is_integral::value || mpt::is_byte::value); return reinterpret_cast(src); } }; template struct void_cast_impl { inline void * operator()(Tsrc * src) const noexcept { static_assert(sizeof(Tsrc) == sizeof(std::byte)); static_assert(mpt::is_byte_castable::value); static_assert(std::is_integral::value || mpt::is_byte::value); return reinterpret_cast(src); } }; template struct void_cast_impl { inline const void * operator()(Tsrc * src) const noexcept { static_assert(sizeof(Tsrc) == sizeof(std::byte)); static_assert(mpt::is_byte_castable::value); static_assert(std::is_integral::value || mpt::is_byte::value); return reinterpret_cast(src); } }; // casts between different byte (char) types or pointers to these types template inline Tdst byte_cast(Tsrc src) noexcept { return byte_cast_impl()(src); } // casts between pointers to void and pointers to byte template inline Tdst void_cast(Tsrc src) noexcept { return void_cast_impl()(src); } template MPT_CONSTEXPRINLINE std::byte as_byte(T src) noexcept { static_assert(std::is_integral::value); return static_cast(static_cast(src)); } template struct as_raw_memory_impl { inline mpt::const_byte_span operator()(const T & v) const { static_assert(mpt::is_binary_safe::type>::value); return mpt::as_span(reinterpret_cast(&v), sizeof(T)); } inline mpt::byte_span operator()(T & v) const { static_assert(mpt::is_binary_safe::type>::value); return mpt::as_span(reinterpret_cast(&v), sizeof(T)); } }; template struct as_raw_memory_impl { inline mpt::const_byte_span operator()(const T (&v)[N]) const { static_assert(mpt::is_binary_safe::type>::value); return mpt::as_span(reinterpret_cast(v), N * sizeof(T)); } inline mpt::byte_span operator()(T (&v)[N]) const { static_assert(mpt::is_binary_safe::type>::value); return mpt::as_span(reinterpret_cast(v), N * sizeof(T)); } }; template struct as_raw_memory_impl { inline mpt::const_byte_span operator()(const T (&v)[N]) const { static_assert(mpt::is_binary_safe::type>::value); return mpt::as_span(reinterpret_cast(v), N * sizeof(T)); } }; // In order to be able to partially specialize it, // as_raw_memory is implemented via a class template. // Do not overload or specialize as_raw_memory directly. // Using a wrapper (by default just around a cast to const std::byte *), // allows for implementing raw memory access // via on-demand generating a cached serialized representation. template inline mpt::const_byte_span as_raw_memory(const T & v) { return mpt::as_raw_memory_impl()(v); } template inline mpt::byte_span as_raw_memory(T & v) { return mpt::as_raw_memory_impl()(v); } template inline void memclear(T & x) { static_assert(std::is_standard_layout::value); static_assert((std::is_trivially_default_constructible::value && std::is_trivially_copyable::value) || mpt::is_binary_safe::value); std::memset(&x, 0, sizeof(T)); } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_MEMORY_HPP