/src/stdplus/include/stdplus/raw.hpp
Line | Count | Source |
1 | | #pragma once |
2 | | #include <stdplus/concepts.hpp> |
3 | | #include <stdplus/exception.hpp> |
4 | | |
5 | | #include <algorithm> |
6 | | #include <format> |
7 | | #include <span> |
8 | | #include <stdexcept> |
9 | | #include <string_view> |
10 | | #include <type_traits> |
11 | | |
12 | | namespace stdplus |
13 | | { |
14 | | namespace raw |
15 | | { |
16 | | |
17 | | namespace detail |
18 | | { |
19 | | |
20 | | /** @brief Gets the datatype referenced in a container |
21 | | */ |
22 | | template <typename Container> |
23 | | using dataType = std::remove_pointer_t<decltype(std::data( |
24 | | std::declval<std::add_lvalue_reference_t<Container>>()))>; |
25 | | |
26 | | /** @brief Gets the sizetype referenced in a container |
27 | | */ |
28 | | template <typename Container> |
29 | | using sizeType = |
30 | | decltype(std::size(std::declval<std::add_lvalue_reference_t<Container>>())); |
31 | | |
32 | | /** @brief Determines if the container holds trivially copyable data |
33 | | */ |
34 | | template <typename Container> |
35 | | inline constexpr bool containsTrivial = |
36 | | std::is_trivially_copyable_v<dataType<Container>>; |
37 | | |
38 | | /** @brief Adds const to A if B is const |
39 | | */ |
40 | | template <typename A, typename B> |
41 | | using copyConst = |
42 | | std::conditional_t<std::is_const_v<B>, std::add_const_t<A>, A>; |
43 | | |
44 | | /** @brief Determines if a type is a container of data |
45 | | */ |
46 | | template <typename, typename = void> |
47 | | inline constexpr bool hasData = false; |
48 | | template <typename T> |
49 | | inline constexpr bool hasData<T, std::void_t<dataType<T>, sizeType<T>>> = true; |
50 | | |
51 | | template <typename T> |
52 | | concept ContainsTrivial = hasData<T> && containsTrivial<T>; |
53 | | template <typename T> |
54 | | concept NotContainerTrivial = !hasData<T> && TriviallyCopyable<T>; |
55 | | |
56 | | } // namespace detail |
57 | | |
58 | | /** @brief Compares two containers to see if their raw bytes are equal |
59 | | * |
60 | | * @param[in] a - The first container |
61 | | * @param[in] b - The second container |
62 | | * @return True if they are the same, false otherwise |
63 | | */ |
64 | | template <TriviallyCopyable A, TriviallyCopyable B> |
65 | | constexpr bool equal(const A& a, const B& b) noexcept |
66 | | { |
67 | | static_assert(sizeof(A) == sizeof(B)); |
68 | | const auto a_byte = reinterpret_cast<const std::byte*>(&a); |
69 | | const auto b_byte = reinterpret_cast<const std::byte*>(&b); |
70 | | return std::equal(a_byte, a_byte + sizeof(A), b_byte); |
71 | | } |
72 | | |
73 | | /** @brief Copies data from a buffer into a copyable type |
74 | | * |
75 | | * @param[in] data - The data buffer being copied from |
76 | | * @return The copyable type with data populated |
77 | | */ |
78 | | #define STDPLUS_COPY_FROM(func, comp) \ |
79 | | template <TriviallyCopyable T, detail::ContainsTrivial Container> \ |
80 | | constexpr T func(const Container& c) \ |
81 | 23.3M | { \ |
82 | 23.3M | T ret; \ |
83 | 23.3M | const size_t bytes = std::size(c) * sizeof(*std::data(c)); \ |
84 | 23.3M | if (bytes comp sizeof(ret)) \ |
85 | 23.3M | { \ |
86 | 1.34k | throw exception::Incomplete( \ |
87 | 1.34k | std::format(#func ": {} < {}", bytes, sizeof(ret))); \ |
88 | 1.34k | } \ |
89 | 23.3M | const auto c_bytes = reinterpret_cast<const std::byte*>(std::data(c)); \ |
90 | 23.3M | const auto ret_bytes = reinterpret_cast<std::byte*>(&ret); \ |
91 | 23.3M | std::copy(c_bytes, c_bytes + sizeof(ret), ret_bytes); \ |
92 | 23.3M | return ret; \ |
93 | 23.3M | } _ZN7stdplus3raw8copyFromITkNS_17TriviallyCopyableE21payload_update_statusTkNS0_6detail15ContainsTrivialENSt3__14spanIKhLm18446744073709551615EEEEET_RKT0_ Line | Count | Source | 81 | 603 | { \ | 82 | 603 | T ret; \ | 83 | 603 | const size_t bytes = std::size(c) * sizeof(*std::data(c)); \ | 84 | 603 | if (bytes comp sizeof(ret)) \ | 85 | 603 | { \ | 86 | 603 | throw exception::Incomplete( \ | 87 | 603 | std::format(#func ": {} < {}", bytes, sizeof(ret))); \ | 88 | 603 | } \ | 89 | 603 | const auto c_bytes = reinterpret_cast<const std::byte*>(std::data(c)); \ | 90 | 0 | const auto ret_bytes = reinterpret_cast<std::byte*>(&ret); \ | 91 | 0 | std::copy(c_bytes, c_bytes + sizeof(ret), ret_bytes); \ | 92 | 0 | return ret; \ | 93 | 603 | } |
_ZN7stdplus3raw8copyFromITkNS_17TriviallyCopyableEN6google4hoth8internal9RspHeaderETkNS0_6detail15ContainsTrivialENSt3__14spanIKhLm18446744073709551615EEEEET_RKT0_ Line | Count | Source | 81 | 23.3M | { \ | 82 | 23.3M | T ret; \ | 83 | 23.3M | const size_t bytes = std::size(c) * sizeof(*std::data(c)); \ | 84 | 23.3M | if (bytes comp sizeof(ret)) \ | 85 | 23.3M | { \ | 86 | 0 | throw exception::Incomplete( \ | 87 | 0 | std::format(#func ": {} < {}", bytes, sizeof(ret))); \ | 88 | 0 | } \ | 89 | 23.3M | const auto c_bytes = reinterpret_cast<const std::byte*>(std::data(c)); \ | 90 | 23.3M | const auto ret_bytes = reinterpret_cast<std::byte*>(&ret); \ | 91 | 23.3M | std::copy(c_bytes, c_bytes + sizeof(ret), ret_bytes); \ | 92 | 23.3M | return ret; \ | 93 | 23.3M | } |
_ZN7stdplus3raw8copyFromITkNS_17TriviallyCopyableE31payload_update_confirm_responseTkNS0_6detail15ContainsTrivialENSt3__14spanIKhLm18446744073709551615EEEEET_RKT0_ Line | Count | Source | 81 | 737 | { \ | 82 | 737 | T ret; \ | 83 | 737 | const size_t bytes = std::size(c) * sizeof(*std::data(c)); \ | 84 | 737 | if (bytes comp sizeof(ret)) \ | 85 | 737 | { \ | 86 | 737 | throw exception::Incomplete( \ | 87 | 737 | std::format(#func ": {} < {}", bytes, sizeof(ret))); \ | 88 | 737 | } \ | 89 | 737 | const auto c_bytes = reinterpret_cast<const std::byte*>(std::data(c)); \ | 90 | 0 | const auto ret_bytes = reinterpret_cast<std::byte*>(&ret); \ | 91 | 0 | std::copy(c_bytes, c_bytes + sizeof(ret), ret_bytes); \ | 92 | 0 | return ret; \ | 93 | 737 | } |
|
94 | | STDPLUS_COPY_FROM(copyFrom, <) |
95 | | STDPLUS_COPY_FROM(copyFromStrict, !=) |
96 | | #undef STDPLUS_COPY_FROM |
97 | | |
98 | | /** @brief If you can guarantee the underlying data is properly aligned |
99 | | * for raw struct access this specifier is used to override compile checks. */ |
100 | | struct Aligned |
101 | | {}; |
102 | | struct UnAligned |
103 | | {}; |
104 | | |
105 | | /** @brief References the data from a buffer if aligned |
106 | | * |
107 | | * @param[in] data - The data buffer being referenced |
108 | | * @return The reference to the data in the new type |
109 | | */ |
110 | | #define STDPLUS_REF_FROM(func, comp) \ |
111 | | template <TriviallyCopyable T, typename A = stdplus::raw::UnAligned, \ |
112 | | detail::ContainsTrivial Container, \ |
113 | | typename Tp = detail::copyConst<T, detail::dataType<Container>>> \ |
114 | | constexpr Tp& func(Container&& c) \ |
115 | 515 | { \ |
116 | 515 | static_assert(std::is_same_v<A, Aligned> || \ |
117 | 515 | sizeof(*std::data(c)) % alignof(Tp) == 0); \ |
118 | 515 | const size_t bytes = std::size(c) * sizeof(*std::data(c)); \ |
119 | 515 | if (bytes comp sizeof(Tp)) \ |
120 | 515 | { \ |
121 | 0 | throw exception::Incomplete( \ |
122 | 0 | std::format(#func ": {} < {}", bytes, sizeof(Tp))); \ |
123 | 0 | } \ |
124 | 515 | return *reinterpret_cast<Tp*>(std::data(c)); \ |
125 | 515 | } |
126 | | STDPLUS_REF_FROM(refFrom, <) |
127 | | STDPLUS_REF_FROM(refFromStrict, !=) |
128 | | #undef STDPLUS_REF_FROM |
129 | | |
130 | | /** @brief Extracts data from a buffer into a copyable type |
131 | | * Updates the data buffer to show that data was removed |
132 | | * |
133 | | * @param[in,out] data - The data buffer being extracted from |
134 | | * @return The copyable type with data populated |
135 | | */ |
136 | | template <typename T, typename CharT> |
137 | | constexpr T extract(std::basic_string_view<CharT>& data) |
138 | | { |
139 | | T ret = copyFrom<T>(data); |
140 | | static_assert(sizeof(T) % sizeof(CharT) == 0); |
141 | | data.remove_prefix(sizeof(T) / sizeof(CharT)); |
142 | | return ret; |
143 | | } |
144 | | template <typename T, TriviallyCopyable IntT> |
145 | | constexpr T extract(std::span<IntT>& data) |
146 | 23.3M | { |
147 | 23.3M | T ret = copyFrom<T>(data); |
148 | 23.3M | static_assert(sizeof(T) % sizeof(IntT) == 0); |
149 | 23.3M | data = data.subspan(sizeof(T) / sizeof(IntT)); |
150 | 23.3M | return ret; |
151 | 23.3M | } |
152 | | |
153 | | /** @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 | | */ |
159 | | template <typename T, typename A = stdplus::raw::UnAligned, typename CharT> |
160 | | constexpr const T& extractRef(std::basic_string_view<CharT>& data) |
161 | | { |
162 | | const T& ret = refFrom<T, A>(data); |
163 | | static_assert(sizeof(T) % sizeof(CharT) == 0); |
164 | | data.remove_prefix(sizeof(T) / sizeof(CharT)); |
165 | | return ret; |
166 | | } |
167 | | template <typename T, typename A = stdplus::raw::UnAligned, |
168 | | TriviallyCopyable IntT, typename Tp = detail::copyConst<T, IntT>> |
169 | | constexpr Tp& extractRef(std::span<IntT>& data) |
170 | 515 | { |
171 | 515 | Tp& ret = refFrom<Tp, A>(data); |
172 | 515 | static_assert(sizeof(Tp) % sizeof(IntT) == 0); |
173 | 515 | data = data.subspan(sizeof(Tp) / sizeof(IntT)); |
174 | 515 | return ret; // NOLINT(clang-analyzer-cplusplus.InnerPointer) |
175 | 515 | } |
176 | | |
177 | | /** @brief Returns the std::span referencing the data of the raw trivial type |
178 | | * or of trivial types in a contiguous container. |
179 | | * |
180 | | * @param[in] t - The trivial raw data |
181 | | * @return A view over the input with the given output integral type |
182 | | */ |
183 | | template <typename CharT, detail::NotContainerTrivial T> |
184 | | constexpr std::basic_string_view<CharT> asView(const T& t) noexcept |
185 | | { |
186 | | static_assert(sizeof(T) % sizeof(CharT) == 0); |
187 | | return {reinterpret_cast<const CharT*>(&t), sizeof(T) / sizeof(CharT)}; |
188 | | } |
189 | | |
190 | | template <typename CharT, detail::ContainsTrivial Container> |
191 | | constexpr std::basic_string_view<CharT> asView(const Container& c) noexcept |
192 | | { |
193 | | static_assert(sizeof(*std::data(c)) % sizeof(CharT) == 0); |
194 | | return {reinterpret_cast<const CharT*>(std::data(c)), |
195 | | std::size(c) * sizeof(*std::data(c)) / sizeof(CharT)}; |
196 | | } |
197 | | |
198 | | template <TriviallyCopyable IntT, detail::NotContainerTrivial T, |
199 | | typename IntTp = detail::copyConst<IntT, T>> |
200 | | constexpr std::span<IntTp> asSpan(T& t) noexcept |
201 | | { |
202 | | static_assert(sizeof(T) % sizeof(IntTp) == 0); |
203 | | return {reinterpret_cast<IntTp*>(&t), sizeof(T) / sizeof(IntTp)}; |
204 | | } |
205 | | template <TriviallyCopyable IntT, detail::ContainsTrivial Container, |
206 | | typename IntTp = detail::copyConst<IntT, detail::dataType<Container>>> |
207 | | constexpr std::span<IntTp> asSpan(Container&& c) noexcept |
208 | | { |
209 | | static_assert(sizeof(*std::data(c)) % sizeof(IntTp) == 0); |
210 | | return {reinterpret_cast<IntTp*>(std::data(c)), |
211 | | std::size(c) * sizeof(*std::data(c)) / sizeof(IntTp)}; |
212 | | } |
213 | | |
214 | | } // namespace raw |
215 | | } // namespace stdplus |