HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
endian.hpp
1// Copyright Take Vos 2019-2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "memory.hpp"
8#include "reflection.hpp"
9#include "cast.hpp"
10#include "../macros.hpp"
11#include <bit>
12#include <concepts>
13
14hi_export_module(hikogui.utility.endian);
15
16hi_warning_push();
17// C26472: Don't use a static_cast for arithmetic conversions. Use brace initialization, gsl::narrow_cast or gsl::narrow
18// (type.1).
19// static_cast are used to cheaply cast integers to unsigned and back, for byteswapping.
20hi_warning_ignore_msvc(26472);
21
22hi_export namespace hi { inline namespace v1 {
23
26template<std::integral T>
27[[nodiscard]] constexpr T little_to_native(T x)
28{
29 if constexpr (std::endian::native == std::endian::little) {
30 return x;
31 } else {
32 return std::byteswap(x);
33 }
34}
35
38template<std::integral T>
39[[nodiscard]] constexpr T big_to_native(T x)
40{
41 if constexpr (std::endian::native == std::endian::big) {
42 return x;
43 } else {
44 return std::byteswap(x);
45 }
46}
47
50template<std::integral T>
51[[nodiscard]] constexpr T native_to_little(T x)
52{
53 if constexpr (std::endian::native == std::endian::little) {
54 return x;
55 } else {
56 return std::byteswap(x);
57 }
58}
59
62template<std::integral T>
63[[nodiscard]] constexpr T native_to_big(T x)
64{
65 if constexpr (std::endian::native == std::endian::big) {
66 return x;
67 } else {
68 return std::byteswap(x);
69 }
70}
71
79template<std::integral Out, std::endian Endian = std::endian::native, typename In>
80[[nodiscard]] constexpr Out load(In const *src) noexcept
81{
82 if constexpr (Endian != std::endian::native) {
83 return std::byteswap(unaligned_load<Out>(src));
84 } else {
85 return unaligned_load<Out>(src);
86 }
87}
88
96template<std::integral T, std::endian Endian = std::endian::native>
97[[nodiscard]] inline T load(void const *src) noexcept
98{
99 auto value = unaligned_load<T>(src);
100 if constexpr (Endian != std::endian::native) {
101 value = std::byteswap(value);
102 }
103 return value;
104}
105
112template<std::integral T>
113[[nodiscard]] constexpr T load_le(T const *src) noexcept
114{
115 return load<T, std::endian::little>(src);
116}
117
124template<std::integral T, byte_like B>
125[[nodiscard]] constexpr T load_le(B const *src) noexcept
126{
127 return load<T, std::endian::little>(src);
128}
129
136template<std::integral T>
137[[nodiscard]] inline T load_le(void const *src) noexcept
138{
139 return load<T, std::endian::little>(src);
140}
141
148template<std::integral T>
149[[nodiscard]] constexpr T load_be(T const *src) noexcept
150{
151 return load<T, std::endian::big>(src);
152}
153
160template<std::integral T, byte_like B>
161[[nodiscard]] constexpr T load_be(B const *src) noexcept
162{
163 return load<T, std::endian::big>(src);
164}
165
172template<std::integral T>
173[[nodiscard]] inline T load_be(void const *src) noexcept
174{
175 return load<T, std::endian::big>(src);
176}
177
193template<unsigned int NumBits, byte_like B>
194[[nodiscard]] constexpr auto load_bits_be(B const *src, size_t bit_index) noexcept
195{
196 static_assert(NumBits <= sizeof(unsigned long long) * CHAR_BIT);
197
198 constexpr auto num_bits = NumBits;
199 constexpr auto num_bytes = (num_bits + CHAR_BIT - 1) / CHAR_BIT;
200
201 // Determine an unsigned type that can be used to read NumBits in a single `load_be()` on every bit offset.
202 // clang-format off
203 using value_type =
204 std::conditional_t<num_bytes < sizeof(unsigned short), unsigned short,
205 std::conditional_t<num_bytes < sizeof(unsigned int), unsigned int,
206 std::conditional_t<num_bytes < sizeof(unsigned long), unsigned long, unsigned long long>>>;
207 // clang-format on
208
209 constexpr auto value_bits = sizeof(value_type) * CHAR_BIT;
210
211 auto const byte_offset = bit_index / CHAR_BIT;
212 auto const bit_offset = bit_index % CHAR_BIT;
213
214 // Optimization of reading a byte, aligned to a byte.
215 if constexpr (num_bits == CHAR_BIT) {
216 if (bit_offset == 0) {
217 return char_cast<value_type>(src[byte_offset]);
218 }
219 }
220
221 // load_be allows unaligned reads.
222 auto r = load_be<value_type>(std::addressof(src[byte_offset]));
223
224 // Align to most significant bit. In preparation for loading
225 // one more byte.
226 r <<= bit_offset;
227
228 if constexpr (num_bytes == sizeof(value_type)) {
229 // In this case it is possible we could not read the whole value in one go.
230 // We may need to read one more byte.
231
232 auto bits_done = value_bits - bit_offset;
233 if (bits_done < num_bits) {
234 auto rest = char_cast<value_type>(src[byte_offset + sizeof(value_type)]);
235 rest >>= CHAR_BIT - bit_offset;
236 r |= rest;
237 }
238 }
239
240 // Align number to least significant bit.
241 r >>= value_bits - num_bits;
242 return r;
243}
244
245template<std::endian Endian = std::endian::native, std::integral T, byte_like B>
246constexpr void store(T value, B const *dst) noexcept
247{
248 if constexpr (Endian != std::endian::native) {
249 value = std::byteswap(value);
250 }
251 unaligned_store<T>(value, dst);
252}
253
254template<std::endian Endian = std::endian::native, std::integral T>
255constexpr void store(T value, void const *dst) noexcept
256{
257 if constexpr (Endian != std::endian::native) {
258 value = std::byteswap(value);
259 }
260 unaligned_store<T>(value, dst);
261}
262
263template<std::integral T, byte_like B>
264constexpr void store_le(T value, B const *dst) noexcept
265{
266 store<std::endian::little>(value, dst);
267}
268
269template<std::integral T>
270inline void store_le(T value, void const *dst) noexcept
271{
272 store<std::endian::little>(value, dst);
273}
274
275template<std::integral T, byte_like B>
276constexpr void store_be(T value, B const *dst) noexcept
277{
278 store<std::endian::big>(value, dst);
279}
280
281template<std::integral T>
282inline void store_be(T value, void const *dst) noexcept
283{
284 store<std::endian::big>(value, dst);
285}
286
287template<typename T, std::endian E, std::size_t A = alignof(T)>
289 using value_type = T;
290 constexpr static std::endian endian = E;
291 constexpr static std::size_t alignment = A;
292
293 alignas(A) std::byte _value[sizeof(T)];
294
295 [[nodiscard]] constexpr value_type operator*() const noexcept
296 {
297 return load<value_type, endian>(_value);
298 }
299
300 constexpr endian_buf_t& operator=(value_type x) noexcept
301 {
302 store<endian>(x, _value);
303 return *this;
304 }
305};
306
308using big_uint32_buf_t = endian_buf_t<uint32_t, std::endian::big, 1>;
309using big_uint16_buf_t = endian_buf_t<uint16_t, std::endian::big, 1>;
325
344
345}} // namespace hi::inline v1
346
347hi_warning_pop();
Functions for casting values between types savely.
The HikoGUI namespace.
Definition array_generic.hpp:20
constexpr T big_to_native(T x)
Convert an integral from big-to-native endian.
Definition endian.hpp:39
constexpr T native_to_little(T x)
Convert an integral from native-to-little endian.
Definition endian.hpp:51
constexpr T load_be(T const *src) noexcept
Load of a numeric value encoded in big-endian format.
Definition endian.hpp:149
constexpr T little_to_native(T x)
Convert an integral from little-to-native endian.
Definition endian.hpp:27
constexpr T load_le(T const *src) noexcept
Load of a numeric value encoded in little-endian format.
Definition endian.hpp:113
constexpr Out load(In const *src) noexcept
Unaligned Load of a numeric value from an array.
Definition endian.hpp:80
constexpr T native_to_big(T x)
Convert an integral from native-to-big endian.
Definition endian.hpp:63
constexpr auto load_bits_be(B const *src, size_t bit_index) noexcept
Unaligned load bits from a big-endian buffer at a bit-offset.
Definition endian.hpp:194
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
Horizontal/Vertical alignment combination.
Definition alignment.hpp:244
Definition endian.hpp:288
T addressof(T... args)