blob: a5206c0b05728e3dc5d93e3fdae698f4c813135d [file] [log] [blame]
#pragma once
#include <algorithm>
#include <fmt/format.h>
#include <span>
#include <stdexcept>
#include <string_view>
#include <type_traits>
namespace stdplus
{
namespace raw
{
namespace detail
{
/** @brief Gets the datatype referenced in a container
*/
template <typename Container>
using dataType = std::remove_pointer_t<decltype(std::data(
std::declval<std::add_lvalue_reference_t<Container>>()))>;
/** @brief Gets the sizetype referenced in a container
*/
template <typename Container>
using sizeType =
decltype(std::size(std::declval<std::add_lvalue_reference_t<Container>>()));
/** @brief Determines if the container holds trivially copyable data
*/
template <typename Container>
inline constexpr bool trivialContainer =
std::is_trivially_copyable_v<dataType<Container>>;
/** @brief Adds const to A if B is const
*/
template <typename A, typename B>
using copyConst =
std::conditional_t<std::is_const_v<B>, std::add_const_t<A>, A>;
/** @brief Determines if a type is a container of data
*/
template <typename, typename = void>
inline constexpr bool hasData = false;
template <typename T>
inline constexpr bool hasData<T, std::void_t<dataType<T>, sizeType<T>>> = true;
} // namespace detail
/** @brief Compares two containers to see if their raw bytes are equal
*
* @param[in] a - The first container
* @param[in] b - The second container
* @return True if they are the same, false otherwise
*/
template <typename A, typename B>
constexpr bool equal(const A& a, const B& b) noexcept
{
static_assert(std::is_trivially_copyable_v<A>);
static_assert(std::is_trivially_copyable_v<B>);
static_assert(sizeof(A) == sizeof(B));
const auto a_byte = reinterpret_cast<const std::byte*>(&a);
const auto b_byte = reinterpret_cast<const std::byte*>(&b);
return std::equal(a_byte, a_byte + sizeof(A), b_byte);
}
/** @brief Copies data from a buffer into a copyable type
*
* @param[in] data - The data buffer being copied from
* @return The copyable type with data populated
*/
#define STDPLUS_COPY_FROM(func, comp) \
template <typename T, typename Container> \
constexpr T func(const Container& c) \
{ \
static_assert(std::is_trivially_copyable_v<T>); \
static_assert(detail::trivialContainer<Container>); \
T ret; \
const size_t bytes = std::size(c) * sizeof(*std::data(c)); \
if (bytes comp sizeof(ret)) \
{ \
throw std::runtime_error( \
fmt::format(#func ": {} < {}", bytes, sizeof(ret))); \
} \
const auto c_bytes = reinterpret_cast<const std::byte*>(std::data(c)); \
const auto ret_bytes = reinterpret_cast<std::byte*>(&ret); \
std::copy(c_bytes, c_bytes + sizeof(ret), ret_bytes); \
return ret; \
}
STDPLUS_COPY_FROM(copyFrom, <)
STDPLUS_COPY_FROM(copyFromStrict, !=)
#undef STDPLUS_COPY_FROM
/** @brief If you can guarantee the underlying data is properly aligned
* for raw struct access this specifier is used to override compile checks. */
struct Aligned
{
};
struct UnAligned
{
};
/** @brief References the data from a buffer if aligned
*
* @param[in] data - The data buffer being referenced
* @return The reference to the data in the new type
*/
#define STDPLUS_REF_FROM(func, comp) \
template <typename T, typename A = stdplus::raw::UnAligned, \
typename Container, \
typename Tp = detail::copyConst<T, detail::dataType<Container>>> \
constexpr Tp& func(Container&& c) \
{ \
static_assert(std::is_trivially_copyable_v<Tp>); \
static_assert(detail::trivialContainer<Container>); \
static_assert(std::is_same_v<A, Aligned> || \
sizeof(*std::data(c)) % alignof(Tp) == 0); \
const size_t bytes = std::size(c) * sizeof(*std::data(c)); \
if (bytes comp sizeof(Tp)) \
{ \
throw std::runtime_error( \
fmt::format(#func ": {} < {}", bytes, sizeof(Tp))); \
} \
return *reinterpret_cast<Tp*>(std::data(c)); \
}
STDPLUS_REF_FROM(refFrom, <)
STDPLUS_REF_FROM(refFromStrict, !=)
#undef STDPLUS_REF_FROM
/** @brief Extracts data from a buffer into a copyable type
* Updates the data buffer to show that data was removed
*
* @param[in,out] data - The data buffer being extracted from
* @return The copyable type with data populated
*/
template <typename T, typename CharT>
constexpr T extract(std::basic_string_view<CharT>& data)
{
T ret = copyFrom<T>(data);
static_assert(sizeof(T) % sizeof(CharT) == 0);
data.remove_prefix(sizeof(T) / sizeof(CharT));
return ret;
}
template <typename T, typename IntT,
typename = std::enable_if_t<std::is_trivially_copyable_v<IntT>>>
constexpr T extract(std::span<IntT>& data)
{
T ret = copyFrom<T>(data);
static_assert(sizeof(T) % sizeof(IntT) == 0);
data = data.subspan(sizeof(T) / sizeof(IntT));
return ret;
}
/** @brief Extracts data from a buffer as a reference if aligned
* Updates the data buffer to show that data was removed
*
* @param[in,out] data - The data buffer being extracted from
* @return A reference to the data
*/
template <typename T, typename A = stdplus::raw::UnAligned, typename CharT>
constexpr const T& extractRef(std::basic_string_view<CharT>& data)
{
const T& ret = refFrom<T, A>(data);
static_assert(sizeof(T) % sizeof(CharT) == 0);
data.remove_prefix(sizeof(T) / sizeof(CharT));
return ret;
}
template <typename T, typename A = stdplus::raw::UnAligned, typename IntT,
typename = std::enable_if_t<std::is_trivially_copyable_v<IntT>>,
typename Tp = detail::copyConst<T, IntT>>
constexpr Tp& extractRef(std::span<IntT>& data)
{
Tp& ret = refFrom<Tp, A>(data);
static_assert(sizeof(Tp) % sizeof(IntT) == 0);
data = data.subspan(sizeof(Tp) / sizeof(IntT));
return ret;
}
/** @brief Returns the std::span referencing the data of the raw trivial type
* or of trivial types in a contiguous container.
*
* @param[in] t - The trivial raw data
* @return A view over the input with the given output integral type
*/
template <typename CharT, typename T>
constexpr std::enable_if_t<!detail::hasData<T>, std::basic_string_view<CharT>>
asView(const T& t) noexcept
{
static_assert(std::is_trivially_copyable_v<T>);
static_assert(sizeof(T) % sizeof(CharT) == 0);
return {reinterpret_cast<const CharT*>(&t), sizeof(T) / sizeof(CharT)};
}
template <typename CharT, typename Container>
constexpr std::enable_if_t<detail::hasData<Container>,
std::basic_string_view<CharT>>
asView(const Container& c) noexcept
{
static_assert(detail::trivialContainer<Container>);
static_assert(sizeof(*std::data(c)) % sizeof(CharT) == 0);
return {reinterpret_cast<const CharT*>(std::data(c)),
std::size(c) * sizeof(*std::data(c)) / sizeof(CharT)};
}
template <typename IntT, typename T,
typename = std::enable_if_t<std::is_trivially_copyable_v<IntT>>,
typename = std::enable_if_t<!detail::hasData<T>>,
typename IntTp = detail::copyConst<IntT, T>>
constexpr std::span<IntTp> asSpan(T& t) noexcept
{
static_assert(std::is_trivially_copyable_v<T>);
static_assert(sizeof(T) % sizeof(IntTp) == 0);
return {reinterpret_cast<IntTp*>(&t), sizeof(T) / sizeof(IntTp)};
}
template <typename IntT, typename Container,
typename = std::enable_if_t<std::is_trivially_copyable_v<IntT>>,
typename = std::enable_if_t<detail::hasData<Container>>,
typename IntTp = detail::copyConst<IntT, detail::dataType<Container>>>
constexpr std::span<IntTp> asSpan(Container&& c) noexcept
{
static_assert(detail::trivialContainer<Container>);
static_assert(sizeof(*std::data(c)) % sizeof(IntTp) == 0);
return {reinterpret_cast<IntTp*>(std::data(c)),
std::size(c) * sizeof(*std::data(c)) / sizeof(IntTp)};
}
} // namespace raw
} // namespace stdplus