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 "utility.hpp"
8#include "architecture.hpp"
9#include "memory.hpp"
10#include "assert.hpp"
11#include "cast.hpp"
12#include "concepts.hpp"
13
14#ifdef HI_HAS_SSE
15#include <immintrin.h>
16#endif
17#ifdef HI_HAS_SSE2
18#include <emmintrin.h>
19#endif
20#ifdef HI_HAS_SSE4_1
21#include <smmintrin.h>
22#endif
23#if HI_COMPILER == HI_CC_MSVC
24#include <stdlib.h>
25#endif
26#include <bit>
27#include <concepts>
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
35namespace hi::inline v1 {
36
37template<std::unsigned_integral T>
38[[nodiscard]] T byte_swap(T x) noexcept
39{
40#if HI_COMPILER == HI_CC_CLANG || HI_COMPILER == HI_CC_GCC
41 if constexpr (sizeof(T) == sizeof(uint64_t)) {
42 return static_cast<T>(__builtin_bswap64(static_cast<uint64_t>(x)));
43 } else if constexpr (sizeof(T) == sizeof(uint32_t)) {
44 return static_cast<T>(__builtin_bswap32(static_cast<uint32_t>(x)));
45 } else if constexpr (sizeof(T) == sizeof(uint16_t)) {
46 return static_cast<T>(__builtin_bswap16(static_cast<uint16_t>(x)));
47 } else {
48 hi_no_default();
49 }
50#elif HI_COMPILER == HI_CC_MSVC
51 if constexpr (sizeof(T) == sizeof(uint64_t)) {
52 return static_cast<T>(_byteswap_uint64(static_cast<uint64_t>(x)));
53 } else if constexpr (sizeof(T) == sizeof(unsigned long)) {
54 return static_cast<T>(_byteswap_ulong(static_cast<unsigned long>(x)));
55 } else if constexpr (sizeof(T) == sizeof(unsigned short)) {
56 return static_cast<T>(_byteswap_ushort(static_cast<unsigned short>(x)));
57 } else {
58 hi_no_default();
59 }
60#else
61#error "Byteswap not implemented for this compiler."
62#endif
63}
64
65template<std::signed_integral T>
66[[nodiscard]] T byte_swap(T x) noexcept
67{
68 return static_cast<T>(byte_swap(static_cast<std::make_unsigned_t<T>>(x)));
69}
70
71template<std::floating_point T>
72[[nodiscard]] T byte_swap(T x) noexcept
73{
74 if constexpr (std::is_same_v<T, float>) {
75 auto utmp = std::bit_cast<uint32_t>(x);
76 utmp = byte_swap(utmp);
77 return std::bit_cast<float>(x);
78 } else if constexpr (std::is_same_v<T, double>) {
79 auto utmp = std::bit_cast<uint64_t>(x);
80 utmp = byte_swap(utmp);
81 return std::bit_cast<double>(x);
82 } else {
83 hi_no_default();
84 }
85}
86
87template<std::integral T>
88[[nodiscard]] T little_to_native(T x)
89{
90 if constexpr (std::endian::native == std::endian::little) {
91 return x;
92 } else {
93 return byte_swap(x);
94 }
95}
96
97template<std::integral T>
98[[nodiscard]] T big_to_native(T x)
99{
100 if constexpr (std::endian::native == std::endian::big) {
101 return x;
102 } else {
103 return byte_swap(x);
104 }
105}
106
107template<std::integral T>
108[[nodiscard]] T native_to_little(T x)
109{
110 if constexpr (std::endian::native == std::endian::little) {
111 return x;
112 } else {
113 return byte_swap(x);
114 }
115}
116
117template<std::integral T>
118[[nodiscard]] T native_to_big(T x)
119{
120 if constexpr (std::endian::native == std::endian::big) {
121 return x;
122 } else {
123 return byte_swap(x);
124 }
125}
126
127template<typename T, std::endian E, std::size_t A = alignof(T)>
129 alignas(A) std::byte _value[sizeof(T)];
130
131 [[nodiscard]] T value() const noexcept
132 {
133 T x;
134 std::memcpy(&x, &_value[0], sizeof(T));
135
136 return E == std::endian::native ? x : byte_swap(x);
137 }
138
139 endian_buf_t& set_value(T x) noexcept
140 {
141 if constexpr (E != std::endian::native) {
142 x = byte_swap(x);
143 }
144
145 std::memcpy(&_value[0], &x, sizeof(T));
146 return *this;
147 }
148
149 endian_buf_t& operator=(T x) noexcept
150 {
151 return set_value(x);
152 }
153
154 operator T() const noexcept
155 {
156 return value();
157 }
158};
159
178
197
200template<numeric T>
201hi_force_inline T load(void const *src) noexcept
202{
203#if HI_COMPILER == HI_CC_MSVC
204 return *reinterpret_cast<__unaligned T const *>(src);
205#else
206#error "missing implementation for load()"
207#endif
208}
209
212template<numeric T>
213hi_force_inline T load_le(void const *src) noexcept
214{
215 return little_to_native(load<T>(src));
216}
217
220template<numeric T>
221hi_force_inline T load_be(void const *src) noexcept
222{
223 return big_to_native(load<T>(src));
224}
225
231template<std::unsigned_integral T>
232hi_force_inline void unaligned_load_le(T& r, void const *src) noexcept
233{
234#ifdef HI_HAS_SSE4_1
235 if constexpr (sizeof(T) == 8) {
236 r = _mm_extract_epi64(_mm_loadu_si64(src), 0);
237 } else if constexpr (sizeof(T) == 4) {
238 r = _mm_extract_epi32(_mm_loadu_si32(src), 0);
239 } else if constexpr (sizeof(T) == 2) {
240 r = _mm_extract_epi16(_mm_loadu_si16(src), 0);
241 } else if constexpr (sizeof(T) == 1) {
242 r = *reinterpret_cast<uint8_t const *>(src);
243 } else {
244 hi_static_no_default();
245 }
246#else
247 auto src_ = reinterpret_cast<uint8_t const *>(src);
248
249 auto i = 8;
250 auto tmp = T{};
251 while (--i) {
252 tmp <<= CHAR_BIT;
253 tmp |= *src_++;
254 }
255 r = byte_swap(tmp);
256#endif
257}
258
267template<std::unsigned_integral T>
268hi_force_inline void unaligned_load_le(T& r, void const *src, size_t size, size_t offset = 0) noexcept
269{
270 hi_axiom(offset < sizeof(T));
271 hi_axiom(size <= sizeof(T));
272 hi_axiom(size + offset <= sizeof(T));
273
274 auto src_ = reinterpret_cast<uint8_t const *>(src);
275
276 hilet first = offset * CHAR_BIT;
277 hilet last = (first + size) * CHAR_BIT;
278
279 for (auto i = first; i != last; i += CHAR_BIT) {
280 r |= wide_cast<T>(*src_++) << i;
281 }
282}
283
284} // namespace hi::inline v1
285
286hi_warning_pop();
Utilities used by the HikoGUI library itself.
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
Functions and macros for handling architectural difference between compilers, CPUs and operating syst...
DOXYGEN BUG.
Definition algorithm.hpp:15
hi_force_inline T load(void const *src) noexcept
Load an integer from unaligned memory in native byte-order.
Definition endian.hpp:201
hi_force_inline void unaligned_load_le(T &r, void const *src) noexcept
Load data from memory.
Definition endian.hpp:232
hi_force_inline T load_le(void const *src) noexcept
Load an integer from unaligned memory in little-endian byte-order.
Definition endian.hpp:213
hi_force_inline T load_be(void const *src) noexcept
Load an integer from unaligned memory in big-endian byte-order.
Definition endian.hpp:221
Definition endian.hpp:128
T memcpy(T... args)