header_utils
Loading...
Searching...
No Matches
bytes.h
1
4
5#pragma once
6
7#include <cstddef>
8#include <string_view>
9#include <array>
10#include <bit>
11#include <memory>
12#include <stdexcept>
13#include <span>
14
15namespace ghassanpl
16{
17 template<typename S, typename D>
18 using copy_const_t = std::conditional_t<std::is_const_v<S>, std::add_const_t<D>, std::remove_const_t<D>>;
19
23
25 template <typename T>
26 concept bytelike = (alignof(T) == alignof(std::byte) && sizeof(T) == sizeof(std::byte) && std::is_trivial_v<T>);
27
28 static_assert(bytelike<char>);
29 static_assert(bytelike<unsigned char>);
30 static_assert(bytelike<uint8_t>);
31 static_assert(bytelike<char8_t>);
32
34 template <typename T>
35 concept bytelike_range = std::ranges::range<T> && bytelike<std::ranges::range_value_t<T>>;
36
38 template <bytelike TO, typename FROM, size_t N = std::dynamic_extent>
39 requires std::is_trivially_copyable_v<FROM>
40 [[nodiscard]] std::span<TO> as_bytelikes(std::span<FROM, N> elements) noexcept
41 {
42 return { reinterpret_cast<TO*>(elements.data()), elements.size() * sizeof(FROM) };
43 }
44
46 template <typename TO, bytelike FROM>
47 requires std::is_trivially_copyable_v<TO>
48 [[nodiscard]] TO from_bytelikes(std::span<FROM const> from)
49 {
51 if (from.size() < sizeof(TO))
52 throw std::invalid_argument("from span must be at least the size of the object");
53 TO result;
54 std::memcpy(std::addressof(result), from.data(), sizeof(TO));
55 return result;
56 }
57
59 template <typename TO, bytelike FROM>
60 requires std::is_trivially_copyable_v<TO>
61 [[nodiscard]] std::span<TO> span_from_bytelikes(std::span<FROM> from)
62 {
63 const auto size = from.size() / sizeof(TO);
64 return std::span<TO>{ reinterpret_cast<TO*>(from.data()), size };
65 }
66
67
69 template <bytelike TO, typename T>
70 requires std::is_trivially_copyable_v<T>
71 [[nodiscard]] auto as_bytelikes(T& pod) noexcept
72 {
73 if constexpr (std::is_const_v<T>)
74 return std::span{ reinterpret_cast<TO const*>(std::addressof(pod)), sizeof(pod) };
75 else
76 return std::span{ reinterpret_cast<TO*>(std::addressof(pod)), sizeof(pod) };
77 }
78
80 template <bytelike FROM> [[nodiscard]] constexpr auto to_u8(FROM byte) noexcept { return std::bit_cast<uint8_t>(byte); }
82 template <bytelike FROM> [[nodiscard]] constexpr auto to_char(FROM byte) noexcept { return std::bit_cast<char>(byte); }
84 template <bytelike FROM> [[nodiscard]] constexpr auto to_byte(FROM byte) noexcept { return std::bit_cast<std::byte>(byte); }
86 template <bytelike FROM> [[nodiscard]] constexpr auto to_uchar(FROM byte) noexcept { return std::bit_cast<unsigned char>(byte); }
88 template <bytelike FROM> [[nodiscard]] constexpr auto to_char8(FROM byte) noexcept { return std::bit_cast<char8_t>(byte); }
89
91 template <bytelike FROM, size_t N = std::dynamic_extent>
92 [[nodiscard]] auto as_chars(std::span<FROM, N> bytes) noexcept { return as_bytelikes<copy_const_t<FROM, char>>(bytes); }
94 template <bytelike FROM, size_t N = std::dynamic_extent>
95 [[nodiscard]] auto as_bytes(std::span<FROM, N> bytes) noexcept { return as_bytelikes<copy_const_t<FROM, std::byte>>(bytes); }
97 template <bytelike FROM, size_t N = std::dynamic_extent>
98 [[nodiscard]] auto as_uchars(std::span<FROM, N> bytes) noexcept { return as_bytelikes<copy_const_t<FROM, unsigned char>>(bytes); }
100 template <bytelike FROM, size_t N = std::dynamic_extent>
101 [[nodiscard]] auto as_u8s(std::span<FROM, N> bytes) noexcept { return as_bytelikes<copy_const_t<FROM, uint8_t>>(bytes); }
103 template <bytelike FROM, size_t N = std::dynamic_extent>
104 [[nodiscard]] auto as_char8s(std::span<FROM, N> bytes) noexcept { return as_bytelikes<copy_const_t<FROM, char8_t>>(bytes); }
105
107 template <typename T> requires std::is_trivial_v<T> [[nodiscard]] auto as_chars(T const& data) noexcept { return as_bytelikes<char>(data); }
109 template <typename T> requires std::is_trivial_v<T> [[nodiscard]] auto as_bytes(T const& data) noexcept { return as_bytelikes<std::byte>(data); }
111 template <typename T> requires std::is_trivial_v<T> [[nodiscard]] auto as_uchars(T const& data) noexcept { return as_bytelikes<unsigned char>(data); }
113 template <typename T> requires std::is_trivial_v<T> [[nodiscard]] auto as_u8s(T const& data) noexcept { return as_bytelikes<uint8_t>(data); }
115 template <typename T> requires std::is_trivial_v<T> [[nodiscard]] auto as_char8s(T const& data) noexcept { return as_bytelikes<char8_t>(data); }
116
118 template <bytelike B, size_t N = std::dynamic_extent>
119 [[nodiscard]] bool nth_bit(std::span<B const, N> range, size_t n) noexcept
120 {
121 if (n >= range.size() * CHAR_BIT)
122 return false;
123 const auto u8range = as_u8s(range);
124 const auto byte = u8range[n / CHAR_BIT];
125 return byte & (1 << (n % CHAR_BIT));
126 }
127
129 template <bytelike B, size_t N = std::dynamic_extent>
130 void set_nth_bit(std::span<B, N> range, size_t n, bool value) noexcept
131 {
132 if (n >= range.size() * CHAR_BIT)
133 return;
134 auto u8range = as_u8s(range);
135 auto byte = u8range[n / CHAR_BIT];
136 if (value)
137 byte |= (1 << (n % CHAR_BIT));
138 else
139 byte &= ~(1 << (n % CHAR_BIT));
140 }
141
143 template <typename T>
144 requires std::is_trivially_copyable_v<T>
145 [[nodiscard]] bool nth_bit(T const& pod, size_t n) noexcept
146 {
147 return nth_bit(as_u8s(pod), n);
148 }
149
151 template <typename T>
152 requires std::is_trivially_copyable_v<T>
153 void set_nth_bit(T& pod, size_t n, bool value) noexcept
154 {
155 set_nth_bit(as_u8s(pod), n, value);
156 }
157
159 template <size_t N, bytelike B, size_t SN = std::dynamic_extent>
160 [[nodiscard]] bool nth_bit(std::span<B const, SN> range) noexcept
161 {
162 if (N >= range.size() * CHAR_BIT)
163 return false;
164 const auto u8range = as_u8s(range);
165 const auto byte = u8range[N / CHAR_BIT];
166 return byte & (1 << (N % CHAR_BIT));
167 }
168
170 template <size_t N, bytelike B, size_t SN = std::dynamic_extent>
171 void set_nth_bit(std::span<B, SN> range, bool value) noexcept
172 {
173 if (N >= range.size() * CHAR_BIT)
174 return;
175 auto u8range = as_u8s(range);
176 auto byte = u8range[N / CHAR_BIT];
177 if (value)
178 byte |= (1 << (N % CHAR_BIT));
179 else
180 byte &= ~(1 << (N % CHAR_BIT));
181 }
182
184 template <size_t N, typename T>
185 requires std::is_trivially_copyable_v<T>
186 [[nodiscard]] bool nth_bit(T const& pod) noexcept
187 {
188 return nth_bit<N>(as_u8s(pod));
189 }
190
192 template <size_t N, typename T>
193 requires std::is_trivially_copyable_v<T>
194 void set_nth_bit(T& pod, bool value) noexcept
195 {
196 set_nth_bit<N>(as_u8s(pod), value);
197 }
198
201 template <bytelike B, std::integral T>
202 [[nodiscard]] constexpr auto to_bytelike_array(T value_)
203 {
204 auto value = std::bit_cast<std::make_unsigned_t<T>>(value_);
205 std::array<B, sizeof(T)> result;
206 for (size_t i = 0; i < sizeof(T); ++i)
207 {
208 result[i] = static_cast<B>(value & 0xFF);
209 value >>= 8;
210 }
211 return result;
212 }
213
214 template <std::integral T> constexpr auto to_u8_array(T value) { return to_bytelike_array<uint8_t>(value); }
215
216 template <bytelike T>
217 struct align_front_to_result
218 {
219 std::span<T> prefix;
220 std::span<T> aligned;
221 };
222
224 template <size_t ALIGN, bytelike T, size_t N = std::dynamic_extent>
225 [[nodiscard]] auto align_front_to(std::span<T, N> bytes) noexcept -> align_front_to_result<T>
226 {
227 static_assert(ALIGN >= 1, "Alignment must be greater or equal to 1");
228 if constexpr (ALIGN == 1)
229 {
230 return bytes;
231 }
232 else
233 {
234 void* ptr = const_cast<std::remove_const_t<T>*>(bytes.data());
235 size_t size = bytes.size();
236 ptr = std::align(ALIGN, ALIGN, ptr, size);
237 if (!ptr)
238 return { bytes, {} };
239
240 return { { bytes.data(), bytes.size() - size}, { const_cast<T*>(reinterpret_cast<std::remove_const_t<T>*>(ptr)), size }};
241 }
242 }
243
244 template <bytelike T>
245 struct align_back_to_result
246 {
247 std::span<T> aligned;
248 std::span<T> suffix;
249 };
250
253 template <size_t ALIGN, bytelike T, size_t N = std::dynamic_extent>
254 [[nodiscard]] constexpr auto align_back_to(std::span<T, N> bytes) noexcept -> align_back_to_result<T>
255 {
256 static_assert(ALIGN >= 1, "Alignment must be greater or equal to 1");
257 if constexpr (ALIGN == 1)
258 {
259 return bytes;
260 }
261 else
262 {
263 const auto size = bytes.size();
264
265 if (size < ALIGN)
266 return { {}, bytes };
267
268 const auto aligned_size = size - (size % ALIGN);
269 return { bytes.subspan(0, aligned_size), bytes.subspan(aligned_size, size % ALIGN) };
270 }
271 }
272
273 template <bytelike T, typename MIDDLE = T>
274 struct align_to_result
275 {
276 std::span<T> prefix;
277 std::span<MIDDLE> aligned;
278 std::span<T> suffix;
279 };
280
283 template <size_t ALIGN, bytelike T, size_t N = std::dynamic_extent>
284 [[nodiscard]] auto align_to(std::span<T, N> bytes) noexcept -> align_to_result<T>
285 {
286 static_assert(ALIGN >= 1, "Alignment must be greater or equal to 1");
287 if constexpr (ALIGN == 1)
288 {
289 return bytes;
290 }
291 else
292 {
293 void* ptr = const_cast<std::remove_const_t<T>*>(bytes.data());
294 size_t size = bytes.size();
295 ptr = std::align(ALIGN, ALIGN, ptr, size);
296 if (!ptr)
297 return { bytes, {}, {} };
298
299 const auto aligned_size = size - (size % ALIGN);
300 return {
301 { bytes.data(), bytes.size() - size},
302 { const_cast<T*>(reinterpret_cast<std::remove_const_t<T>*>(ptr)), aligned_size },
303 { const_cast<T*>(reinterpret_cast<std::remove_const_t<T>*>(ptr)) + aligned_size, size % ALIGN }
304 };
305 }
306 }
307
310 template <typename TO, bytelike B, size_t N = std::dynamic_extent>
311 [[nodiscard]] auto aligned_span_cast(std::span<B, N> bytes) noexcept -> align_to_result<B, TO>
312 {
313 const auto [prefix, aligned_original, suffix] = align_to<std::max(alignof(TO), sizeof(TO))>(bytes);
314 const std::span<TO> aligned {
315 reinterpret_cast<TO*>(aligned_original.data()),
316 reinterpret_cast<TO*>(aligned_original.data() + aligned_original.size())
317 };
318 return { prefix, aligned, suffix };
319 }
320
322 template <typename TO, bytelike B, size_t N = std::dynamic_extent>
323 [[nodiscard]] auto aligned_span_cast_and_construct(std::span<B, N> bytes) noexcept -> align_to_result<B, TO>
324 {
325 const auto result = aligned_span_cast<TO>(bytes);
326 std::uninitialized_default_construct_n(result.aligned.data(), result.aligned.size());
327 return result;
328 }
329
331
333}
A range of bytelikes.
Definition bytes.h:35
Represents any trivially copyable type that is trivially castable to an internal object representatio...
Definition bytes.h:26
std::span< TO > as_bytelikes(std::span< FROM, N > elements) noexcept
Converts a span of trivial values to a span of bytelike s.
Definition bytes.h:40
constexpr auto to_u8(FROM byte) noexcept
Bit-casts the given bytelike to a uint8_t.
Definition bytes.h:80
std::span< TO > span_from_bytelikes(std::span< FROM > from)
Returns an object whose internal representation is initialized from the argument.
Definition bytes.h:61
void set_nth_bit(std::span< B, N > range, size_t n, bool value) noexcept
Sets the value of the nth bit in the range of bytelikes.
Definition bytes.h:130
auto aligned_span_cast_and_construct(std::span< B, N > bytes) noexcept -> align_to_result< B, TO >
Does the same as aligned_span_cast but then default-constructs the elements of the of middle span.
Definition bytes.h:323
constexpr auto to_char8(FROM byte) noexcept
Bit-casts the given bytelike to a char8_t.
Definition bytes.h:88
constexpr auto to_bytelike_array(T value_)
A constexpr function that converts an integral value to its constituent bytelikes TODO: This is NOT l...
Definition bytes.h:202
constexpr auto to_char(FROM byte) noexcept
Bit-casts the given bytelike to a char.
Definition bytes.h:82
auto align_front_to(std::span< T, N > bytes) noexcept -> align_front_to_result< T >
Splits the argument span into two spans, the first one being the prefix, the second one having its da...
Definition bytes.h:225
auto as_u8s(std::span< FROM, N > bytes) noexcept
Returns a span of uint8_ts that represents the bytelikes in bytes.
Definition bytes.h:101
auto as_chars(std::span< FROM, N > bytes) noexcept
Returns a span of chars that represents the bytelikes in bytes.
Definition bytes.h:92
TO from_bytelikes(std::span< FROM const > from)
Returns an object whose internal representation is initialized from the argument.
Definition bytes.h:48
constexpr auto to_byte(FROM byte) noexcept
Bit-casts the given bytelike to a std::byte.
Definition bytes.h:84
auto as_char8s(std::span< FROM, N > bytes) noexcept
Returns a span of char8_ts that represents the bytelikes in bytes.
Definition bytes.h:104
auto aligned_span_cast(std::span< B, N > bytes) noexcept -> align_to_result< B, TO >
Splits the argument span into three spans, the first one being the prefix, the second one capable of ...
Definition bytes.h:311
bool nth_bit(std::span< B const, N > range, size_t n) noexcept
Returns the value of the nth bit in the range of bytelikes.
Definition bytes.h:119
auto as_bytes(std::span< FROM, N > bytes) noexcept
Returns a span of std::bytes that represents the bytelikes in bytes.
Definition bytes.h:95
auto align_to(std::span< T, N > bytes) noexcept -> align_to_result< T >
Splits the argument span into three spans, the first one being the prefix, the second one having its ...
Definition bytes.h:284
auto as_uchars(std::span< FROM, N > bytes) noexcept
Returns a span of unsigned chars that represents the bytelikes in bytes.
Definition bytes.h:98
constexpr auto align_back_to(std::span< T, N > bytes) noexcept -> align_back_to_result< T >
Splits the argument span into two spans, the first one having its size aligned to the given alignment...
Definition bytes.h:254
constexpr auto to_uchar(FROM byte) noexcept
Bit-casts the given bytelike to a unsigned char.
Definition bytes.h:86
Primary namespace for everything in this library.
Definition align+rec2.h:10