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