/* * mptMemory.h * ----------- * Purpose: Raw memory manipulation * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "BuildSettings.h" #include "mptAssert.h" #include "mptBaseTypes.h" #include "mptSpan.h" #if MPT_CXX_AT_LEAST(20) #include #endif #include #include #include #include OPENMPT_NAMESPACE_BEGIN namespace mpt { typedef mpt::span byte_span; typedef mpt::span const_byte_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 { }; // 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::false_type { }; // Specialization for byte types. template <> struct is_binary_safe : public std::true_type { }; template <> struct is_binary_safe : public std::true_type { }; template <> struct is_binary_safe : public std::true_type { }; template <> struct is_binary_safe : public std::true_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 { }; } // namespace mpt #define MPT_BINARY_STRUCT(type, size) \ static_assert(sizeof( type ) == (size) ); \ static_assert(alignof( type ) == 1); \ static_assert(std::is_standard_layout< type >::value); \ namespace mpt { \ template <> struct is_binary_safe< type > : public std::true_type { }; \ } \ /**/ template struct value_initializer { inline void operator () (T & x) { x = T(); } }; template struct value_initializer { inline void operator () (T (& a)[N]) { for(auto & e : a) { value_initializer()(e); } } }; template inline void Clear(T & x) { static_assert(!std::is_pointer::value); value_initializer()(x); } // Memset given object to zero. template inline void MemsetZero(T &a) { static_assert(std::is_pointer::value == false, "Won't memset pointers."); 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(&a, 0, sizeof(T)); } namespace mpt { #if MPT_CXX_AT_LEAST(20) using std::bit_cast; #else // C++2a compatible bit_cast. // Not implementing constexpr because this is not easily possible pre C++20. template MPT_FORCEINLINE typename std::enable_if<(sizeof(Tdst) == sizeof(Tsrc)) && std::is_trivially_copyable::value && std::is_trivially_copyable::value, Tdst>::type bit_cast(const Tsrc & src) noexcept { Tdst dst{}; std::memcpy(&dst, &src, sizeof(Tdst)); return dst; } #endif template struct byte_cast_impl { inline Tdst operator () (Tsrc src) const { 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 { 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 { 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 { 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 { 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 { 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 { 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) { return byte_cast_impl()(src); } // casts between pointers to void and pointers to byte template inline Tdst void_cast(Tsrc src) { return void_cast_impl()(src); } template MPT_CONSTEXPR14_FUN std::byte as_byte(T src) noexcept { static_assert(std::is_integral::value); return static_cast(static_cast(src)); } template struct GetRawBytesFunctor { 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 GetRawBytesFunctor { 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 GetRawBytesFunctor { 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::GetRawBytesFunctor()(v); } template inline mpt::byte_span as_raw_memory(T & v) { return mpt::GetRawBytesFunctor()(v); } } // namespace mpt OPENMPT_NAMESPACE_END