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 {
98 return unaligned_load<Out>(src);
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{
164 return load<T, std::endian::big>(src);
165}
166
173template<std::integral T, byte_like B>
174[[nodiscard]] constexpr T load_be(B const *src) noexcept
175{
176 return load<T, std::endian::big>(src);
177}
178
185template<std::integral T>
186[[nodiscard]] inline T load_be(void const *src) noexcept
187{
188 return load<T, std::endian::big>(src);
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
224 hilet byte_offset = bit_index / CHAR_BIT;
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) {
230 return char_cast<value_type>(src[byte_offset]);
231 }
232 }
233
234 // load_be allows unaligned reads.
235 auto r = load_be<value_type>(std::addressof(src[byte_offset]));
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
245 auto bits_done = value_bits - bit_offset;
246 if (bits_done < num_bits) {
247 auto rest = char_cast<value_type>(src[byte_offset + sizeof(value_type)]);
248 rest >>= CHAR_BIT - bit_offset;
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{
279 store<std::endian::little>(value, dst);
280}
281
282template<std::integral T>
283inline void store_le(T value, void const *dst) noexcept
284{
285 store<std::endian::little>(value, dst);
286}
287
288template<std::integral T, byte_like B>
289constexpr void store_be(T value, B const *dst) noexcept
290{
291 store<std::endian::big>(value, dst);
292}
293
294template<std::integral T>
295inline void store_be(T value, void const *dst) noexcept
296{
297 store<std::endian::big>(value, dst);
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
320using big_uint64_buf_t = endian_buf_t<uint64_t, std::endian::big, 1>;
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>;
323using big_int64_buf_t = endian_buf_t<int64_t, std::endian::big, 1>;
324using big_int32_buf_t = endian_buf_t<int32_t, std::endian::big, 1>;
325using big_int16_buf_t = endian_buf_t<int16_t, std::endian::big, 1>;
326using little_uint64_buf_t = endian_buf_t<uint64_t, std::endian::little, 1>;
327using little_uint32_buf_t = endian_buf_t<uint32_t, std::endian::little, 1>;
328using little_uint16_buf_t = endian_buf_t<uint16_t, std::endian::little, 1>;
329using little_int64_buf_t = endian_buf_t<int64_t, std::endian::little, 1>;
330using little_int32_buf_t = endian_buf_t<int32_t, std::endian::little, 1>;
331using little_int16_buf_t = endian_buf_t<int16_t, std::endian::little, 1>;
332using native_uint64_buf_t = endian_buf_t<uint64_t, std::endian::native, 1>;
333using native_uint32_buf_t = endian_buf_t<uint32_t, std::endian::native, 1>;
334using native_uint16_buf_t = endian_buf_t<uint16_t, std::endian::native, 1>;
335using native_int64_buf_t = endian_buf_t<int64_t, std::endian::native, 1>;
336using native_int32_buf_t = endian_buf_t<int32_t, std::endian::native, 1>;
337using native_int16_buf_t = endian_buf_t<int16_t, std::endian::native, 1>;
338
339using big_uint64_buf_at = endian_buf_t<uint64_t, std::endian::big>;
340using big_uint32_buf_at = endian_buf_t<uint32_t, std::endian::big>;
341using big_uint16_buf_at = endian_buf_t<uint16_t, std::endian::big>;
342using big_int64_buf_at = endian_buf_t<int64_t, std::endian::big>;
343using big_int32_buf_at = endian_buf_t<int32_t, std::endian::big>;
344using big_int16_buf_at = endian_buf_t<int16_t, std::endian::big>;
345using little_uint64_buf_at = endian_buf_t<uint64_t, std::endian::little>;
346using little_uint32_buf_at = endian_buf_t<uint32_t, std::endian::little>;
347using little_uint16_buf_at = endian_buf_t<uint16_t, std::endian::little>;
348using little_int64_buf_at = endian_buf_t<int64_t, std::endian::little>;
349using little_int32_buf_at = endian_buf_t<int32_t, std::endian::little>;
350using little_int16_buf_at = endian_buf_t<int16_t, std::endian::little>;
351using native_uint64_buf_at = endian_buf_t<uint64_t, std::endian::native>;
352using native_uint32_buf_at = endian_buf_t<uint32_t, std::endian::native>;
353using native_uint16_buf_at = endian_buf_t<uint16_t, std::endian::native>;
354using native_int64_buf_at = endian_buf_t<int64_t, std::endian::native>;
355using native_int32_buf_at = endian_buf_t<int32_t, std::endian::native>;
356using native_int16_buf_at = endian_buf_t<int16_t, std::endian::native>;
357
358}} // namespace hi::inline v1
359
360hi_warning_pop();
geometry/margins.hpp
Definition lookahead_iterator.hpp:5
The HikoGUI API version 1.
Definition lookahead_iterator.hpp:6
constexpr T big_to_native(T x)
Convert an integral from big-to-native endian.
Definition endian.hpp:52
constexpr Out char_cast(In rhs) noexcept
Cast a character.
Definition cast.hpp:550
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
Horizontal/Vertical alignment combination.
Definition alignment.hpp:242
Definition endian.hpp:301
T addressof(T... args)