RAVENNAKIT
Loading...
Searching...
No Matches
byte_order.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: AGPL-3.0-or-later
2/*
3 * Project: RAVENNAKIT (RAVENNA / AES67 / ST2110-30 SDK)
4 * Copyright (c) 2024-2025 Sound on Digital
5 *
6 * This file is part of RAVENNAKIT.
7 *
8 * RAVENNAKIT is dual-licensed:
9 * 1) Under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version (the "AGPL License"); and
12 * 2) Under a commercial license from Sound on Digital, for customers who
13 * cannot (or do not wish to) comply with the AGPL License terms.
14 *
15 * If you obtained this file under the AGPL License, you may redistribute it
16 * and/or modify it under the terms of the AGPL License. See the LICENSE
17 * file in the project root for details.
18 *
19 * For commercial licensing, support, and other inquiries, please visit:
20 *
21 * https://ravennakit.com
22 *
23 */
24
25#pragma once
26
27#include <cstdint>
28#include <cstring>
29
31
32// This defines RAV_LITTLE_ENDIAN and RAV_BIG_ENDIAN macros indicating the byte order of the system.
33#if (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) || (defined(__BIG_ENDIAN__) && __BIG_ENDIAN__)
34 #define RAV_LITTLE_ENDIAN 0
35 #define RAV_BIG_ENDIAN 1
36
37namespace rav {
38static constexpr bool little_endian = false;
39static constexpr bool big_endian = true;
40} // namespace rav
41
42 #warning "Big endian systems have not been tested yet"
43#else
44 #define RAV_LITTLE_ENDIAN 1
45 #define RAV_BIG_ENDIAN 0
46
47namespace rav {
48static constexpr bool little_endian = true;
49static constexpr bool big_endian = false;
50} // namespace rav
51#endif
52
53// This defines the byte swap functions for 16, 32, and 64 bit integers, using platform-specific implementations if
54// possible.
55#if defined(__clang__) || defined(__GNUC__)
56 #define RAV_BYTE_SWAP_16(x) __builtin_bswap16(x)
57 #define RAV_BYTE_SWAP_32(x) __builtin_bswap32(x)
58 #define RAV_BYTE_SWAP_64(x) __builtin_bswap64(x)
59#elif defined(_MSC_VER)
60 #include <intrin.h>
61 #define RAV_BYTE_SWAP_16(x) _byteswap_ushort(x)
62 #define RAV_BYTE_SWAP_32(x) _byteswap_ulong(x)
63 #define RAV_BYTE_SWAP_64(x) _byteswap_uint64(x)
64#else
65 #error "Unsupported compiler"
66#endif
67
68namespace rav {
69
75inline void swap_bytes(uint8_t* data, const size_t size) {
76 // Check for null data pointer or zero size
77 if (data == nullptr || size == 0) {
78 return;
79 }
80
81 for (size_t i = 0; i < size / 2; ++i) {
82 std::swap(data[i], data[size - i - 1]);
83 }
84}
85
92inline void swap_bytes(uint8_t* data, const size_t size, const size_t stride) {
93 // Check for null data pointer or zero size
94 if (data == nullptr || size == 0 || stride <= 1) {
95 return;
96 }
97
98 for (size_t i = 0; i < size; i += stride) {
99 for (size_t j = 0; j < stride / 2; ++j) {
100 std::swap(data[i + j], data[i + stride - j - 1]);
101 }
102 }
103}
104
110template<typename Type, std::enable_if_t<std::is_trivially_copyable_v<Type>, bool> = true>
111Type swap_bytes(Type value) {
112 // Note: this function hasn't been benchmarked.
113 if constexpr (sizeof(Type) == 1) {
114 return value;
115 } else if constexpr (sizeof(Type) == 2) {
116 return static_cast<Type>(RAV_BYTE_SWAP_16(static_cast<uint16_t>(value)));
117 } else if constexpr (sizeof(Type) == 4) {
118 return static_cast<Type>(RAV_BYTE_SWAP_32(static_cast<uint32_t>(value)));
119 } else if constexpr (sizeof(Type) == 8) {
120 return static_cast<Type>(RAV_BYTE_SWAP_64(static_cast<uint64_t>(value)));
121 } else {
122 swap_bytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
123 return value;
124 }
125}
126
132template<>
133inline float swap_bytes<float>(const float value) {
134 static_assert(sizeof(float) == sizeof(uint32_t), "Float must be 32 bits");
135
136 union {
137 float f;
138 uint32_t i;
139 } u = {value};
140
141 u.i = RAV_BYTE_SWAP_32(u.i);
142 return u.f;
143}
144
150template<>
151inline double swap_bytes<double>(const double value) {
152 static_assert(sizeof(double) == sizeof(uint64_t), "Double must be 64 bits");
153
154 union {
155 double f;
156 uint64_t i;
157 } u = {value};
158
159 u.i = RAV_BYTE_SWAP_64(u.i);
160 return u.f;
161}
162
169template<typename Type, std::enable_if_t<std::is_trivially_copyable_v<Type>, bool> = true>
170Type swap_if_le(const Type value) {
171 if constexpr (little_endian) {
172 return swap_bytes(value);
173 } else {
174 return value;
175 }
176}
177
184template<typename Type, std::enable_if_t<std::is_trivially_copyable_v<Type>, bool> = true>
185Type swap_if_be(const Type value) {
186 if constexpr (big_endian) {
187 return swap_bytes(value);
188 } else {
189 return value;
190 }
191}
192
199template<typename Type, std::enable_if_t<std::is_trivially_copyable_v<Type>, bool> = true>
200Type read_ne(const uint8_t* data) {
201 Type value;
202 std::memcpy(std::addressof(value), data, sizeof(Type));
203 return value;
204}
205
212template<typename Type, std::enable_if_t<std::is_trivially_copyable_v<Type>, bool> = true>
213Type read_be(const uint8_t* data) {
214 return swap_if_le(read_ne<Type>(data));
215}
216
223template<typename Type, std::enable_if_t<std::is_trivially_copyable_v<Type>, bool> = true>
224Type read_le(const uint8_t* data) {
225 return swap_if_be(read_ne<Type>(data));
226}
227
234template<typename Type, std::enable_if_t<std::is_trivially_copyable_v<Type>, bool> = true>
235void write_ne(uint8_t* dst, const Type value) {
236 std::memcpy(dst, std::addressof(value), sizeof(Type));
237}
238
245template<typename Type, std::enable_if_t<std::is_trivially_copyable_v<Type>, bool> = true>
246void write_be(uint8_t* dst, const Type value) {
247 write_ne(dst, swap_if_le(value));
248}
249
256template<typename Type, std::enable_if_t<std::is_trivially_copyable_v<Type>, bool> = true>
257void write_le(uint8_t* dst, const Type value) {
258 write_ne(dst, swap_if_be(value));
259}
260
265inline bool is_little_endian_at_runtime() noexcept {
266 static size_t native_one = 1;
267 return reinterpret_cast<uint8_t*>(&native_one)[0] == 1;
268}
269
273inline void validate_byte_order() {
274#if RAV_LITTLE_ENDIAN
276 RAV_THROW_EXCEPTION("Compile-time and runtime byte order do not match");
277 }
278#else
279 if (is_little_endian_runtime()) {
280 RAV_THROW_EXCEPTION("Compile-time and runtime byte order do not match");
281 }
282#endif
283}
284
285} // namespace rav
#define RAV_THROW_EXCEPTION(...)
Definition exception.hpp:44
float swap_bytes< float >(const float value)
Specialization of swap_bytes for floats.
static constexpr bool big_endian
Type read_le(const uint8_t *data)
Reads a little-endian value from the given data.
void swap_bytes(uint8_t *data, const size_t size)
Swaps given amount of bytes in the given data, in place.
Type swap_if_le(const Type value)
Swaps the bytes of the given value if the system is little-endian.
Type read_ne(const uint8_t *data)
Reads a value from the given data in native byte order (not to be confused with network-endian).
bool is_little_endian_at_runtime() noexcept
Checks if the system is little-endian at runtime.
static constexpr bool little_endian
Type read_be(const uint8_t *data)
Reads a big-endian value from the given data.
void write_le(uint8_t *dst, const Type value)
Writes a little-endian value to the given destination.
void write_be(uint8_t *dst, const Type value)
Writes a big-endian value to the given destination.
Type swap_if_be(const Type value)
Swaps the bytes of the given value if the system is big-endian.
double swap_bytes< double >(const double value)
Specialization of swap_bytes for doubles.
void validate_byte_order()
Validates whether the compiled byte order matches the runtime byte order.
void write_ne(uint8_t *dst, const Type value)
Writes a value to the given destination in native byte order (not to be confused with network-endian)...