1 | // Copyright (C) 2021 The Qt Company Ltd. |
2 | // Copyright (C) 2021 Intel Corporation. |
3 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
4 | |
5 | #ifndef QENDIAN_H |
6 | #define QENDIAN_H |
7 | |
8 | #if 0 |
9 | #pragma qt_class(QtEndian) |
10 | #endif |
11 | |
12 | #include <QtCore/qfloat16.h> |
13 | #include <QtCore/qglobal.h> |
14 | |
15 | #include <limits> |
16 | |
17 | // include stdlib.h and hope that it defines __GLIBC__ for glibc-based systems |
18 | #include <stdlib.h> |
19 | #include <string.h> |
20 | |
21 | #ifdef min // MSVC |
22 | #undef min |
23 | #undef max |
24 | #endif |
25 | |
26 | QT_BEGIN_NAMESPACE |
27 | |
28 | /* |
29 | * ENDIAN FUNCTIONS |
30 | */ |
31 | |
32 | // Used to implement a type-safe and alignment-safe copy operation |
33 | // If you want to avoid the memcpy, you must write specializations for these functions |
34 | template <typename T> Q_ALWAYS_INLINE void qToUnaligned(const T src, void *dest) |
35 | { |
36 | // Using sizeof(T) inside memcpy function produces internal compiler error with |
37 | // MSVC2008/ARM in tst_endian -> use extra indirection to resolve size of T. |
38 | const size_t size = sizeof(T); |
39 | #if __has_builtin(__builtin_memcpy) |
40 | __builtin_memcpy |
41 | #else |
42 | memcpy |
43 | #endif |
44 | (dest, &src, size); |
45 | } |
46 | |
47 | template <typename T> Q_ALWAYS_INLINE T qFromUnaligned(const void *src) |
48 | { |
49 | T dest; |
50 | const size_t size = sizeof(T); |
51 | #if __has_builtin(__builtin_memcpy) |
52 | __builtin_memcpy |
53 | #else |
54 | memcpy |
55 | #endif |
56 | (&dest, src, size); |
57 | return dest; |
58 | } |
59 | |
60 | // These definitions are written so that they are recognized by most compilers |
61 | // as bswap and replaced with single instruction builtins if available. |
62 | inline constexpr quint64 qbswap_helper(quint64 source) |
63 | { |
64 | return 0 |
65 | | ((source & Q_UINT64_C(0x00000000000000ff)) << 56) |
66 | | ((source & Q_UINT64_C(0x000000000000ff00)) << 40) |
67 | | ((source & Q_UINT64_C(0x0000000000ff0000)) << 24) |
68 | | ((source & Q_UINT64_C(0x00000000ff000000)) << 8) |
69 | | ((source & Q_UINT64_C(0x000000ff00000000)) >> 8) |
70 | | ((source & Q_UINT64_C(0x0000ff0000000000)) >> 24) |
71 | | ((source & Q_UINT64_C(0x00ff000000000000)) >> 40) |
72 | | ((source & Q_UINT64_C(0xff00000000000000)) >> 56); |
73 | } |
74 | |
75 | inline constexpr quint32 qbswap_helper(quint32 source) |
76 | { |
77 | return 0 |
78 | | ((source & 0x000000ff) << 24) |
79 | | ((source & 0x0000ff00) << 8) |
80 | | ((source & 0x00ff0000) >> 8) |
81 | | ((source & 0xff000000) >> 24); |
82 | } |
83 | |
84 | inline constexpr quint16 qbswap_helper(quint16 source) |
85 | { |
86 | return quint16( 0 |
87 | | ((source & 0x00ff) << 8) |
88 | | ((source & 0xff00) >> 8) ); |
89 | } |
90 | |
91 | inline constexpr quint8 qbswap_helper(quint8 source) |
92 | { |
93 | return source; |
94 | } |
95 | |
96 | /* |
97 | * T qbswap(T source). |
98 | * Changes the byte order of a value from big-endian to little-endian or vice versa. |
99 | * This function can be used if you are not concerned about alignment issues, |
100 | * and it is therefore a bit more convenient and in most cases more efficient. |
101 | */ |
102 | template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>> |
103 | inline constexpr T qbswap(T source) |
104 | { |
105 | return T(qbswap_helper(typename QIntegerForSizeof<T>::Unsigned(source))); |
106 | } |
107 | |
108 | #ifdef QT_SUPPORTS_INT128 |
109 | // extra definitions for q(u)int128, in case std::is_integral_v<~~> == false |
110 | inline constexpr quint128 qbswap(quint128 source) |
111 | { |
112 | quint128 result = {}; |
113 | result = qbswap_helper(source: quint64(source)); |
114 | result <<= 64; |
115 | result |= qbswap_helper(source: quint64(source >> 64)); |
116 | return result; |
117 | } |
118 | |
119 | inline constexpr quint128 qbswap(qint128 source) |
120 | { |
121 | return qint128(qbswap(source: quint128(source))); |
122 | } |
123 | #endif |
124 | |
125 | // floating specializations |
126 | template<typename Float> |
127 | Float qbswapFloatHelper(Float source) |
128 | { |
129 | // memcpy call in qFromUnaligned is recognized by optimizer as a correct way of type prunning |
130 | auto temp = qFromUnaligned<typename QIntegerForSizeof<Float>::Unsigned>(&source); |
131 | temp = qbswap(temp); |
132 | return qFromUnaligned<Float>(&temp); |
133 | } |
134 | |
135 | inline qfloat16 qbswap(qfloat16 source) |
136 | { |
137 | return qbswapFloatHelper(source); |
138 | } |
139 | |
140 | inline float qbswap(float source) |
141 | { |
142 | return qbswapFloatHelper(source); |
143 | } |
144 | |
145 | inline double qbswap(double source) |
146 | { |
147 | return qbswapFloatHelper(source); |
148 | } |
149 | |
150 | /* |
151 | * qbswap(const T src, const void *dest); |
152 | * Changes the byte order of \a src from big-endian to little-endian or vice versa |
153 | * and stores the result in \a dest. |
154 | * There is no alignment requirements for \a dest. |
155 | */ |
156 | template <typename T> inline void qbswap(const T src, void *dest) |
157 | { |
158 | qToUnaligned<T>(qbswap(src), dest); |
159 | } |
160 | |
161 | template <int Size> void *qbswap(const void *source, qsizetype count, void *dest) noexcept; |
162 | template<> inline void *qbswap<1>(const void *source, qsizetype count, void *dest) noexcept |
163 | { |
164 | return source != dest ? memcpy(dest: dest, src: source, n: size_t(count)) : dest; |
165 | } |
166 | template<> Q_CORE_EXPORT void *qbswap<2>(const void *source, qsizetype count, void *dest) noexcept; |
167 | template<> Q_CORE_EXPORT void *qbswap<4>(const void *source, qsizetype count, void *dest) noexcept; |
168 | template<> Q_CORE_EXPORT void *qbswap<8>(const void *source, qsizetype count, void *dest) noexcept; |
169 | |
170 | #if Q_BYTE_ORDER == Q_BIG_ENDIAN |
171 | |
172 | template <typename T> inline constexpr T qToBigEndian(T source) |
173 | { return source; } |
174 | template <typename T> inline constexpr T qFromBigEndian(T source) |
175 | { return source; } |
176 | template <typename T> inline constexpr T qToLittleEndian(T source) |
177 | { return qbswap(source); } |
178 | template <typename T> inline constexpr T qFromLittleEndian(T source) |
179 | { return qbswap(source); } |
180 | template <typename T> inline void qToBigEndian(T src, void *dest) |
181 | { qToUnaligned<T>(src, dest); } |
182 | template <typename T> inline void qToLittleEndian(T src, void *dest) |
183 | { qbswap<T>(src, dest); } |
184 | |
185 | template <typename T> inline void qToBigEndian(const void *source, qsizetype count, void *dest) |
186 | { if (source != dest) memcpy(dest, source, count * sizeof(T)); } |
187 | template <typename T> inline void qToLittleEndian(const void *source, qsizetype count, void *dest) |
188 | { qbswap<sizeof(T)>(source, count, dest); } |
189 | template <typename T> inline void qFromBigEndian(const void *source, qsizetype count, void *dest) |
190 | { if (source != dest) memcpy(dest, source, count * sizeof(T)); } |
191 | template <typename T> inline void qFromLittleEndian(const void *source, qsizetype count, void *dest) |
192 | { qbswap<sizeof(T)>(source, count, dest); } |
193 | #else // Q_LITTLE_ENDIAN |
194 | |
195 | template <typename T> inline constexpr T qToBigEndian(T source) |
196 | { return qbswap(source); } |
197 | template <typename T> inline constexpr T qFromBigEndian(T source) |
198 | { return qbswap(source); } |
199 | template <typename T> inline constexpr T qToLittleEndian(T source) |
200 | { return source; } |
201 | template <typename T> inline constexpr T qFromLittleEndian(T source) |
202 | { return source; } |
203 | template <typename T> inline void qToBigEndian(T src, void *dest) |
204 | { qbswap<T>(src, dest); } |
205 | template <typename T> inline void qToLittleEndian(T src, void *dest) |
206 | { qToUnaligned<T>(src, dest); } |
207 | |
208 | template <typename T> inline void qToBigEndian(const void *source, qsizetype count, void *dest) |
209 | { qbswap<sizeof(T)>(source, count, dest); } |
210 | template <typename T> inline void qToLittleEndian(const void *source, qsizetype count, void *dest) |
211 | { if (source != dest) memcpy(dest: dest, src: source, n: count * sizeof(T)); } |
212 | template <typename T> inline void qFromBigEndian(const void *source, qsizetype count, void *dest) |
213 | { qbswap<sizeof(T)>(source, count, dest); } |
214 | template <typename T> inline void qFromLittleEndian(const void *source, qsizetype count, void *dest) |
215 | { if (source != dest) memcpy(dest: dest, src: source, n: count * sizeof(T)); } |
216 | #endif // Q_BYTE_ORDER == Q_BIG_ENDIAN |
217 | |
218 | |
219 | /* T qFromLittleEndian(const void *src) |
220 | * This function will read a little-endian encoded value from \a src |
221 | * and return the value in host-endian encoding. |
222 | * There is no requirement that \a src must be aligned. |
223 | */ |
224 | template <typename T> inline T qFromLittleEndian(const void *src) |
225 | { |
226 | return qFromLittleEndian(qFromUnaligned<T>(src)); |
227 | } |
228 | |
229 | template <> inline quint8 qFromLittleEndian<quint8>(const void *src) |
230 | { return static_cast<const quint8 *>(src)[0]; } |
231 | template <> inline qint8 qFromLittleEndian<qint8>(const void *src) |
232 | { return static_cast<const qint8 *>(src)[0]; } |
233 | |
234 | /* This function will read a big-endian (also known as network order) encoded value from \a src |
235 | * and return the value in host-endian encoding. |
236 | * There is no requirement that \a src must be aligned. |
237 | */ |
238 | template <class T> inline T qFromBigEndian(const void *src) |
239 | { |
240 | return qFromBigEndian(qFromUnaligned<T>(src)); |
241 | } |
242 | |
243 | template <> inline quint8 qFromBigEndian<quint8>(const void *src) |
244 | { return static_cast<const quint8 *>(src)[0]; } |
245 | template <> inline qint8 qFromBigEndian<qint8>(const void *src) |
246 | { return static_cast<const qint8 *>(src)[0]; } |
247 | |
248 | template<class S> |
249 | class QSpecialInteger |
250 | { |
251 | typedef typename S::StorageType T; |
252 | T val; |
253 | public: |
254 | QSpecialInteger() = default; |
255 | explicit constexpr QSpecialInteger(T i) : val(S::toSpecial(i)) {} |
256 | |
257 | QSpecialInteger &operator =(T i) { val = S::toSpecial(i); return *this; } |
258 | operator T() const { return S::fromSpecial(val); } |
259 | |
260 | bool operator ==(QSpecialInteger<S> i) const { return val == i.val; } |
261 | bool operator !=(QSpecialInteger<S> i) const { return val != i.val; } |
262 | |
263 | QSpecialInteger &operator +=(T i) |
264 | { return (*this = S::fromSpecial(val) + i); } |
265 | QSpecialInteger &operator -=(T i) |
266 | { return (*this = S::fromSpecial(val) - i); } |
267 | QSpecialInteger &operator *=(T i) |
268 | { return (*this = S::fromSpecial(val) * i); } |
269 | QSpecialInteger &operator >>=(T i) |
270 | { return (*this = S::fromSpecial(val) >> i); } |
271 | QSpecialInteger &operator <<=(T i) |
272 | { return (*this = S::fromSpecial(val) << i); } |
273 | QSpecialInteger &operator /=(T i) |
274 | { return (*this = S::fromSpecial(val) / i); } |
275 | QSpecialInteger &operator %=(T i) |
276 | { return (*this = S::fromSpecial(val) % i); } |
277 | QSpecialInteger &operator |=(T i) |
278 | { return (*this = S::fromSpecial(val) | i); } |
279 | QSpecialInteger &operator &=(T i) |
280 | { return (*this = S::fromSpecial(val) & i); } |
281 | QSpecialInteger &operator ^=(T i) |
282 | { return (*this = S::fromSpecial(val) ^ i); } |
283 | QSpecialInteger &operator ++() |
284 | { return (*this = S::fromSpecial(val) + 1); } |
285 | QSpecialInteger &operator --() |
286 | { return (*this = S::fromSpecial(val) - 1); } |
287 | QSpecialInteger operator ++(int) |
288 | { |
289 | QSpecialInteger<S> pre = *this; |
290 | *this += 1; |
291 | return pre; |
292 | } |
293 | QSpecialInteger operator --(int) |
294 | { |
295 | QSpecialInteger<S> pre = *this; |
296 | *this -= 1; |
297 | return pre; |
298 | } |
299 | |
300 | static constexpr QSpecialInteger max() |
301 | { return QSpecialInteger((std::numeric_limits<T>::max)()); } |
302 | static constexpr QSpecialInteger min() |
303 | { return QSpecialInteger((std::numeric_limits<T>::min)()); } |
304 | }; |
305 | |
306 | template<typename T> |
307 | class QLittleEndianStorageType { |
308 | public: |
309 | typedef T StorageType; |
310 | static constexpr T toSpecial(T source) { return qToLittleEndian(source); } |
311 | static constexpr T fromSpecial(T source) { return qFromLittleEndian(source); } |
312 | }; |
313 | |
314 | template<typename T> |
315 | class QBigEndianStorageType { |
316 | public: |
317 | typedef T StorageType; |
318 | static constexpr T toSpecial(T source) { return qToBigEndian(source); } |
319 | static constexpr T fromSpecial(T source) { return qFromBigEndian(source); } |
320 | }; |
321 | |
322 | #ifdef Q_QDOC |
323 | template<typename T> |
324 | class QLEInteger { |
325 | public: |
326 | explicit constexpr QLEInteger(T i); |
327 | QLEInteger &operator =(T i); |
328 | operator T() const; |
329 | bool operator ==(QLEInteger i) const; |
330 | bool operator !=(QLEInteger i) const; |
331 | QLEInteger &operator +=(T i); |
332 | QLEInteger &operator -=(T i); |
333 | QLEInteger &operator *=(T i); |
334 | QLEInteger &operator >>=(T i); |
335 | QLEInteger &operator <<=(T i); |
336 | QLEInteger &operator /=(T i); |
337 | QLEInteger &operator %=(T i); |
338 | QLEInteger &operator |=(T i); |
339 | QLEInteger &operator &=(T i); |
340 | QLEInteger &operator ^=(T i); |
341 | QLEInteger &operator ++(); |
342 | QLEInteger &operator --(); |
343 | QLEInteger operator ++(int); |
344 | QLEInteger operator --(int); |
345 | |
346 | static constexpr QLEInteger max(); |
347 | static constexpr QLEInteger min(); |
348 | }; |
349 | |
350 | template<typename T> |
351 | class QBEInteger { |
352 | public: |
353 | explicit constexpr QBEInteger(T i); |
354 | QBEInteger &operator =(T i); |
355 | operator T() const; |
356 | bool operator ==(QBEInteger i) const; |
357 | bool operator !=(QBEInteger i) const; |
358 | QBEInteger &operator +=(T i); |
359 | QBEInteger &operator -=(T i); |
360 | QBEInteger &operator *=(T i); |
361 | QBEInteger &operator >>=(T i); |
362 | QBEInteger &operator <<=(T i); |
363 | QBEInteger &operator /=(T i); |
364 | QBEInteger &operator %=(T i); |
365 | QBEInteger &operator |=(T i); |
366 | QBEInteger &operator &=(T i); |
367 | QBEInteger &operator ^=(T i); |
368 | QBEInteger &operator ++(); |
369 | QBEInteger &operator --(); |
370 | QBEInteger operator ++(int); |
371 | QBEInteger operator --(int); |
372 | |
373 | static constexpr QBEInteger max(); |
374 | static constexpr QBEInteger min(); |
375 | }; |
376 | #else |
377 | |
378 | template<typename T> |
379 | using QLEInteger = QSpecialInteger<QLittleEndianStorageType<T>>; |
380 | |
381 | template<typename T> |
382 | using QBEInteger = QSpecialInteger<QBigEndianStorageType<T>>; |
383 | #endif |
384 | template <typename T> |
385 | class QTypeInfo<QLEInteger<T> > |
386 | : public QTypeInfoMerger<QLEInteger<T>, T> {}; |
387 | |
388 | template <typename T> |
389 | class QTypeInfo<QBEInteger<T> > |
390 | : public QTypeInfoMerger<QBEInteger<T>, T> {}; |
391 | |
392 | typedef QLEInteger<qint16> qint16_le; |
393 | typedef QLEInteger<qint32> qint32_le; |
394 | typedef QLEInteger<qint64> qint64_le; |
395 | typedef QLEInteger<quint16> quint16_le; |
396 | typedef QLEInteger<quint32> quint32_le; |
397 | typedef QLEInteger<quint64> quint64_le; |
398 | |
399 | typedef QBEInteger<qint16> qint16_be; |
400 | typedef QBEInteger<qint32> qint32_be; |
401 | typedef QBEInteger<qint64> qint64_be; |
402 | typedef QBEInteger<quint16> quint16_be; |
403 | typedef QBEInteger<quint32> quint32_be; |
404 | typedef QBEInteger<quint64> quint64_be; |
405 | |
406 | QT_END_NAMESPACE |
407 | |
408 | #endif // QENDIAN_H |
409 | |