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 "../macros.hpp"
10
11#ifdef HI_HAS_SSE
12#include <immintrin.h>
13#endif
14#ifdef HI_HAS_SSE2
15#include <emmintrin.h>
16#endif
17#ifdef HI_HAS_SSE4_1
18#include <smmintrin.h>
19#endif
20#if HI_COMPILER == HI_CC_MSVC
21#include <stdlib.h>
22#endif
23
24#include <bit>
25#include <concepts>
26
27hi_export_module(hikogui.utility.endian);
28
29hi_warning_push();
30// C26472: Don't use a static_cast for arithmetic conversions. Use brace initialization, gsl::narrow_cast or gsl::narrow
31// (type.1).
32// static_cast are used to cheaply cast integers to unsigned and back, for byteswapping.
33hi_warning_ignore_msvc(26472);
34
35hi_export namespace hi { inline namespace v1 {
36
39template<std::integral T>
40[[nodiscard]] constexpr T little_to_native(T x)
41{
42 if constexpr (std::endian::native == std::endian::little) {
43 return x;
44 } else {
45 return std::byteswap(x);
46 }
47}
48
51template<std::integral T>
52[[nodiscard]] constexpr T big_to_native(T x)
53{
54 if constexpr (std::endian::native == std::endian::big) {
55 return x;
56 } else {
57 return std::byteswap(x);
58 }
59}
60
63template<std::integral T>
64[[nodiscard]] constexpr T native_to_little(T x)
65{
66 if constexpr (std::endian::native == std::endian::little) {
67 return x;
68 } else {
69 return std::byteswap(x);
70 }
71}
72
75template<std::integral T>
76[[nodiscard]] constexpr T native_to_big(T x)
77{
78 if constexpr (std::endian::native == std::endian::big) {
79 return x;
80 } else {
81 return std::byteswap(x);
82 }
83}
84
92template<std::integral Out, std::endian Endian = std::endian::native, typename In>
93[[nodiscard]] constexpr Out load(In const *src) noexcept
94{
95 if constexpr (Endian != std::endian::native) {
96 return std::byteswap(unaligned_load<Out>(src));
97 } else {
99 }
100}
101
109template<std::integral T, std::endian Endian = std::endian::native>
110[[nodiscard]] inline T load(void const *src) noexcept
111{
112 auto value = unaligned_load<T>(src);
113 if constexpr (Endian != std::endian::native) {
114 value = std::byteswap(value);
115 }
116 return value;
117}
118
125template<std::integral T>
126[[nodiscard]] constexpr T load_le(T const *src) noexcept
127{
129}
130
137template<std::integral T, byte_like B>
138[[nodiscard]] constexpr T load_le(B const *src) noexcept
139{
141}
142
149template<std::integral T>
150[[nodiscard]] inline T load_le(void const *src) noexcept
151{
153}
154
161template<std::integral T>
162[[nodiscard]] constexpr T load_be(T const *src) noexcept
163{
165}
166
173template<std::integral T, byte_like B>
174[[nodiscard]] constexpr T load_be(B const *src) noexcept
175{
177}
178
185template<std::integral T>
186[[nodiscard]] inline T load_be(void const *src) noexcept
187{
189}
190
206template<unsigned int NumBits, byte_like B>
207[[nodiscard]] constexpr auto load_bits_be(B const *src, size_t bit_index) noexcept
208{
209 static_assert(NumBits <= sizeof(unsigned long long) * CHAR_BIT);
210
211 constexpr auto num_bits = NumBits;
212 constexpr auto num_bytes = (num_bits + CHAR_BIT - 1) / CHAR_BIT;
213
214 // Determine an unsigned type that can be used to read NumBits in a single `load_be()` on every bit offset.
215 // clang-format off
216 using value_type =
217 std::conditional_t<num_bytes < sizeof(unsigned short), unsigned short,
218 std::conditional_t<num_bytes < sizeof(unsigned int), unsigned int,
219 std::conditional_t<num_bytes < sizeof(unsigned long), unsigned long, unsigned long long>>>;
220 // clang-format on
221
222 constexpr auto value_bits = sizeof(value_type) * CHAR_BIT;
223
225 hilet bit_offset = bit_index % CHAR_BIT;
226
227 // Optimization of reading a byte, aligned to a byte.
228 if constexpr (num_bits == CHAR_BIT) {
229 if (bit_offset == 0) {
231 }
232 }
233
234 // load_be allows unaligned reads.
236
237 // Align to most significant bit. In preparation for loading
238 // one more byte.
239 r <<= bit_offset;
240
241 if constexpr (num_bytes == sizeof(value_type)) {
242 // In this case it is possible we could not read the whole value in one go.
243 // We may need to read one more byte.
244
246 if (bits_done < num_bits) {
247 auto rest = char_cast<value_type>(src[byte_offset + sizeof(value_type)]);
249 r |= rest;
250 }
251 }
252
253 // Align number to least significant bit.
254 r >>= value_bits - num_bits;
255 return r;
256}
257
258template<std::endian Endian = std::endian::native, std::integral T, byte_like B>
259constexpr void store(T value, B const *dst) noexcept
260{
261 if constexpr (Endian != std::endian::native) {
262 value = std::byteswap(value);
263 }
264 unaligned_store<T>(value, dst);
265}
266
267template<std::endian Endian = std::endian::native, std::integral T>
268constexpr void store(T value, void const *dst) noexcept
269{
270 if constexpr (Endian != std::endian::native) {
271 value = std::byteswap(value);
272 }
273 unaligned_store<T>(value, dst);
274}
275
276template<std::integral T, byte_like B>
277constexpr void store_le(T value, B const *dst) noexcept
278{
280}
281
282template<std::integral T>
283inline void store_le(T value, void const *dst) noexcept
284{
286}
287
288template<std::integral T, byte_like B>
289constexpr void store_be(T value, B const *dst) noexcept
290{
292}
293
294template<std::integral T>
295inline void store_be(T value, void const *dst) noexcept
296{
298}
299
300template<typename T, std::endian E, std::size_t A = alignof(T)>
302 using value_type = T;
303 constexpr static std::endian endian = E;
304 constexpr static std::size_t alignment = A;
305
306 alignas(A) std::byte _value[sizeof(T)];
307
308 [[nodiscard]] constexpr value_type operator*() const noexcept
309 {
310 return load<value_type, endian>(_value);
311 }
312
313 constexpr endian_buf_t& operator=(value_type x) noexcept
314 {
315 store<endian>(x, _value);
316 return *this;
317 }
318};
319
321using big_uint32_buf_t = endian_buf_t<uint32_t, std::endian::big, 1>;
322using big_uint16_buf_t = endian_buf_t<uint16_t, std::endian::big, 1>;
338
357
358}} // namespace hi::inline v1
359
360hi_warning_pop();
DOXYGEN BUG.
Definition algorithm.hpp:16
geometry/margins.hpp
Definition lookahead_iterator.hpp:5
constexpr T big_to_native(T x)
Convert an integral from big-to-native endian.
Definition endian.hpp:52
constexpr T native_to_little(T x)
Convert an integral from native-to-little endian.
Definition endian.hpp:64
constexpr T load_be(T const *src) noexcept
Load of a numeric value encoded in big-endian format.
Definition endian.hpp:162
constexpr T little_to_native(T x)
Convert an integral from little-to-native endian.
Definition endian.hpp:40
constexpr T load_le(T const *src) noexcept
Load of a numeric value encoded in little-endian format.
Definition endian.hpp:126
constexpr Out load(In const *src) noexcept
Unaligned Load of a numeric value from an array.
Definition endian.hpp:93
constexpr T native_to_big(T x)
Convert an integral from native-to-big endian.
Definition endian.hpp:76
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:207
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:377
Horizontal/Vertical alignment combination.
Definition alignment.hpp:242
Definition endian.hpp:301
T addressof(T... args)