Line data Source code
1 : /**
2 : Copyright (c) 2016-2022 Roman Katuntsev <sbkarr@stappler.org>
3 : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
4 :
5 : Permission is hereby granted, free of charge, to any person obtaining a copy
6 : of this software and associated documentation files (the "Software"), to deal
7 : in the Software without restriction, including without limitation the rights
8 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 : copies of the Software, and to permit persons to whom the Software is
10 : furnished to do so, subject to the following conditions:
11 :
12 : The above copyright notice and this permission notice shall be included in
13 : all copies or substantial portions of the Software.
14 :
15 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 : THE SOFTWARE.
22 : **/
23 :
24 : #ifndef STAPPLER_CORE_STRING_SPBYTEORDER_H_
25 : #define STAPPLER_CORE_STRING_SPBYTEORDER_H_
26 :
27 : #include "SPCore.h"
28 :
29 : #ifndef __CDT_PARSER__
30 : #ifndef SPINLINE
31 : #define SPINLINE __attribute__((always_inline))
32 : #endif
33 : #endif
34 :
35 : #ifndef __has_builtin // Optional of course
36 : #define __has_builtin(x) 0 // Compatibility with non-clang compilers
37 : #endif
38 :
39 : // Adapted code from BOOST_ENDIAN_INTRINSICS
40 : // GCC and Clang recent versions provide intrinsic byte swaps via builtins
41 : #if (defined(__clang__) && __has_builtin(__builtin_bswap32) && __has_builtin(__builtin_bswap64)) \
42 : || (defined(__GNUC__ ) && \
43 : (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)))
44 :
45 : // prior to 4.8, gcc did not provide __builtin_bswap16 on some platforms so we emulate it
46 : // see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52624
47 : // Clang has a similar problem, but their feature test macros make it easier to detect
48 : namespace STAPPLER_VERSIONIZED stappler::byteorder {
49 : # if (defined(__clang__) && __has_builtin(__builtin_bswap16)) \
50 : || (defined(__GNUC__) &&(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
51 6547721 : static SPINLINE inline uint16_t bswap16(uint16_t x) { return __builtin_bswap16(x); }
52 : # else
53 : static SPINLINE inline uint16_t bswap16(uint16_t x) { return __builtin_bswap32(x) << 16; }
54 : # endif
55 21743236 : static SPINLINE inline uint32_t bswap32(uint32_t x) { return __builtin_bswap32(x); }
56 13125 : static SPINLINE inline uint64_t bswap64(uint64_t x) { return __builtin_bswap64(x); }
57 : }
58 :
59 : // Linux systems provide the byteswap.h header, with
60 : #elif defined(__linux__)
61 : // don't check for obsolete forms defined(linux) and defined(__linux) on the theory that
62 : // compilers that predefine only these are so old that byteswap.h probably isn't present.
63 : # include <byteswap.h>
64 : namespace STAPPLER_VERSIONIZED stappler::byteorder {
65 : static SPINLINE inline uint16_t bswap16(uint16_t x) { return bswap_16(x); }
66 : static SPINLINE inline uint32_t bswap32(uint32_t x) { return bswap_32(x); }
67 : static SPINLINE inline uint64_t bswap64(uint64_t x) { return bswap_64(x); }
68 : }
69 :
70 : #elif defined(_MSC_VER)
71 : // Microsoft documents these as being compatible since Windows 95 and specificly
72 : // lists runtime library support since Visual Studio 2003 (aka 7.1).
73 : # include <cstdlib>
74 : namespace STAPPLER_VERSIONIZED stappler::byteorder {
75 : static SPINLINE inline uint16_t bswap16(uint16_t x) { return _byteswap_ushort(x); }
76 : static SPINLINE inline uint32_t bswap32(uint32_t x) { return _byteswap_ulong(x); }
77 : static SPINLINE inline uint64_t bswap64(uint64_t x) { return _byteswap_uint64(x); }
78 : }
79 : #else
80 : namespace STAPPLER_VERSIONIZED stappler::byteorder {
81 : static SPINLINE inline uint16_t bswap16(uint16_t x) {
82 : return (x & 0xFF) << 8 | ((x >> 8) & 0xFF);
83 : }
84 :
85 : static SPINLINE inline uint32_t bswap32(uint32_t x) {
86 : return x & 0xFF << 24
87 : | (x >> 8 & 0xFF) << 16
88 : | (x >> 16 & 0xFF) << 8
89 : | (x >> 24 & 0xFF);
90 : }
91 : static SPINLINE inline uint64_t bswap64(uint64_t x) {
92 : return x & 0xFF << 56
93 : | (x >> 8 & 0xFF) << 48
94 : | (x >> 16 & 0xFF) << 40
95 : | (x >> 24 & 0xFF) << 32
96 : | (x >> 32 & 0xFF) << 24
97 : | (x >> 40 & 0xFF) << 16
98 : | (x >> 48 & 0xFF) << 8
99 : | (x >> 56 & 0xFF);
100 : }
101 : }
102 : #endif
103 :
104 : namespace STAPPLER_VERSIONIZED stappler {
105 :
106 : #if __cpp_lib_endian >= 201907L
107 : enum class Endian {
108 : Big,
109 : Little,
110 : Mixed,
111 : Network = Big,
112 : Host = (std::endian::native == std::endian::little) ? Little : ((std::endian::native == std::endian::big) ? Big : Mixed), // no support for custom endian
113 : };
114 : #else
115 : enum class Endian {
116 : Big,
117 : Little,
118 : Mixed,
119 : Network = Big,
120 : #if (__i386__) || (_M_IX86) || (__x86_64__) || (_M_X64) || (__arm__) || (_M_ARM) || (__arm64__) || (__arm64) || defined(__aarch64__) || defined(__e2k__)
121 : Host = Little,
122 : #else
123 : Host = Big,
124 : #endif
125 : };
126 : #endif
127 :
128 : }
129 :
130 : namespace STAPPLER_VERSIONIZED stappler::byteorder {
131 :
132 : enum class ShouldSwap {
133 : NoSwap,
134 : DoSwap,
135 : CustomSwap,
136 : };
137 :
138 : static constexpr size_t Bit8Size = 1;
139 : static constexpr size_t Bit16Size = 2;
140 : static constexpr size_t Bit32Size = 4;
141 : static constexpr size_t Bit64Size = 8;
142 :
143 : template <class T, ShouldSwap ByteSwap, size_t Size>
144 : struct Converter;
145 :
146 : template <class T>
147 : struct Converter<T, ShouldSwap::DoSwap, Bit8Size> {
148 : static inline T Swap(T value) {
149 : return value;
150 : }
151 : };
152 :
153 : template <class T>
154 : struct Converter<T, ShouldSwap::DoSwap, Bit16Size> {
155 6547721 : static inline T Swap(T value) {
156 13095442 : return reinterpretValue<T>(bswap16(reinterpretValue<uint16_t>(value)));
157 : }
158 : };
159 :
160 : template <class T>
161 : struct Converter<T, ShouldSwap::DoSwap, Bit32Size> {
162 21743236 : static inline T Swap(T value) {
163 43486472 : return reinterpretValue<T>(bswap32(reinterpretValue<uint32_t>(value)));
164 : }
165 : };
166 :
167 : template <class T>
168 : struct Converter<T, ShouldSwap::DoSwap, Bit64Size> {
169 13125 : static inline T Swap(T value) {
170 26250 : return reinterpretValue<T>(bswap64(reinterpretValue<uint64_t>(value)));
171 : }
172 : };
173 :
174 : template <class T, size_t Size>
175 : struct Converter<T, ShouldSwap::DoSwap, Size> {
176 : static inline T Swap(T value) {
177 : T ret;
178 : char *ptr = (char *)&ret;
179 : ::memcpy((void *)ptr, (const void *)&value, sizeof(T));
180 : std::reverse(ptr, ptr + sizeof(T));
181 : return ret;
182 : }
183 : };
184 :
185 : template <class T, size_t Size>
186 : struct Converter<T, ShouldSwap::NoSwap, Size> {
187 60434 : static inline T Swap(T value) { return value; }
188 : };
189 :
190 :
191 : static constexpr ShouldSwap hostToNetwork() {
192 : if constexpr (Endian::Host == Endian::Network) { return ShouldSwap::NoSwap; }
193 : if constexpr (Endian::Host == Endian::Little) { return ShouldSwap::DoSwap; }
194 : return ShouldSwap::CustomSwap;
195 : }
196 :
197 : static constexpr ShouldSwap hostToLittle() {
198 : if constexpr (Endian::Host == Endian::Little) { return ShouldSwap::NoSwap; }
199 : if constexpr (Endian::Host == Endian::Big) { return ShouldSwap::DoSwap; }
200 : return ShouldSwap::CustomSwap;
201 : }
202 :
203 : static constexpr ShouldSwap hostToBig() {
204 : if constexpr (Endian::Host == Endian::Big) { return ShouldSwap::NoSwap; }
205 : if constexpr (Endian::Host == Endian::Little) { return ShouldSwap::DoSwap; }
206 : return ShouldSwap::CustomSwap;
207 : }
208 :
209 : static constexpr bool isLittleEndian() {
210 : return Endian::Host == Endian::Little;
211 : }
212 :
213 : template <class T>
214 : using NetworkConverter = Converter<T, hostToNetwork(), sizeof(T)>;
215 :
216 : template <class T>
217 : using LittleConverter = Converter<T, hostToLittle(), sizeof(T)>;
218 :
219 : template <class T>
220 : using BigConverter = Converter<T, hostToBig(), sizeof(T)>;
221 :
222 : template <class T>
223 : using HostConverter = Converter<T, ShouldSwap::NoSwap, sizeof(T)>;
224 :
225 : template <class T>
226 4961036 : static inline T HostToNetwork(T value) {
227 4961036 : return NetworkConverter<T>::Swap(value);
228 : }
229 :
230 : template <class T>
231 : static inline T NetworkToHost(T value) {
232 : return NetworkConverter<T>::Swap(value);
233 : }
234 :
235 : template <class T>
236 4200 : static inline T HostToLittle(T value) {
237 4200 : return LittleConverter<T>::Swap(value);
238 : }
239 :
240 : template <class T>
241 : static inline T LittleToHost(T value) {
242 : return LittleConverter<T>::Swap(value);
243 : }
244 :
245 : template <class T>
246 400 : static inline T HostToBig(T value) {
247 400 : return BigConverter<T>::Swap(value);
248 : }
249 :
250 : template <class T>
251 : static inline T BigToHost(T value) {
252 : return BigConverter<T>::Swap(value);
253 : }
254 :
255 : template <Endian Endianess, typename T>
256 : struct ConverterTraits;
257 :
258 : template <typename T>
259 : struct ConverterTraits<Endian::Big, T> : BigConverter<T> { };
260 :
261 : template <typename T>
262 : struct ConverterTraits<Endian::Little, T> : LittleConverter<T> { };
263 :
264 : }
265 :
266 : #endif /* STAPPLER_CORE_STRING_SPBYTEORDER_H_ */
|