blob: 21dc7282bd90837776ad9c0d6195c8edd8cb44b3 [file] [log] [blame]
William A. Kennington IIIe0990382019-10-18 02:10:25 -07001#pragma once
2#include <fmt/format.h>
Patrick Williamsd1984dd2023-05-10 16:12:44 -05003
4#include <algorithm>
Patrick Williams68975b92022-04-27 16:00:26 -05005#include <span>
William A. Kennington IIIe0990382019-10-18 02:10:25 -07006#include <stdexcept>
William A. Kennington IIIe0990382019-10-18 02:10:25 -07007#include <string_view>
8#include <type_traits>
9
10namespace stdplus
11{
12namespace raw
13{
14
15namespace detail
16{
17
William A. Kennington III0431e3c2020-07-19 02:08:26 -070018/** @brief Gets the datatype referenced in a container
19 */
20template <typename Container>
Patrick Williams17759262021-09-10 11:46:02 -050021using dataType = std::remove_pointer_t<decltype(std::data(
22 std::declval<std::add_lvalue_reference_t<Container>>()))>;
William A. Kennington III0431e3c2020-07-19 02:08:26 -070023
William A. Kennington IIIa9cf86f2021-08-03 11:42:11 -070024/** @brief Gets the sizetype referenced in a container
25 */
26template <typename Container>
27using sizeType =
28 decltype(std::size(std::declval<std::add_lvalue_reference_t<Container>>()));
29
William A. Kennington IIIe0990382019-10-18 02:10:25 -070030/** @brief Determines if the container holds trivially copyable data
31 */
32template <typename Container>
33inline constexpr bool trivialContainer =
William A. Kennington III0431e3c2020-07-19 02:08:26 -070034 std::is_trivially_copyable_v<dataType<Container>>;
35
36/** @brief Adds const to A if B is const
37 */
38template <typename A, typename B>
39using copyConst =
40 std::conditional_t<std::is_const_v<B>, std::add_const_t<A>, A>;
William A. Kennington IIIe0990382019-10-18 02:10:25 -070041
William A. Kennington IIIba7d7542020-07-19 20:04:44 -070042/** @brief Determines if a type is a container of data
43 */
44template <typename, typename = void>
45inline constexpr bool hasData = false;
46template <typename T>
William A. Kennington IIIa9cf86f2021-08-03 11:42:11 -070047inline constexpr bool hasData<T, std::void_t<dataType<T>, sizeType<T>>> = true;
William A. Kennington IIIba7d7542020-07-19 20:04:44 -070048
William A. Kennington IIIe0990382019-10-18 02:10:25 -070049} // namespace detail
50
William A. Kennington III3b35fcf2020-06-13 18:42:39 -070051/** @brief Compares two containers to see if their raw bytes are equal
52 *
53 * @param[in] a - The first container
54 * @param[in] b - The second container
55 * @return True if they are the same, false otherwise
56 */
57template <typename A, typename B>
William A. Kennington III1bdf3c82022-11-20 16:36:31 -080058constexpr bool equal(const A& a, const B& b) noexcept
William A. Kennington III3b35fcf2020-06-13 18:42:39 -070059{
60 static_assert(std::is_trivially_copyable_v<A>);
61 static_assert(std::is_trivially_copyable_v<B>);
62 static_assert(sizeof(A) == sizeof(B));
William A. Kennington III6c819a72022-10-11 12:43:41 -070063 const auto a_byte = reinterpret_cast<const std::byte*>(&a);
64 const auto b_byte = reinterpret_cast<const std::byte*>(&b);
65 return std::equal(a_byte, a_byte + sizeof(A), b_byte);
William A. Kennington III3b35fcf2020-06-13 18:42:39 -070066}
67
William A. Kennington IIIe0990382019-10-18 02:10:25 -070068/** @brief Copies data from a buffer into a copyable type
69 *
70 * @param[in] data - The data buffer being copied from
71 * @return The copyable type with data populated
72 */
William A. Kennington IIIfca0fdf2022-10-11 12:21:29 -070073#define STDPLUS_COPY_FROM(func, comp) \
74 template <typename T, typename Container> \
William A. Kennington III1bdf3c82022-11-20 16:36:31 -080075 constexpr T func(const Container& c) \
William A. Kennington IIIfca0fdf2022-10-11 12:21:29 -070076 { \
77 static_assert(std::is_trivially_copyable_v<T>); \
78 static_assert(detail::trivialContainer<Container>); \
79 T ret; \
80 const size_t bytes = std::size(c) * sizeof(*std::data(c)); \
81 if (bytes comp sizeof(ret)) \
82 { \
83 throw std::runtime_error( \
84 fmt::format(#func ": {} < {}", bytes, sizeof(ret))); \
85 } \
William A. Kennington III6c819a72022-10-11 12:43:41 -070086 const auto c_bytes = reinterpret_cast<const std::byte*>(std::data(c)); \
87 const auto ret_bytes = reinterpret_cast<std::byte*>(&ret); \
88 std::copy(c_bytes, c_bytes + sizeof(ret), ret_bytes); \
William A. Kennington IIIfca0fdf2022-10-11 12:21:29 -070089 return ret; \
William A. Kennington IIIe0990382019-10-18 02:10:25 -070090 }
William A. Kennington IIIfca0fdf2022-10-11 12:21:29 -070091STDPLUS_COPY_FROM(copyFrom, <)
92STDPLUS_COPY_FROM(copyFromStrict, !=)
93#undef STDPLUS_COPY_FROM
William A. Kennington IIIe0990382019-10-18 02:10:25 -070094
William A. Kennington III3ad6baf2022-10-26 16:44:56 -070095/** @brief If you can guarantee the underlying data is properly aligned
96 * for raw struct access this specifier is used to override compile checks. */
97struct Aligned
Patrick Williamsd1984dd2023-05-10 16:12:44 -050098{};
William A. Kennington III3ad6baf2022-10-26 16:44:56 -070099struct UnAligned
Patrick Williamsd1984dd2023-05-10 16:12:44 -0500100{};
William A. Kennington III3ad6baf2022-10-26 16:44:56 -0700101
William A. Kennington III5c99ff42020-07-19 02:29:26 -0700102/** @brief References the data from a buffer if aligned
103 *
104 * @param[in] data - The data buffer being referenced
105 * @return The reference to the data in the new type
106 */
William A. Kennington IIIfca0fdf2022-10-11 12:21:29 -0700107#define STDPLUS_REF_FROM(func, comp) \
William A. Kennington III3ad6baf2022-10-26 16:44:56 -0700108 template <typename T, typename A = stdplus::raw::UnAligned, \
109 typename Container, \
William A. Kennington IIIfca0fdf2022-10-11 12:21:29 -0700110 typename Tp = detail::copyConst<T, detail::dataType<Container>>> \
William A. Kennington III1bdf3c82022-11-20 16:36:31 -0800111 constexpr Tp& func(Container&& c) \
William A. Kennington IIIfca0fdf2022-10-11 12:21:29 -0700112 { \
113 static_assert(std::is_trivially_copyable_v<Tp>); \
114 static_assert(detail::trivialContainer<Container>); \
William A. Kennington III3ad6baf2022-10-26 16:44:56 -0700115 static_assert(std::is_same_v<A, Aligned> || \
116 sizeof(*std::data(c)) % alignof(Tp) == 0); \
William A. Kennington IIIfca0fdf2022-10-11 12:21:29 -0700117 const size_t bytes = std::size(c) * sizeof(*std::data(c)); \
118 if (bytes comp sizeof(Tp)) \
119 { \
120 throw std::runtime_error( \
121 fmt::format(#func ": {} < {}", bytes, sizeof(Tp))); \
122 } \
123 return *reinterpret_cast<Tp*>(std::data(c)); \
William A. Kennington III5c99ff42020-07-19 02:29:26 -0700124 }
William A. Kennington IIIfca0fdf2022-10-11 12:21:29 -0700125STDPLUS_REF_FROM(refFrom, <)
126STDPLUS_REF_FROM(refFromStrict, !=)
127#undef STDPLUS_REF_FROM
William A. Kennington III5c99ff42020-07-19 02:29:26 -0700128
William A. Kennington IIIe0990382019-10-18 02:10:25 -0700129/** @brief Extracts data from a buffer into a copyable type
130 * Updates the data buffer to show that data was removed
131 *
132 * @param[in,out] data - The data buffer being extracted from
133 * @return The copyable type with data populated
134 */
135template <typename T, typename CharT>
William A. Kennington III1bdf3c82022-11-20 16:36:31 -0800136constexpr T extract(std::basic_string_view<CharT>& data)
William A. Kennington IIIe0990382019-10-18 02:10:25 -0700137{
138 T ret = copyFrom<T>(data);
139 static_assert(sizeof(T) % sizeof(CharT) == 0);
140 data.remove_prefix(sizeof(T) / sizeof(CharT));
141 return ret;
142}
William A. Kennington IIIe0990382019-10-18 02:10:25 -0700143template <typename T, typename IntT,
William A. Kennington III800e9cc2020-07-19 04:23:53 -0700144 typename = std::enable_if_t<std::is_trivially_copyable_v<IntT>>>
William A. Kennington III1bdf3c82022-11-20 16:36:31 -0800145constexpr T extract(std::span<IntT>& data)
William A. Kennington IIIe0990382019-10-18 02:10:25 -0700146{
147 T ret = copyFrom<T>(data);
148 static_assert(sizeof(T) % sizeof(IntT) == 0);
149 data = data.subspan(sizeof(T) / sizeof(IntT));
150 return ret;
151}
William A. Kennington IIIe0990382019-10-18 02:10:25 -0700152
William A. Kennington III5c99ff42020-07-19 02:29:26 -0700153/** @brief Extracts data from a buffer as a reference if aligned
154 * Updates the data buffer to show that data was removed
155 *
156 * @param[in,out] data - The data buffer being extracted from
157 * @return A reference to the data
158 */
William A. Kennington III3ad6baf2022-10-26 16:44:56 -0700159template <typename T, typename A = stdplus::raw::UnAligned, typename CharT>
William A. Kennington III1bdf3c82022-11-20 16:36:31 -0800160constexpr const T& extractRef(std::basic_string_view<CharT>& data)
William A. Kennington III5c99ff42020-07-19 02:29:26 -0700161{
William A. Kennington III3ad6baf2022-10-26 16:44:56 -0700162 const T& ret = refFrom<T, A>(data);
William A. Kennington III5c99ff42020-07-19 02:29:26 -0700163 static_assert(sizeof(T) % sizeof(CharT) == 0);
164 data.remove_prefix(sizeof(T) / sizeof(CharT));
165 return ret;
166}
William A. Kennington III3ad6baf2022-10-26 16:44:56 -0700167template <typename T, typename A = stdplus::raw::UnAligned, typename IntT,
William A. Kennington III5c99ff42020-07-19 02:29:26 -0700168 typename = std::enable_if_t<std::is_trivially_copyable_v<IntT>>,
169 typename Tp = detail::copyConst<T, IntT>>
William A. Kennington III1bdf3c82022-11-20 16:36:31 -0800170constexpr Tp& extractRef(std::span<IntT>& data)
William A. Kennington III5c99ff42020-07-19 02:29:26 -0700171{
William A. Kennington III3ad6baf2022-10-26 16:44:56 -0700172 Tp& ret = refFrom<Tp, A>(data);
William A. Kennington III5c99ff42020-07-19 02:29:26 -0700173 static_assert(sizeof(Tp) % sizeof(IntT) == 0);
174 data = data.subspan(sizeof(Tp) / sizeof(IntT));
William A. Kennington III49d92692023-06-06 14:02:01 -0700175 return ret; // NOLINT(clang-analyzer-cplusplus.InnerPointer)
William A. Kennington III5c99ff42020-07-19 02:29:26 -0700176}
William A. Kennington III5c99ff42020-07-19 02:29:26 -0700177
Patrick Williams68975b92022-04-27 16:00:26 -0500178/** @brief Returns the std::span referencing the data of the raw trivial type
William A. Kennington IIIe0990382019-10-18 02:10:25 -0700179 * or of trivial types in a contiguous container.
180 *
181 * @param[in] t - The trivial raw data
182 * @return A view over the input with the given output integral type
183 */
William A. Kennington IIIba7d7542020-07-19 20:04:44 -0700184template <typename CharT, typename T>
William A. Kennington III1bdf3c82022-11-20 16:36:31 -0800185constexpr std::enable_if_t<!detail::hasData<T>, std::basic_string_view<CharT>>
William A. Kennington IIIba7d7542020-07-19 20:04:44 -0700186 asView(const T& t) noexcept
William A. Kennington IIIe0990382019-10-18 02:10:25 -0700187{
William A. Kennington IIIba7d7542020-07-19 20:04:44 -0700188 static_assert(std::is_trivially_copyable_v<T>);
William A. Kennington IIIe0990382019-10-18 02:10:25 -0700189 static_assert(sizeof(T) % sizeof(CharT) == 0);
190 return {reinterpret_cast<const CharT*>(&t), sizeof(T) / sizeof(CharT)};
191}
William A. Kennington IIIba7d7542020-07-19 20:04:44 -0700192
193template <typename CharT, typename Container>
William A. Kennington III1bdf3c82022-11-20 16:36:31 -0800194constexpr std::enable_if_t<detail::hasData<Container>,
195 std::basic_string_view<CharT>>
William A. Kennington IIIba7d7542020-07-19 20:04:44 -0700196 asView(const Container& c) noexcept
William A. Kennington IIIe0990382019-10-18 02:10:25 -0700197{
198 static_assert(detail::trivialContainer<Container>);
199 static_assert(sizeof(*std::data(c)) % sizeof(CharT) == 0);
200 return {reinterpret_cast<const CharT*>(std::data(c)),
201 std::size(c) * sizeof(*std::data(c)) / sizeof(CharT)};
202}
William A. Kennington IIIba7d7542020-07-19 20:04:44 -0700203
William A. Kennington IIIe0990382019-10-18 02:10:25 -0700204template <typename IntT, typename T,
William A. Kennington III800e9cc2020-07-19 04:23:53 -0700205 typename = std::enable_if_t<std::is_trivially_copyable_v<IntT>>,
William A. Kennington IIIba7d7542020-07-19 20:04:44 -0700206 typename = std::enable_if_t<!detail::hasData<T>>,
William A. Kennington III0431e3c2020-07-19 02:08:26 -0700207 typename IntTp = detail::copyConst<IntT, T>>
William A. Kennington III1bdf3c82022-11-20 16:36:31 -0800208constexpr std::span<IntTp> asSpan(T& t) noexcept
William A. Kennington IIIe0990382019-10-18 02:10:25 -0700209{
William A. Kennington IIIba7d7542020-07-19 20:04:44 -0700210 static_assert(std::is_trivially_copyable_v<T>);
William A. Kennington III0431e3c2020-07-19 02:08:26 -0700211 static_assert(sizeof(T) % sizeof(IntTp) == 0);
212 return {reinterpret_cast<IntTp*>(&t), sizeof(T) / sizeof(IntTp)};
William A. Kennington IIIe0990382019-10-18 02:10:25 -0700213}
214template <typename IntT, typename Container,
William A. Kennington III800e9cc2020-07-19 04:23:53 -0700215 typename = std::enable_if_t<std::is_trivially_copyable_v<IntT>>,
William A. Kennington IIIba7d7542020-07-19 20:04:44 -0700216 typename = std::enable_if_t<detail::hasData<Container>>,
William A. Kennington III0431e3c2020-07-19 02:08:26 -0700217 typename IntTp = detail::copyConst<IntT, detail::dataType<Container>>>
William A. Kennington III1bdf3c82022-11-20 16:36:31 -0800218constexpr std::span<IntTp> asSpan(Container&& c) noexcept
William A. Kennington IIIe0990382019-10-18 02:10:25 -0700219{
220 static_assert(detail::trivialContainer<Container>);
William A. Kennington III0431e3c2020-07-19 02:08:26 -0700221 static_assert(sizeof(*std::data(c)) % sizeof(IntTp) == 0);
222 return {reinterpret_cast<IntTp*>(std::data(c)),
223 std::size(c) * sizeof(*std::data(c)) / sizeof(IntTp)};
William A. Kennington IIIe0990382019-10-18 02:10:25 -0700224}
William A. Kennington IIIe0990382019-10-18 02:10:25 -0700225
226} // namespace raw
227} // namespace stdplus