1 ///////////////////////////////////////////////////////////////////////////////
3 /// \file tuklib_integer.h
4 /// \brief Various integer and bit operations
6 /// This file provides macros or functions to do some basic integer and bit
9 /// Native endian inline functions (XX = 16, 32, or 64):
10 /// - Unaligned native endian reads: readXXne(ptr)
11 /// - Unaligned native endian writes: writeXXne(ptr, num)
12 /// - Aligned native endian reads: aligned_readXXne(ptr)
13 /// - Aligned native endian writes: aligned_writeXXne(ptr, num)
15 /// Endianness-converting integer operations (these can be macros!)
16 /// (XX = 16, 32, or 64; Y = b or l):
17 /// - Byte swapping: bswapXX(num)
18 /// - Byte order conversions to/from native (byteswaps if Y isn't
19 /// the native endianness): convXXYe(num)
20 /// - Unaligned reads: readXXYe(ptr)
21 /// - Unaligned writes: writeXXYe(ptr, num)
22 /// - Aligned reads: aligned_readXXYe(ptr)
23 /// - Aligned writes: aligned_writeXXYe(ptr, num)
25 /// Since the above can macros, the arguments should have no side effects
26 /// because they may be evaluated more than once.
28 /// Bit scan operations for non-zero 32-bit integers (inline functions):
29 /// - Bit scan reverse (find highest non-zero bit): bsr32(num)
30 /// - Count leading zeros: clz32(num)
31 /// - Count trailing zeros: ctz32(num)
32 /// - Bit scan forward (simply an alias for ctz32()): bsf32(num)
34 /// The above bit scan operations return 0-31. If num is zero,
35 /// the result is undefined.
37 // Authors: Lasse Collin
40 // This file has been put into the public domain.
41 // You can do whatever you want with this file.
43 ///////////////////////////////////////////////////////////////////////////////
45 #ifndef TUKLIB_INTEGER_H
46 #define TUKLIB_INTEGER_H
48 #include "tuklib_common.h"
51 // Newer Intel C compilers require immintrin.h for _bit_scan_reverse()
52 // and such functions.
53 #if defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 1500)
54 # include <immintrin.h>
55 // Only include <intrin.h> when it is needed. GCC and Clang can both
56 // use __builtin's, so we only need Windows instrincs when using MSVC.
57 // GCC and Clang can set _MSC_VER on Windows, so we need to exclude these
59 #elif defined(_MSC_VER) && !TUKLIB_GNUC_REQ(3, 4) && !defined(__clang__)
68 #if defined(HAVE___BUILTIN_BSWAPXX)
69 // GCC >= 4.8 and Clang
70 # define bswap16(n) __builtin_bswap16(n)
71 # define bswap32(n) __builtin_bswap32(n)
72 # define bswap64(n) __builtin_bswap64(n)
74 #elif defined(HAVE_BYTESWAP_H)
75 // glibc, uClibc, dietlibc
76 # include <byteswap.h>
78 # define bswap16(num) bswap_16(num)
81 # define bswap32(num) bswap_32(num)
84 # define bswap64(num) bswap_64(num)
87 #elif defined(HAVE_SYS_ENDIAN_H)
89 # include <sys/endian.h>
91 #elif defined(HAVE_SYS_BYTEORDER_H)
93 # include <sys/byteorder.h>
95 # define bswap16(num) BSWAP_16(num)
98 # define bswap32(num) BSWAP_32(num)
101 # define bswap64(num) BSWAP_64(num)
104 # define conv16be(num) BE_16(num)
107 # define conv32be(num) BE_32(num)
110 # define conv64be(num) BE_64(num)
113 # define conv16le(num) LE_16(num)
116 # define conv32le(num) LE_32(num)
119 # define conv64le(num) LE_64(num)
124 # define bswap16(n) (uint16_t)( \
125 (((n) & 0x00FFU) << 8) \
126 | (((n) & 0xFF00U) >> 8) \
131 # define bswap32(n) (uint32_t)( \
132 (((n) & UINT32_C(0x000000FF)) << 24) \
133 | (((n) & UINT32_C(0x0000FF00)) << 8) \
134 | (((n) & UINT32_C(0x00FF0000)) >> 8) \
135 | (((n) & UINT32_C(0xFF000000)) >> 24) \
140 # define bswap64(n) (uint64_t)( \
141 (((n) & UINT64_C(0x00000000000000FF)) << 56) \
142 | (((n) & UINT64_C(0x000000000000FF00)) << 40) \
143 | (((n) & UINT64_C(0x0000000000FF0000)) << 24) \
144 | (((n) & UINT64_C(0x00000000FF000000)) << 8) \
145 | (((n) & UINT64_C(0x000000FF00000000)) >> 8) \
146 | (((n) & UINT64_C(0x0000FF0000000000)) >> 24) \
147 | (((n) & UINT64_C(0x00FF000000000000)) >> 40) \
148 | (((n) & UINT64_C(0xFF00000000000000)) >> 56) \
152 // Define conversion macros using the basic byte swapping macros.
153 #ifdef WORDS_BIGENDIAN
155 # define conv16be(num) ((uint16_t)(num))
158 # define conv32be(num) ((uint32_t)(num))
161 # define conv64be(num) ((uint64_t)(num))
164 # define conv16le(num) bswap16(num)
167 # define conv32le(num) bswap32(num)
170 # define conv64le(num) bswap64(num)
174 # define conv16be(num) bswap16(num)
177 # define conv32be(num) bswap32(num)
180 # define conv64be(num) bswap64(num)
183 # define conv16le(num) ((uint16_t)(num))
186 # define conv32le(num) ((uint32_t)(num))
189 # define conv64le(num) ((uint64_t)(num))
194 ////////////////////////////////
195 // Unaligned reads and writes //
196 ////////////////////////////////
198 // The traditional way of casting e.g. *(const uint16_t *)uint8_pointer
199 // is bad even if the uint8_pointer is properly aligned because this kind
200 // of casts break strict aliasing rules and result in undefined behavior.
201 // With unaligned pointers it's even worse: compilers may emit vector
202 // instructions that require aligned pointers even if non-vector
203 // instructions work with unaligned pointers.
205 // Using memcpy() is the standard compliant way to do unaligned access.
206 // Many modern compilers inline it so there is no function call overhead.
207 // For those compilers that don't handle the memcpy() method well, the
208 // old casting method (that violates strict aliasing) can be requested at
209 // build time. A third method, casting to a packed struct, would also be
210 // an option but isn't provided to keep things simpler (it's already a mess).
211 // Hopefully this is flexible enough in practice.
213 static inline uint16_t
214 read16ne(const uint8_t *buf
)
216 #if defined(TUKLIB_FAST_UNALIGNED_ACCESS) \
217 && defined(TUKLIB_USE_UNSAFE_TYPE_PUNNING)
218 return *(const uint16_t *)buf
;
221 memcpy(&num
, buf
, sizeof(num
));
227 static inline uint32_t
228 read32ne(const uint8_t *buf
)
230 #if defined(TUKLIB_FAST_UNALIGNED_ACCESS) \
231 && defined(TUKLIB_USE_UNSAFE_TYPE_PUNNING)
232 return *(const uint32_t *)buf
;
235 memcpy(&num
, buf
, sizeof(num
));
241 static inline uint64_t
242 read64ne(const uint8_t *buf
)
244 #if defined(TUKLIB_FAST_UNALIGNED_ACCESS) \
245 && defined(TUKLIB_USE_UNSAFE_TYPE_PUNNING)
246 return *(const uint64_t *)buf
;
249 memcpy(&num
, buf
, sizeof(num
));
256 write16ne(uint8_t *buf
, uint16_t num
)
258 #if defined(TUKLIB_FAST_UNALIGNED_ACCESS) \
259 && defined(TUKLIB_USE_UNSAFE_TYPE_PUNNING)
260 *(uint16_t *)buf
= num
;
262 memcpy(buf
, &num
, sizeof(num
));
269 write32ne(uint8_t *buf
, uint32_t num
)
271 #if defined(TUKLIB_FAST_UNALIGNED_ACCESS) \
272 && defined(TUKLIB_USE_UNSAFE_TYPE_PUNNING)
273 *(uint32_t *)buf
= num
;
275 memcpy(buf
, &num
, sizeof(num
));
282 write64ne(uint8_t *buf
, uint64_t num
)
284 #if defined(TUKLIB_FAST_UNALIGNED_ACCESS) \
285 && defined(TUKLIB_USE_UNSAFE_TYPE_PUNNING)
286 *(uint64_t *)buf
= num
;
288 memcpy(buf
, &num
, sizeof(num
));
294 static inline uint16_t
295 read16be(const uint8_t *buf
)
297 #if defined(WORDS_BIGENDIAN) || defined(TUKLIB_FAST_UNALIGNED_ACCESS)
298 uint16_t num
= read16ne(buf
);
299 return conv16be(num
);
301 uint16_t num
= ((uint16_t)buf
[0] << 8) | (uint16_t)buf
[1];
307 static inline uint16_t
308 read16le(const uint8_t *buf
)
310 #if !defined(WORDS_BIGENDIAN) || defined(TUKLIB_FAST_UNALIGNED_ACCESS)
311 uint16_t num
= read16ne(buf
);
312 return conv16le(num
);
314 uint16_t num
= ((uint16_t)buf
[0]) | ((uint16_t)buf
[1] << 8);
320 static inline uint32_t
321 read32be(const uint8_t *buf
)
323 #if defined(WORDS_BIGENDIAN) || defined(TUKLIB_FAST_UNALIGNED_ACCESS)
324 uint32_t num
= read32ne(buf
);
325 return conv32be(num
);
327 uint32_t num
= (uint32_t)buf
[0] << 24;
328 num
|= (uint32_t)buf
[1] << 16;
329 num
|= (uint32_t)buf
[2] << 8;
330 num
|= (uint32_t)buf
[3];
336 static inline uint32_t
337 read32le(const uint8_t *buf
)
339 #if !defined(WORDS_BIGENDIAN) || defined(TUKLIB_FAST_UNALIGNED_ACCESS)
340 uint32_t num
= read32ne(buf
);
341 return conv32le(num
);
343 uint32_t num
= (uint32_t)buf
[0];
344 num
|= (uint32_t)buf
[1] << 8;
345 num
|= (uint32_t)buf
[2] << 16;
346 num
|= (uint32_t)buf
[3] << 24;
352 static inline uint64_t
353 read64be(const uint8_t *buf
)
355 #if defined(WORDS_BIGENDIAN) || defined(TUKLIB_FAST_UNALIGNED_ACCESS)
356 uint64_t num
= read64ne(buf
);
357 return conv64be(num
);
359 uint64_t num
= (uint64_t)buf
[0] << 56;
360 num
|= (uint64_t)buf
[1] << 48;
361 num
|= (uint64_t)buf
[2] << 40;
362 num
|= (uint64_t)buf
[3] << 32;
363 num
|= (uint64_t)buf
[4] << 24;
364 num
|= (uint64_t)buf
[5] << 16;
365 num
|= (uint64_t)buf
[6] << 8;
366 num
|= (uint64_t)buf
[7];
372 static inline uint64_t
373 read64le(const uint8_t *buf
)
375 #if !defined(WORDS_BIGENDIAN) || defined(TUKLIB_FAST_UNALIGNED_ACCESS)
376 uint64_t num
= read64ne(buf
);
377 return conv64le(num
);
379 uint64_t num
= (uint64_t)buf
[0];
380 num
|= (uint64_t)buf
[1] << 8;
381 num
|= (uint64_t)buf
[2] << 16;
382 num
|= (uint64_t)buf
[3] << 24;
383 num
|= (uint64_t)buf
[4] << 32;
384 num
|= (uint64_t)buf
[5] << 40;
385 num
|= (uint64_t)buf
[6] << 48;
386 num
|= (uint64_t)buf
[7] << 56;
392 // NOTE: Possible byte swapping must be done in a macro to allow the compiler
393 // to optimize byte swapping of constants when using glibc's or *BSD's
394 // byte swapping macros. The actual write is done in an inline function
395 // to make type checking of the buf pointer possible.
396 #if defined(WORDS_BIGENDIAN) || defined(TUKLIB_FAST_UNALIGNED_ACCESS)
397 # define write16be(buf, num) write16ne(buf, conv16be(num))
398 # define write32be(buf, num) write32ne(buf, conv32be(num))
399 # define write64be(buf, num) write64ne(buf, conv64be(num))
402 #if !defined(WORDS_BIGENDIAN) || defined(TUKLIB_FAST_UNALIGNED_ACCESS)
403 # define write16le(buf, num) write16ne(buf, conv16le(num))
404 # define write32le(buf, num) write32ne(buf, conv32le(num))
405 # define write64le(buf, num) write64ne(buf, conv64le(num))
411 write16be(uint8_t *buf
, uint16_t num
)
413 buf
[0] = (uint8_t)(num
>> 8);
414 buf
[1] = (uint8_t)num
;
422 write16le(uint8_t *buf
, uint16_t num
)
424 buf
[0] = (uint8_t)num
;
425 buf
[1] = (uint8_t)(num
>> 8);
433 write32be(uint8_t *buf
, uint32_t num
)
435 buf
[0] = (uint8_t)(num
>> 24);
436 buf
[1] = (uint8_t)(num
>> 16);
437 buf
[2] = (uint8_t)(num
>> 8);
438 buf
[3] = (uint8_t)num
;
446 write32le(uint8_t *buf
, uint32_t num
)
448 buf
[0] = (uint8_t)num
;
449 buf
[1] = (uint8_t)(num
>> 8);
450 buf
[2] = (uint8_t)(num
>> 16);
451 buf
[3] = (uint8_t)(num
>> 24);
457 //////////////////////////////
458 // Aligned reads and writes //
459 //////////////////////////////
461 // Separate functions for aligned reads and writes are provided since on
462 // strict-align archs aligned access is much faster than unaligned access.
464 // Just like in the unaligned case, memcpy() is needed to avoid
465 // strict aliasing violations. However, on archs that don't support
466 // unaligned access the compiler cannot know that the pointers given
467 // to memcpy() are aligned which results in slow code. As of C11 there is
468 // no standard way to tell the compiler that we know that the address is
469 // aligned but some compilers have language extensions to do that. With
470 // such language extensions the memcpy() method gives excellent results.
472 // What to do on a strict-align system when no known language extentensions
473 // are available? Falling back to byte-by-byte access would be safe but ruin
474 // optimizations that have been made specifically with aligned access in mind.
475 // As a compromise, aligned reads will fall back to non-compliant type punning
476 // but aligned writes will be byte-by-byte, that is, fast reads are preferred
477 // over fast writes. This obviously isn't great but hopefully it's a working
478 // compromise for now.
480 // __builtin_assume_aligned is support by GCC >= 4.7 and clang >= 3.6.
481 #ifdef HAVE___BUILTIN_ASSUME_ALIGNED
482 # define tuklib_memcpy_aligned(dest, src, size) \
483 memcpy(dest, __builtin_assume_aligned(src, size), size)
485 # define tuklib_memcpy_aligned(dest, src, size) \
486 memcpy(dest, src, size)
487 # ifndef TUKLIB_FAST_UNALIGNED_ACCESS
488 # define TUKLIB_USE_UNSAFE_ALIGNED_READS 1
493 static inline uint16_t
494 aligned_read16ne(const uint8_t *buf
)
496 #if defined(TUKLIB_USE_UNSAFE_TYPE_PUNNING) \
497 || defined(TUKLIB_USE_UNSAFE_ALIGNED_READS)
498 return *(const uint16_t *)buf
;
501 tuklib_memcpy_aligned(&num
, buf
, sizeof(num
));
507 static inline uint32_t
508 aligned_read32ne(const uint8_t *buf
)
510 #if defined(TUKLIB_USE_UNSAFE_TYPE_PUNNING) \
511 || defined(TUKLIB_USE_UNSAFE_ALIGNED_READS)
512 return *(const uint32_t *)buf
;
515 tuklib_memcpy_aligned(&num
, buf
, sizeof(num
));
521 static inline uint64_t
522 aligned_read64ne(const uint8_t *buf
)
524 #if defined(TUKLIB_USE_UNSAFE_TYPE_PUNNING) \
525 || defined(TUKLIB_USE_UNSAFE_ALIGNED_READS)
526 return *(const uint64_t *)buf
;
529 tuklib_memcpy_aligned(&num
, buf
, sizeof(num
));
536 aligned_write16ne(uint8_t *buf
, uint16_t num
)
538 #ifdef TUKLIB_USE_UNSAFE_TYPE_PUNNING
539 *(uint16_t *)buf
= num
;
541 tuklib_memcpy_aligned(buf
, &num
, sizeof(num
));
548 aligned_write32ne(uint8_t *buf
, uint32_t num
)
550 #ifdef TUKLIB_USE_UNSAFE_TYPE_PUNNING
551 *(uint32_t *)buf
= num
;
553 tuklib_memcpy_aligned(buf
, &num
, sizeof(num
));
560 aligned_write64ne(uint8_t *buf
, uint64_t num
)
562 #ifdef TUKLIB_USE_UNSAFE_TYPE_PUNNING
563 *(uint64_t *)buf
= num
;
565 tuklib_memcpy_aligned(buf
, &num
, sizeof(num
));
571 static inline uint16_t
572 aligned_read16be(const uint8_t *buf
)
574 uint16_t num
= aligned_read16ne(buf
);
575 return conv16be(num
);
579 static inline uint16_t
580 aligned_read16le(const uint8_t *buf
)
582 uint16_t num
= aligned_read16ne(buf
);
583 return conv16le(num
);
587 static inline uint32_t
588 aligned_read32be(const uint8_t *buf
)
590 uint32_t num
= aligned_read32ne(buf
);
591 return conv32be(num
);
595 static inline uint32_t
596 aligned_read32le(const uint8_t *buf
)
598 uint32_t num
= aligned_read32ne(buf
);
599 return conv32le(num
);
603 static inline uint64_t
604 aligned_read64be(const uint8_t *buf
)
606 uint64_t num
= aligned_read64ne(buf
);
607 return conv64be(num
);
611 static inline uint64_t
612 aligned_read64le(const uint8_t *buf
)
614 uint64_t num
= aligned_read64ne(buf
);
615 return conv64le(num
);
619 // These need to be macros like in the unaligned case.
620 #define aligned_write16be(buf, num) aligned_write16ne((buf), conv16be(num))
621 #define aligned_write16le(buf, num) aligned_write16ne((buf), conv16le(num))
622 #define aligned_write32be(buf, num) aligned_write32ne((buf), conv32be(num))
623 #define aligned_write32le(buf, num) aligned_write32ne((buf), conv32le(num))
624 #define aligned_write64be(buf, num) aligned_write64ne((buf), conv64be(num))
625 #define aligned_write64le(buf, num) aligned_write64ne((buf), conv64le(num))
632 static inline uint32_t
635 // Check for ICC first, since it tends to define __GNUC__ too.
636 #if defined(__INTEL_COMPILER)
637 return _bit_scan_reverse(n
);
639 #elif (TUKLIB_GNUC_REQ(3, 4) || defined(__clang__)) && UINT_MAX == UINT32_MAX
640 // GCC >= 3.4 has __builtin_clz(), which gives good results on
641 // multiple architectures. On x86, __builtin_clz() ^ 31U becomes
642 // either plain BSR (so the XOR gets optimized away) or LZCNT and
643 // XOR (if -march indicates that SSE4a instructions are supported).
644 return (uint32_t)__builtin_clz(n
) ^ 31U;
646 #elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
648 __asm__("bsrl %1, %0" : "=r" (i
) : "rm" (n
));
651 #elif defined(_MSC_VER)
653 _BitScanReverse(&i
, n
);
659 if ((n
& 0xFFFF0000) == 0) {
664 if ((n
& 0xFF000000) == 0) {
669 if ((n
& 0xF0000000) == 0) {
674 if ((n
& 0xC0000000) == 0) {
679 if ((n
& 0x80000000) == 0)
687 static inline uint32_t
690 #if defined(__INTEL_COMPILER)
691 return _bit_scan_reverse(n
) ^ 31U;
693 #elif (TUKLIB_GNUC_REQ(3, 4) || defined(__clang__)) && UINT_MAX == UINT32_MAX
694 return (uint32_t)__builtin_clz(n
);
696 #elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
698 __asm__("bsrl %1, %0\n\t"
700 : "=r" (i
) : "rm" (n
));
703 #elif defined(_MSC_VER)
705 _BitScanReverse(&i
, n
);
711 if ((n
& 0xFFFF0000) == 0) {
716 if ((n
& 0xFF000000) == 0) {
721 if ((n
& 0xF0000000) == 0) {
726 if ((n
& 0xC0000000) == 0) {
731 if ((n
& 0x80000000) == 0)
739 static inline uint32_t
742 #if defined(__INTEL_COMPILER)
743 return _bit_scan_forward(n
);
745 #elif (TUKLIB_GNUC_REQ(3, 4) || defined(__clang__)) && UINT_MAX >= UINT32_MAX
746 return (uint32_t)__builtin_ctz(n
);
748 #elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
750 __asm__("bsfl %1, %0" : "=r" (i
) : "rm" (n
));
753 #elif defined(_MSC_VER)
755 _BitScanForward(&i
, n
);
761 if ((n
& 0x0000FFFF) == 0) {
766 if ((n
& 0x000000FF) == 0) {
771 if ((n
& 0x0000000F) == 0) {
776 if ((n
& 0x00000003) == 0) {
781 if ((n
& 0x00000001) == 0)