blob: b56fb9840ec30c3330d15517366cba09299b696e [file] [log] [blame]
#pragma once
#include <fmt/format.h>
#include <stdexcept>
#include <stdplus/types.hpp>
#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 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>>> = 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>
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));
return memcmp(&a, &b, sizeof(A)) == 0;
}
/** @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
*/
template <typename T, typename Container>
T copyFrom(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 < sizeof(ret))
{
throw std::runtime_error(
fmt::format("CopyFrom: {} < {}", bytes, sizeof(ret)));
}
std::memcpy(&ret, std::data(c), sizeof(ret));
return ret;
}
/** @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
*/
template <typename T, typename Container,
typename Tp = detail::copyConst<T, detail::dataType<Container>>>
Tp& refFrom(Container&& c)
{
static_assert(std::is_trivially_copyable_v<Tp>);
static_assert(detail::trivialContainer<Container>);
static_assert(sizeof(*std::data(c)) % alignof(Tp) == 0);
const size_t bytes = std::size(c) * sizeof(*std::data(c));
if (bytes < sizeof(Tp))
{
throw std::runtime_error(
fmt::format("RefFrom: {} < {}", bytes, sizeof(Tp)));
}
return *reinterpret_cast<Tp*>(std::data(c));
}
/** @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>
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;
}
#ifdef STDPLUS_SPAN_TYPE
template <typename T, typename IntT,
typename = std::enable_if_t<std::is_trivially_copyable_v<IntT>>>
T extract(span<IntT>& data)
{
T ret = copyFrom<T>(data);
static_assert(sizeof(T) % sizeof(IntT) == 0);
data = data.subspan(sizeof(T) / sizeof(IntT));
return ret;
}
#endif
/** @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 CharT>
const T& extractRef(std::basic_string_view<CharT>& data)
{
const T& ret = refFrom<T>(data);
static_assert(sizeof(T) % sizeof(CharT) == 0);
data.remove_prefix(sizeof(T) / sizeof(CharT));
return ret;
}
#ifdef STDPLUS_SPAN_TYPE
template <typename T, typename IntT,
typename = std::enable_if_t<std::is_trivially_copyable_v<IntT>>,
typename Tp = detail::copyConst<T, IntT>>
Tp& extractRef(span<IntT>& data)
{
Tp& ret = refFrom<Tp>(data);
static_assert(sizeof(Tp) % sizeof(IntT) == 0);
data = data.subspan(sizeof(Tp) / sizeof(IntT));
return ret;
}
#endif
/** @brief Returns the 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>
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>
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)};
}
#ifdef STDPLUS_SPAN_TYPE
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>>
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>>>
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)};
}
#endif
} // namespace raw
} // namespace stdplus