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
9#ifdef HI_HAS_SSE
10#include <immintrin.h>
11#endif
12#ifdef HI_HAS_SSE2
13#include <emmintrin.h>
14#endif
15#ifdef HI_HAS_SSE4_1
16#include <smmintrin.h>
17#endif
18#if HI_COMPILER == HI_CC_MSVC
19#include <stdlib.h>
20#endif
21#include <bit>
22#include <concepts>
23
24hi_warning_push();
25// C26472: Don't use a static_cast for arithmetic conversions. Use brace initialization, gsl::narrow_cast or gsl::narrow
26// (type.1).
27// static_cast are used to cheaply cast integers to unsigned and back, for byteswapping.
28hi_warning_ignore_msvc(26472);
29
30namespace hi::inline v1 {
31
34template<std::integral T>
35[[nodiscard]] constexpr T little_to_native(T x)
36{
37 if constexpr (std::endian::native == std::endian::little) {
38 return x;
39 } else {
40 return std::byteswap(x);
41 }
42}
43
46template<std::integral T>
47[[nodiscard]] constexpr T big_to_native(T x)
48{
49 if constexpr (std::endian::native == std::endian::big) {
50 return x;
51 } else {
52 return std::byteswap(x);
53 }
54}
55
58template<std::integral T>
59[[nodiscard]] constexpr T native_to_little(T x)
60{
61 if constexpr (std::endian::native == std::endian::little) {
62 return x;
63 } else {
64 return std::byteswap(x);
65 }
66}
67
70template<std::integral T>
71[[nodiscard]] constexpr T native_to_big(T x)
72{
73 if constexpr (std::endian::native == std::endian::big) {
74 return x;
75 } else {
76 return std::byteswap(x);
77 }
78}
79
87template<std::integral T, std::endian Endian = std::endian::native>
88[[nodiscard]] constexpr T load(T const *src) noexcept
89{
90 auto value = *src;
91 if constexpr (Endian != std::endian::native) {
92 value = std::byteswap(value);
93 }
94 return value;
95}
96
104template<std::integral T, std::endian Endian = std::endian::native, byte_like B>
105[[nodiscard]] constexpr T load(B const *src) noexcept
106{
107 auto value = unaligned_load<T>(src);
108 if constexpr (Endian != std::endian::native) {
109 value = std::byteswap(value);
110 }
111 return value;
112}
113
121template<std::integral T, std::endian Endian = std::endian::native>
122[[nodiscard]] inline T load(void const *src) noexcept
123{
124 auto value = unaligned_load<T>(src);
125 if constexpr (Endian != std::endian::native) {
126 value = std::byteswap(value);
127 }
128 return value;
129}
130
137template<std::integral T>
138[[nodiscard]] constexpr T load_le(T const *src) noexcept
139{
140 return load<T, std::endian::little>(src);
141}
142
149template<std::integral T, byte_like B>
150[[nodiscard]] constexpr T load_le(B const *src) noexcept
151{
152 return load<T, std::endian::little>(src);
153}
154
161template<std::integral T>
162[[nodiscard]] inline T load_le(void const *src) noexcept
163{
164 return load<T, std::endian::little>(src);
165}
166
173template<std::integral T>
174[[nodiscard]] constexpr T load_be(T const *src) noexcept
175{
176 return load<T, std::endian::big>(src);
177}
178
185template<std::integral T, byte_like B>
186[[nodiscard]] constexpr T load_be(B const *src) noexcept
187{
188 return load<T, std::endian::big>(src);
189}
190
197template<std::integral T>
198[[nodiscard]] inline T load_be(void const *src) noexcept
199{
200 return load<T, std::endian::big>(src);
201}
202
218template<unsigned int NumBits, byte_like B>
219[[nodiscard]] constexpr auto load_bits_be(B const *src, size_t bit_index) noexcept
220{
221 static_assert(NumBits <= sizeof(unsigned long long) * CHAR_BIT);
222
223 constexpr auto num_bits = NumBits;
224 constexpr auto num_bytes = (num_bits + CHAR_BIT - 1) / CHAR_BIT;
225
226 // Determine an unsigned type that can be used to read NumBits in a single `load_be()` on every bit offset.
227 // clang-format off
228 using value_type =
229 std::conditional_t<num_bytes < sizeof(unsigned short), unsigned short,
230 std::conditional_t<num_bytes < sizeof(unsigned int), unsigned int,
231 std::conditional_t<num_bytes < sizeof(unsigned long), unsigned long, unsigned long long>>>;
232 // clang-format on
233
234 constexpr auto value_bits = sizeof(value_type) * CHAR_BIT;
235
236 hilet byte_offset = bit_index / CHAR_BIT;
237 hilet bit_offset = bit_index % CHAR_BIT;
238
239 // Optimization of reading a byte, aligned to a byte.
240 if constexpr (num_bits == CHAR_BIT) {
241 if (bit_offset == 0) {
242 return char_cast<value_type>(src[byte_offset]);
243 }
244 }
245
246 // load_be allows unaligned reads.
247 auto r = load_be<value_type>(std::addressof(src[byte_offset]));
248
249 // Align to most significant bit. In preparation for loading
250 // one more byte.
251 r <<= bit_offset;
252
253 if constexpr (num_bytes == sizeof(value_type)) {
254 // In this case it is possible we could not read the whole value in one go.
255 // We may need to read one more byte.
256
257 auto bits_done = value_bits - bit_offset;
258 if (bits_done < num_bits) {
259 auto rest = char_cast<value_type>(src[byte_offset + sizeof(value_type)]);
260 rest >>= CHAR_BIT - bit_offset;
261 r |= rest;
262 }
263 }
264
265 // Align number to least significant bit.
266 r >>= value_bits - num_bits;
267 return r;
268}
269
270
271
272template<std::endian Endian = std::endian::native, std::integral T, byte_like B>
273constexpr void store(T value, B const *dst) noexcept
274{
275 if constexpr (Endian != std::endian::native) {
276 value = std::byteswap(value);
277 }
278 unaligned_store<T>(value, dst);
279}
280
281template<std::endian Endian = std::endian::native, std::integral T>
282constexpr void store(T value, void const *dst) noexcept
283{
284 if constexpr (Endian != std::endian::native) {
285 value = std::byteswap(value);
286 }
287 unaligned_store<T>(value, dst);
288}
289
290template<std::integral T, byte_like B>
291constexpr void store_le(T value, B const *dst) noexcept
292{
293 store<std::endian::little>(value, dst);
294}
295
296template<std::integral T>
297inline void store_le(T value, void const *dst) noexcept
298{
299 store<std::endian::little>(value, dst);
300}
301
302template<std::integral T, byte_like B>
303constexpr void store_be(T value, B const *dst) noexcept
304{
305 store<std::endian::big>(value, dst);
306}
307
308template<std::integral T>
309inline void store_be(T value, void const *dst) noexcept
310{
311 store<std::endian::big>(value, dst);
312}
313
314
315template<typename T, std::endian E, std::size_t A = alignof(T)>
317 using value_type = T;
318 constexpr static std::endian endian = E;
319 constexpr static std::size_t alignment = A;
320
321 alignas(A) std::byte _value[sizeof(T)];
322
323 [[nodiscard]] constexpr value_type operator*() const noexcept
324 {
325 return load<value_type, endian>(_value);
326 }
327
328 constexpr endian_buf_t& operator=(value_type x) noexcept
329 {
330 store<endian>(x, _value);
331 return *this;
332 }
333};
334
353
372
373} // namespace hi::inline v1
374
375hi_warning_pop();
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
DOXYGEN BUG.
Definition algorithm.hpp:13
constexpr T big_to_native(T x)
Convert an integral from big-to-native endian.
Definition endian.hpp:47
constexpr T little_to_native(T x)
Convert an integral from little-to-native endian.
Definition endian.hpp:35
constexpr T native_to_big(T x)
Convert an integral from native-to-big endian.
Definition endian.hpp:71
constexpr T load_le(T const *src) noexcept
Load of a numeric value encoded in little-endian format.
Definition endian.hpp:138
constexpr T native_to_little(T x)
Convert an integral from native-to-little endian.
Definition endian.hpp:59
constexpr T load_be(T const *src) noexcept
Load of a numeric value encoded in big-endian format.
Definition endian.hpp:174
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:219
constexpr T load(T const *src) noexcept
Load a numeric value from memory.
Definition endian.hpp:88
Definition endian.hpp:316
T addressof(T... args)