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
26QT_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
34template <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
47template <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.
62inline 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
75inline 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
84inline constexpr quint16 qbswap_helper(quint16 source)
85{
86 return quint16( 0
87 | ((source & 0x00ff) << 8)
88 | ((source & 0xff00) >> 8) );
89}
90
91inline 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*/
102template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
103inline 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
110inline 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
119inline constexpr quint128 qbswap(qint128 source)
120{
121 return qint128(qbswap(source: quint128(source)));
122}
123#endif
124
125// floating specializations
126template<typename Float>
127Float 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
135inline qfloat16 qbswap(qfloat16 source)
136{
137 return qbswapFloatHelper(source);
138}
139
140inline float qbswap(float source)
141{
142 return qbswapFloatHelper(source);
143}
144
145inline 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*/
156template <typename T> inline void qbswap(const T src, void *dest)
157{
158 qToUnaligned<T>(qbswap(src), dest);
159}
160
161template <int Size> void *qbswap(const void *source, qsizetype count, void *dest) noexcept;
162template<> 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}
166template<> Q_CORE_EXPORT void *qbswap<2>(const void *source, qsizetype count, void *dest) noexcept;
167template<> Q_CORE_EXPORT void *qbswap<4>(const void *source, qsizetype count, void *dest) noexcept;
168template<> Q_CORE_EXPORT void *qbswap<8>(const void *source, qsizetype count, void *dest) noexcept;
169
170#if Q_BYTE_ORDER == Q_BIG_ENDIAN
171
172template <typename T> inline constexpr T qToBigEndian(T source)
173{ return source; }
174template <typename T> inline constexpr T qFromBigEndian(T source)
175{ return source; }
176template <typename T> inline constexpr T qToLittleEndian(T source)
177{ return qbswap(source); }
178template <typename T> inline constexpr T qFromLittleEndian(T source)
179{ return qbswap(source); }
180template <typename T> inline void qToBigEndian(T src, void *dest)
181{ qToUnaligned<T>(src, dest); }
182template <typename T> inline void qToLittleEndian(T src, void *dest)
183{ qbswap<T>(src, dest); }
184
185template <typename T> inline void qToBigEndian(const void *source, qsizetype count, void *dest)
186{ if (source != dest) memcpy(dest, source, count * sizeof(T)); }
187template <typename T> inline void qToLittleEndian(const void *source, qsizetype count, void *dest)
188{ qbswap<sizeof(T)>(source, count, dest); }
189template <typename T> inline void qFromBigEndian(const void *source, qsizetype count, void *dest)
190{ if (source != dest) memcpy(dest, source, count * sizeof(T)); }
191template <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
195template <typename T> inline constexpr T qToBigEndian(T source)
196{ return qbswap(source); }
197template <typename T> inline constexpr T qFromBigEndian(T source)
198{ return qbswap(source); }
199template <typename T> inline constexpr T qToLittleEndian(T source)
200{ return source; }
201template <typename T> inline constexpr T qFromLittleEndian(T source)
202{ return source; }
203template <typename T> inline void qToBigEndian(T src, void *dest)
204{ qbswap<T>(src, dest); }
205template <typename T> inline void qToLittleEndian(T src, void *dest)
206{ qToUnaligned<T>(src, dest); }
207
208template <typename T> inline void qToBigEndian(const void *source, qsizetype count, void *dest)
209{ qbswap<sizeof(T)>(source, count, dest); }
210template <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)); }
212template <typename T> inline void qFromBigEndian(const void *source, qsizetype count, void *dest)
213{ qbswap<sizeof(T)>(source, count, dest); }
214template <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*/
224template <typename T> inline T qFromLittleEndian(const void *src)
225{
226 return qFromLittleEndian(qFromUnaligned<T>(src));
227}
228
229template <> inline quint8 qFromLittleEndian<quint8>(const void *src)
230{ return static_cast<const quint8 *>(src)[0]; }
231template <> 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*/
238template <class T> inline T qFromBigEndian(const void *src)
239{
240 return qFromBigEndian(qFromUnaligned<T>(src));
241}
242
243template <> inline quint8 qFromBigEndian<quint8>(const void *src)
244{ return static_cast<const quint8 *>(src)[0]; }
245template <> inline qint8 qFromBigEndian<qint8>(const void *src)
246{ return static_cast<const qint8 *>(src)[0]; }
247
248template<class S>
249class QSpecialInteger
250{
251 typedef typename S::StorageType T;
252 T val;
253public:
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
306template<typename T>
307class QLittleEndianStorageType {
308public:
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
314template<typename T>
315class QBigEndianStorageType {
316public:
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
323template<typename T>
324class QLEInteger {
325public:
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
350template<typename T>
351class QBEInteger {
352public:
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
378template<typename T>
379using QLEInteger = QSpecialInteger<QLittleEndianStorageType<T>>;
380
381template<typename T>
382using QBEInteger = QSpecialInteger<QBigEndianStorageType<T>>;
383#endif
384template <typename T>
385class QTypeInfo<QLEInteger<T> >
386 : public QTypeInfoMerger<QLEInteger<T>, T> {};
387
388template <typename T>
389class QTypeInfo<QBEInteger<T> >
390 : public QTypeInfoMerger<QBEInteger<T>, T> {};
391
392typedef QLEInteger<qint16> qint16_le;
393typedef QLEInteger<qint32> qint32_le;
394typedef QLEInteger<qint64> qint64_le;
395typedef QLEInteger<quint16> quint16_le;
396typedef QLEInteger<quint32> quint32_le;
397typedef QLEInteger<quint64> quint64_le;
398
399typedef QBEInteger<qint16> qint16_be;
400typedef QBEInteger<qint32> qint32_be;
401typedef QBEInteger<qint64> qint64_be;
402typedef QBEInteger<quint16> quint16_be;
403typedef QBEInteger<quint32> quint32_be;
404typedef QBEInteger<quint64> quint64_be;
405
406QT_END_NAMESPACE
407
408#endif // QENDIAN_H
409

source code of qtbase/src/corelib/global/qendian.h