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 {
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 {
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 {
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>
201[[nodiscard]] hi_force_inline T load(void const *src) noexcept
202{
203 return load<T>(reinterpret_cast<uint8_t const *>(src));
204}
205
208template<numeric T>
209hi_force_inline T load_le(void const *src) noexcept
210{
211 return little_to_native(load<T>(src));
212}
213
216template<numeric T>
217hi_force_inline T load_be(void const *src) noexcept
218{
219 return big_to_native(load<T>(src));
220}
221
227template<std::unsigned_integral T>
228hi_force_inline void unaligned_load_le(T& r, void const *src) noexcept
229{
230#ifdef HI_HAS_SSE4_1
231 if constexpr (sizeof(T) == 8) {
232 r = _mm_extract_epi64(_mm_loadu_si64(src), 0);
233 } else if constexpr (sizeof(T) == 4) {
234 r = _mm_extract_epi32(_mm_loadu_si32(src), 0);
235 } else if constexpr (sizeof(T) == 2) {
236 r = _mm_extract_epi16(_mm_loadu_si16(src), 0);
237 } else if constexpr (sizeof(T) == 1) {
238 r = *reinterpret_cast<uint8_t const *>(src);
239 } else {
241 }
242#else
243 auto src_ = reinterpret_cast<uint8_t const *>(src);
244
245 auto i = 8;
246 auto tmp = T{};
247 while (--i) {
248 tmp <<= CHAR_BIT;
249 tmp |= *src_++;
250 }
251 r = byte_swap(tmp);
252#endif
253}
254
263template<std::unsigned_integral T>
264hi_force_inline void unaligned_load_le(T& r, void const *src, size_t size, size_t offset = 0) noexcept
265{
266 hi_axiom(offset < sizeof(T));
267 hi_axiom(size <= sizeof(T));
268 hi_axiom(size + offset <= sizeof(T));
269
270 auto src_ = reinterpret_cast<uint8_t const *>(src);
271
272 hilet first = offset * CHAR_BIT;
273 hilet last = (first + size) * CHAR_BIT;
274
275 for (auto i = first; i != last; i += CHAR_BIT) {
276 r |= wide_cast<T>(*src_++) << i;
277 }
278}
279
280} // namespace hi::inline v1
281
282hi_warning_pop();
Utilities to assert and bound check.
#define hi_static_no_default(...)
This part of the code should not be reachable, unless a programming bug.
Definition assert.hpp:181
#define hi_no_default(...)
This part of the code should not be reachable, unless a programming bug.
Definition assert.hpp:148
#define hi_axiom(expression,...)
Specify an axiom; an expression that is true.
Definition assert.hpp:133
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:228
hi_force_inline T load_le(void const *src) noexcept
Load an integer from unaligned memory in little-endian byte-order.
Definition endian.hpp:209
hi_force_inline T load_be(void const *src) noexcept
Load an integer from unaligned memory in big-endian byte-order.
Definition endian.hpp:217
Definition endian.hpp:128
T memcpy(T... args)