portability.h 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  1. /*
  2. * portability.h
  3. *
  4. */
  5. /**
  6. * All macros should be prefixed with either CROARING or ROARING.
  7. * The library uses both ROARING_...
  8. * as well as CROAIRING_ as prefixes. The ROARING_ prefix is for
  9. * macros that are provided by the build system or that are closely
  10. * related to the format. The header macros may also use ROARING_.
  11. * The CROARING_ prefix is for internal macros that a user is unlikely
  12. * to ever interact with.
  13. */
  14. #ifndef CROARING_INCLUDE_PORTABILITY_H_
  15. #define CROARING_INCLUDE_PORTABILITY_H_
  16. #ifndef _GNU_SOURCE
  17. #define _GNU_SOURCE 1
  18. #endif // _GNU_SOURCE
  19. #ifndef __STDC_FORMAT_MACROS
  20. #define __STDC_FORMAT_MACROS 1
  21. #endif // __STDC_FORMAT_MACROS
  22. #ifdef _MSC_VER
  23. #define CROARING_VISUAL_STUDIO 1
  24. /**
  25. * We want to differentiate carefully between
  26. * clang under visual studio and regular visual
  27. * studio.
  28. */
  29. #ifdef __clang__
  30. // clang under visual studio
  31. #define CROARING_CLANG_VISUAL_STUDIO 1
  32. #else
  33. // just regular visual studio (best guess)
  34. #define CROARING_REGULAR_VISUAL_STUDIO 1
  35. #endif // __clang__
  36. #endif // _MSC_VER
  37. #ifndef CROARING_VISUAL_STUDIO
  38. #define CROARING_VISUAL_STUDIO 0
  39. #endif
  40. #ifndef CROARING_CLANG_VISUAL_STUDIO
  41. #define CROARING_CLANG_VISUAL_STUDIO 0
  42. #endif
  43. #ifndef CROARING_REGULAR_VISUAL_STUDIO
  44. #define CROARING_REGULAR_VISUAL_STUDIO 0
  45. #endif
  46. #if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE < 200809L)
  47. #undef _POSIX_C_SOURCE
  48. #endif
  49. #ifndef _POSIX_C_SOURCE
  50. #define _POSIX_C_SOURCE 200809L
  51. #endif // !(defined(_POSIX_C_SOURCE)) || (_POSIX_C_SOURCE < 200809L)
  52. #if !(defined(_XOPEN_SOURCE)) || (_XOPEN_SOURCE < 700)
  53. #define _XOPEN_SOURCE 700
  54. #endif // !(defined(_XOPEN_SOURCE)) || (_XOPEN_SOURCE < 700)
  55. #ifdef __illumos__
  56. #define __EXTENSIONS__
  57. #endif
  58. #include <stdbool.h>
  59. #include <stdint.h>
  60. #include <stdlib.h> // will provide posix_memalign with _POSIX_C_SOURCE as defined above
  61. #ifdef __GLIBC__
  62. #include <malloc.h> // this should never be needed but there are some reports that it is needed.
  63. #endif
  64. #ifdef __cplusplus
  65. extern "C" { // portability definitions are in global scope, not a namespace
  66. #endif
  67. #if defined(__SIZEOF_LONG_LONG__) && __SIZEOF_LONG_LONG__ != 8
  68. #error This code assumes 64-bit long longs (by use of the GCC intrinsics). Your system is not currently supported.
  69. #endif
  70. #if CROARING_REGULAR_VISUAL_STUDIO
  71. #ifndef __restrict__
  72. #define __restrict__ __restrict
  73. #endif // __restrict__
  74. #endif // CROARING_REGULAR_VISUAL_STUDIO
  75. #if defined(__x86_64__) || defined(_M_X64)
  76. // we have an x64 processor
  77. #define CROARING_IS_X64 1
  78. #if defined(_MSC_VER) && (_MSC_VER < 1910)
  79. // Old visual studio systems won't support AVX2 well.
  80. #undef CROARING_IS_X64
  81. #endif
  82. #if defined(__clang_major__) && (__clang_major__ <= 8) && !defined(__AVX2__)
  83. // Older versions of clang have a bug affecting us
  84. // https://stackoverflow.com/questions/57228537/how-does-one-use-pragma-clang-attribute-push-with-c-namespaces
  85. #undef CROARING_IS_X64
  86. #endif
  87. #ifdef ROARING_DISABLE_X64
  88. #undef CROARING_IS_X64
  89. #endif
  90. // we include the intrinsic header
  91. #if !CROARING_REGULAR_VISUAL_STUDIO
  92. /* Non-Microsoft C/C++-compatible compiler */
  93. #include <x86intrin.h> // on some recent GCC, this will declare posix_memalign
  94. #if CROARING_CLANG_VISUAL_STUDIO
  95. /**
  96. * You are not supposed, normally, to include these
  97. * headers directly. Instead you should either include intrin.h
  98. * or x86intrin.h. However, when compiling with clang
  99. * under Windows (i.e., when _MSC_VER is set), these headers
  100. * only get included *if* the corresponding features are detected
  101. * from macros:
  102. * e.g., if __AVX2__ is set... in turn, we normally set these
  103. * macros by compiling against the corresponding architecture
  104. * (e.g., arch:AVX2, -mavx2, etc.) which compiles the whole
  105. * software with these advanced instructions. These headers would
  106. * normally guard against such usage, but we carefully included
  107. * <x86intrin.h> (or <intrin.h>) before, so the headers
  108. * are fooled.
  109. */
  110. // To avoid reordering imports:
  111. // clang-format off
  112. #include <bmiintrin.h> // for _blsr_u64
  113. #include <lzcntintrin.h> // for __lzcnt64
  114. #include <immintrin.h> // for most things (AVX2, AVX512, _popcnt64)
  115. #include <smmintrin.h>
  116. #include <tmmintrin.h>
  117. #include <avxintrin.h>
  118. #include <avx2intrin.h>
  119. #include <wmmintrin.h>
  120. #if _MSC_VER >= 1920
  121. // Important: we need the AVX-512 headers:
  122. #include <avx512fintrin.h>
  123. #include <avx512dqintrin.h>
  124. #include <avx512cdintrin.h>
  125. #include <avx512bwintrin.h>
  126. #include <avx512vlintrin.h>
  127. #include <avx512vbmiintrin.h>
  128. #include <avx512vbmi2intrin.h>
  129. #include <avx512vpopcntdqintrin.h>
  130. // clang-format on
  131. #endif // _MSC_VER >= 1920
  132. // unfortunately, we may not get _blsr_u64, but, thankfully, clang
  133. // has it as a macro.
  134. #ifndef _blsr_u64
  135. // we roll our own
  136. #define _blsr_u64(n) ((n - 1) & n)
  137. #endif // _blsr_u64
  138. #endif // SIMDJSON_CLANG_VISUAL_STUDIO
  139. #endif // CROARING_REGULAR_VISUAL_STUDIO
  140. #endif // defined(__x86_64__) || defined(_M_X64)
  141. #if !defined(CROARING_USENEON) && !defined(DISABLENEON) && defined(__ARM_NEON)
  142. #define CROARING_USENEON
  143. #endif
  144. #if defined(CROARING_USENEON)
  145. #include <arm_neon.h>
  146. #endif
  147. #if !CROARING_REGULAR_VISUAL_STUDIO
  148. /* Non-Microsoft C/C++-compatible compiler, assumes that it supports inline
  149. * assembly */
  150. #define CROARING_INLINE_ASM 1
  151. #endif // _MSC_VER
  152. #if CROARING_REGULAR_VISUAL_STUDIO
  153. /* Microsoft C/C++-compatible compiler */
  154. #include <intrin.h>
  155. #ifndef __clang__ // if one compiles with MSVC *with* clang, then these
  156. // intrinsics are defined!!!
  157. #define CROARING_INTRINSICS 1
  158. // sadly there is no way to check whether we are missing these intrinsics
  159. // specifically.
  160. /* wrappers for Visual Studio built-ins that look like gcc built-ins
  161. * __builtin_ctzll */
  162. /** result might be undefined when input_num is zero */
  163. inline int roaring_trailing_zeroes(unsigned long long input_num) {
  164. unsigned long index;
  165. #ifdef _WIN64 // highly recommended!!!
  166. _BitScanForward64(&index, input_num);
  167. #else // if we must support 32-bit Windows
  168. if ((uint32_t)input_num != 0) {
  169. _BitScanForward(&index, (uint32_t)input_num);
  170. } else {
  171. _BitScanForward(&index, (uint32_t)(input_num >> 32));
  172. index += 32;
  173. }
  174. #endif // _WIN64
  175. return index;
  176. }
  177. /* wrappers for Visual Studio built-ins that look like gcc built-ins
  178. * __builtin_clzll */
  179. /** result might be undefined when input_num is zero */
  180. inline int roaring_leading_zeroes(unsigned long long input_num) {
  181. unsigned long index;
  182. #ifdef _WIN64 // highly recommended!!!
  183. _BitScanReverse64(&index, input_num);
  184. #else // if we must support 32-bit Windows
  185. if (input_num > 0xFFFFFFFF) {
  186. _BitScanReverse(&index, (uint32_t)(input_num >> 32));
  187. index += 32;
  188. } else {
  189. _BitScanReverse(&index, (uint32_t)(input_num));
  190. }
  191. #endif // _WIN64
  192. return 63 - index;
  193. }
  194. /* Use #define so this is effective even under /Ob0 (no inline) */
  195. #define roaring_unreachable __assume(0)
  196. #endif // __clang__
  197. #endif // CROARING_REGULAR_VISUAL_STUDIO
  198. #ifndef CROARING_INTRINSICS
  199. #define CROARING_INTRINSICS 1
  200. #define roaring_unreachable __builtin_unreachable()
  201. /** result might be undefined when input_num is zero */
  202. inline int roaring_trailing_zeroes(unsigned long long input_num) {
  203. return __builtin_ctzll(input_num);
  204. }
  205. /** result might be undefined when input_num is zero */
  206. inline int roaring_leading_zeroes(unsigned long long input_num) {
  207. return __builtin_clzll(input_num);
  208. }
  209. #endif
  210. #if CROARING_REGULAR_VISUAL_STUDIO
  211. #define ALIGNED(x) __declspec(align(x))
  212. #elif defined(__GNUC__) || defined(__clang__)
  213. #define ALIGNED(x) __attribute__((aligned(x)))
  214. #else
  215. #warning "Warning. Unrecognized compiler."
  216. #define ALIGNED(x)
  217. #endif
  218. #if defined(__GNUC__) || defined(__clang__)
  219. #define CROARING_WARN_UNUSED __attribute__((warn_unused_result))
  220. #else
  221. #define CROARING_WARN_UNUSED
  222. #endif
  223. #define IS_BIG_ENDIAN (*(uint16_t *)"\0\xff" < 0x100)
  224. #ifdef CROARING_USENEON
  225. // we can always compute the popcount fast.
  226. #elif (defined(_M_ARM) || defined(_M_ARM64)) && \
  227. ((defined(_WIN64) || defined(_WIN32)) && \
  228. defined(CROARING_REGULAR_VISUAL_STUDIO) && \
  229. CROARING_REGULAR_VISUAL_STUDIO)
  230. // we will need this function:
  231. static inline int roaring_hamming_backup(uint64_t x) {
  232. uint64_t c1 = UINT64_C(0x5555555555555555);
  233. uint64_t c2 = UINT64_C(0x3333333333333333);
  234. uint64_t c4 = UINT64_C(0x0F0F0F0F0F0F0F0F);
  235. x -= (x >> 1) & c1;
  236. x = ((x >> 2) & c2) + (x & c2);
  237. x = (x + (x >> 4)) & c4;
  238. x *= UINT64_C(0x0101010101010101);
  239. return x >> 56;
  240. }
  241. #endif
  242. static inline int roaring_hamming(uint64_t x) {
  243. #if defined(_WIN64) && defined(CROARING_REGULAR_VISUAL_STUDIO) && \
  244. CROARING_REGULAR_VISUAL_STUDIO
  245. #ifdef CROARING_USENEON
  246. return vaddv_u8(vcnt_u8(vcreate_u8(input_num)));
  247. #elif defined(_M_ARM64)
  248. return roaring_hamming_backup(x);
  249. // (int) _CountOneBits64(x); is unavailable
  250. #else // _M_ARM64
  251. return (int)__popcnt64(x);
  252. #endif // _M_ARM64
  253. #elif defined(_WIN32) && defined(CROARING_REGULAR_VISUAL_STUDIO) && \
  254. CROARING_REGULAR_VISUAL_STUDIO
  255. #ifdef _M_ARM
  256. return roaring_hamming_backup(x);
  257. // _CountOneBits is unavailable
  258. #else // _M_ARM
  259. return (int)__popcnt((unsigned int)x) +
  260. (int)__popcnt((unsigned int)(x >> 32));
  261. #endif // _M_ARM
  262. #else
  263. return __builtin_popcountll(x);
  264. #endif
  265. }
  266. #ifndef UINT64_C
  267. #define UINT64_C(c) (c##ULL)
  268. #endif // UINT64_C
  269. #ifndef UINT32_C
  270. #define UINT32_C(c) (c##UL)
  271. #endif // UINT32_C
  272. #ifdef __cplusplus
  273. } // extern "C" {
  274. #endif // __cplusplus
  275. // this is almost standard?
  276. #undef STRINGIFY_IMPLEMENTATION_
  277. #undef STRINGIFY
  278. #define STRINGIFY_IMPLEMENTATION_(a) #a
  279. #define STRINGIFY(a) STRINGIFY_IMPLEMENTATION_(a)
  280. // Our fast kernels require 64-bit systems.
  281. //
  282. // On 32-bit x86, we lack 64-bit popcnt, lzcnt, blsr instructions.
  283. // Furthermore, the number of SIMD registers is reduced.
  284. //
  285. // On 32-bit ARM, we would have smaller registers.
  286. //
  287. // The library should still have the fallback kernel. It is
  288. // slower, but it should run everywhere.
  289. //
  290. // Enable valid runtime implementations, and select
  291. // CROARING_BUILTIN_IMPLEMENTATION
  292. //
  293. // We are going to use runtime dispatch.
  294. #if CROARING_IS_X64
  295. #ifdef __clang__
  296. // clang does not have GCC push pop
  297. // warning: clang attribute push can't be used within a namespace in clang up
  298. // til 8.0 so CROARING_TARGET_REGION and CROARING_UNTARGET_REGION must be
  299. // *outside* of a namespace.
  300. #define CROARING_TARGET_REGION(T) \
  301. _Pragma(STRINGIFY(clang attribute push(__attribute__((target(T))), \
  302. apply_to = function)))
  303. #define CROARING_UNTARGET_REGION _Pragma("clang attribute pop")
  304. #elif defined(__GNUC__)
  305. // GCC is easier
  306. #define CROARING_TARGET_REGION(T) \
  307. _Pragma("GCC push_options") _Pragma(STRINGIFY(GCC target(T)))
  308. #define CROARING_UNTARGET_REGION _Pragma("GCC pop_options")
  309. #endif // clang then gcc
  310. #endif // CROARING_IS_X64
  311. // Default target region macros don't do anything.
  312. #ifndef CROARING_TARGET_REGION
  313. #define CROARING_TARGET_REGION(T)
  314. #define CROARING_UNTARGET_REGION
  315. #endif
  316. #define CROARING_TARGET_AVX2 \
  317. CROARING_TARGET_REGION("avx2,bmi,pclmul,lzcnt,popcnt")
  318. #define CROARING_TARGET_AVX512 \
  319. CROARING_TARGET_REGION( \
  320. "avx2,bmi,bmi2,pclmul,lzcnt,popcnt,avx512f,avx512dq,avx512bw," \
  321. "avx512vbmi2,avx512bitalg,avx512vpopcntdq")
  322. #define CROARING_UNTARGET_AVX2 CROARING_UNTARGET_REGION
  323. #define CROARING_UNTARGET_AVX512 CROARING_UNTARGET_REGION
  324. #ifdef __AVX2__
  325. // No need for runtime dispatching.
  326. // It is unnecessary and harmful to old clang to tag regions.
  327. #undef CROARING_TARGET_AVX2
  328. #define CROARING_TARGET_AVX2
  329. #undef CROARING_UNTARGET_AVX2
  330. #define CROARING_UNTARGET_AVX2
  331. #endif
  332. #if defined(__AVX512F__) && defined(__AVX512DQ__) && defined(__AVX512BW__) && \
  333. defined(__AVX512VBMI2__) && defined(__AVX512BITALG__) && \
  334. defined(__AVX512VPOPCNTDQ__)
  335. // No need for runtime dispatching.
  336. // It is unnecessary and harmful to old clang to tag regions.
  337. #undef CROARING_TARGET_AVX512
  338. #define CROARING_TARGET_AVX512
  339. #undef CROARING_UNTARGET_AVX512
  340. #define CROARING_UNTARGET_AVX512
  341. #endif
  342. // Allow unaligned memory access
  343. #if defined(__GNUC__) || defined(__clang__)
  344. #define ALLOW_UNALIGNED __attribute__((no_sanitize("alignment")))
  345. #else
  346. #define ALLOW_UNALIGNED
  347. #endif
  348. #if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__)
  349. #define CROARING_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
  350. #elif defined(_WIN32)
  351. #define CROARING_IS_BIG_ENDIAN 0
  352. #else
  353. #if defined(__APPLE__) || \
  354. defined(__FreeBSD__) // defined __BYTE_ORDER__ && defined
  355. // __ORDER_BIG_ENDIAN__
  356. #include <machine/endian.h>
  357. #elif defined(sun) || \
  358. defined(__sun) // defined(__APPLE__) || defined(__FreeBSD__)
  359. #error #include <sys/byteorder.h>
  360. #else // defined(__APPLE__) || defined(__FreeBSD__)
  361. #ifdef __has_include
  362. #if __has_include(<endian.h>)
  363. #include <endian.h>
  364. #endif //__has_include(<endian.h>)
  365. #endif //__has_include
  366. #endif // defined(__APPLE__) || defined(__FreeBSD__)
  367. #ifndef !defined(__BYTE_ORDER__) || !defined(__ORDER_LITTLE_ENDIAN__)
  368. #define CROARING_IS_BIG_ENDIAN 0
  369. #endif
  370. #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
  371. #define CROARING_IS_BIG_ENDIAN 0
  372. #else // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
  373. #define CROARING_IS_BIG_ENDIAN 1
  374. #endif // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
  375. #endif
  376. // Host <-> big endian conversion.
  377. #if CROARING_IS_BIG_ENDIAN
  378. #define croaring_htobe64(x) (x)
  379. #elif defined(_WIN32) || defined(_WIN64) // CROARING_IS_BIG_ENDIAN
  380. #include <stdlib.h>
  381. #define croaring_htobe64(x) _byteswap_uint64(x)
  382. #elif defined(__APPLE__) // CROARING_IS_BIG_ENDIAN
  383. #include <libkern/OSByteOrder.h>
  384. #define croaring_htobe64(x) OSSwapInt64(x)
  385. #elif defined(__has_include) && \
  386. __has_include( \
  387. <byteswap.h>) && (defined(__linux__) || defined(__FreeBSD__)) // CROARING_IS_BIG_ENDIAN
  388. #include <byteswap.h>
  389. #if defined(__linux__)
  390. #define croaring_htobe64(x) bswap_64(x)
  391. #elif defined(__FreeBSD__)
  392. #define croaring_htobe64(x) bswap64(x)
  393. #else
  394. #warning "Unknown platform, report as an error"
  395. #endif
  396. #else // CROARING_IS_BIG_ENDIAN
  397. // Gets compiled to bswap or equivalent on most compilers.
  398. #define croaring_htobe64(x) \
  399. (((x & 0x00000000000000FFULL) << 56) | \
  400. ((x & 0x000000000000FF00ULL) << 40) | \
  401. ((x & 0x0000000000FF0000ULL) << 24) | \
  402. ((x & 0x00000000FF000000ULL) << 8) | ((x & 0x000000FF00000000ULL) >> 8) | \
  403. ((x & 0x0000FF0000000000ULL) >> 24) | \
  404. ((x & 0x00FF000000000000ULL) >> 40) | \
  405. ((x & 0xFF00000000000000ULL) >> 56))
  406. #endif // CROARING_IS_BIG_ENDIAN
  407. #define croaring_be64toh(x) croaring_htobe64(x)
  408. // End of host <-> big endian conversion.
  409. // Defines for the possible CROARING atomic implementations
  410. #define CROARING_ATOMIC_IMPL_NONE 1
  411. #define CROARING_ATOMIC_IMPL_CPP 2
  412. #define CROARING_ATOMIC_IMPL_C 3
  413. #define CROARING_ATOMIC_IMPL_C_WINDOWS 4
  414. // If the use has forced a specific implementation, use that, otherwise,
  415. // figure out the best implementation we can use.
  416. #if !defined(CROARING_ATOMIC_IMPL)
  417. #if defined(__cplusplus) && __cplusplus >= 201103L
  418. #ifdef __has_include
  419. #if __has_include(<atomic>)
  420. #define CROARING_ATOMIC_IMPL CROARING_ATOMIC_IMPL_CPP
  421. #endif //__has_include(<atomic>)
  422. #else
  423. // We lack __has_include to check:
  424. #define CROARING_ATOMIC_IMPL CROARING_ATOMIC_IMPL_CPP
  425. #endif //__has_include
  426. #elif __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__)
  427. #define CROARING_ATOMIC_IMPL CROARING_ATOMIC_IMPL_C
  428. #elif CROARING_REGULAR_VISUAL_STUDIO
  429. // https://www.technetworkhub.com/c11-atomics-in-visual-studio-2022-version-17/
  430. #define CROARING_ATOMIC_IMPL CROARING_ATOMIC_IMPL_C_WINDOWS
  431. #endif
  432. #endif // !defined(CROARING_ATOMIC_IMPL)
  433. #if CROARING_ATOMIC_IMPL == CROARING_ATOMIC_IMPL_C
  434. #include <stdatomic.h>
  435. typedef _Atomic(uint32_t) croaring_refcount_t;
  436. static inline void croaring_refcount_inc(croaring_refcount_t *val) {
  437. // Increasing the reference counter can always be done with
  438. // memory_order_relaxed: New references to an object can only be formed from
  439. // an existing reference, and passing an existing reference from one thread
  440. // to another must already provide any required synchronization.
  441. atomic_fetch_add_explicit(val, 1, memory_order_relaxed);
  442. }
  443. static inline bool croaring_refcount_dec(croaring_refcount_t *val) {
  444. // It is important to enforce any possible access to the object in one
  445. // thread (through an existing reference) to happen before deleting the
  446. // object in a different thread. This is achieved by a "release" operation
  447. // after dropping a reference (any access to the object through this
  448. // reference must obviously happened before), and an "acquire" operation
  449. // before deleting the object.
  450. bool is_zero = atomic_fetch_sub_explicit(val, 1, memory_order_release) == 1;
  451. if (is_zero) {
  452. atomic_thread_fence(memory_order_acquire);
  453. }
  454. return is_zero;
  455. }
  456. static inline uint32_t croaring_refcount_get(const croaring_refcount_t *val) {
  457. return atomic_load_explicit(val, memory_order_relaxed);
  458. }
  459. #elif CROARING_ATOMIC_IMPL == CROARING_ATOMIC_IMPL_CPP
  460. #include <atomic>
  461. typedef std::atomic<uint32_t> croaring_refcount_t;
  462. static inline void croaring_refcount_inc(croaring_refcount_t *val) {
  463. val->fetch_add(1, std::memory_order_relaxed);
  464. }
  465. static inline bool croaring_refcount_dec(croaring_refcount_t *val) {
  466. // See above comments on the c11 atomic implementation for memory ordering
  467. bool is_zero = val->fetch_sub(1, std::memory_order_release) == 1;
  468. if (is_zero) {
  469. std::atomic_thread_fence(std::memory_order_acquire);
  470. }
  471. return is_zero;
  472. }
  473. static inline uint32_t croaring_refcount_get(const croaring_refcount_t *val) {
  474. return val->load(std::memory_order_relaxed);
  475. }
  476. #elif CROARING_ATOMIC_IMPL == CROARING_ATOMIC_IMPL_C_WINDOWS
  477. #include <intrin.h>
  478. #pragma intrinsic(_InterlockedIncrement)
  479. #pragma intrinsic(_InterlockedDecrement)
  480. // _InterlockedIncrement and _InterlockedDecrement take a (signed) long, and
  481. // overflow is defined to wrap, so we can pretend it is a uint32_t for our case
  482. typedef volatile long croaring_refcount_t;
  483. static inline void croaring_refcount_inc(croaring_refcount_t *val) {
  484. _InterlockedIncrement(val);
  485. }
  486. static inline bool croaring_refcount_dec(croaring_refcount_t *val) {
  487. return _InterlockedDecrement(val) == 0;
  488. }
  489. static inline uint32_t croaring_refcount_get(const croaring_refcount_t *val) {
  490. // Per
  491. // https://learn.microsoft.com/en-us/windows/win32/sync/interlocked-variable-access
  492. // > Simple reads and writes to properly-aligned 32-bit variables are atomic
  493. // > operations. In other words, you will not end up with only one portion
  494. // > of the variable updated; all bits are updated in an atomic fashion.
  495. return *val;
  496. }
  497. #elif CROARING_ATOMIC_IMPL == CROARING_ATOMIC_IMPL_NONE
  498. #include <assert.h>
  499. typedef uint32_t croaring_refcount_t;
  500. static inline void croaring_refcount_inc(croaring_refcount_t *val) {
  501. *val += 1;
  502. }
  503. static inline bool croaring_refcount_dec(croaring_refcount_t *val) {
  504. assert(*val > 0);
  505. *val -= 1;
  506. return val == 0;
  507. }
  508. static inline uint32_t croaring_refcount_get(const croaring_refcount_t *val) {
  509. return *val;
  510. }
  511. #else
  512. #error "Unknown atomic implementation"
  513. #endif
  514. #if defined(__GNUC__) || defined(__clang__)
  515. #define CROARING_DEPRECATED __attribute__((deprecated))
  516. #elif defined(_MSC_VER)
  517. #define CROARING_DEPRECATED __declspec(deprecated)
  518. #else
  519. #define CROARING_DEPRECATED
  520. #endif // defined(__GNUC__) || defined(__clang__)
  521. // We want to initialize structs to zero portably (C and C++), without
  522. // warnings. We can do mystruct s = CROARING_ZERO_INITIALIZER;
  523. #if __cplusplus
  524. #define CROARING_ZERO_INITIALIZER \
  525. {}
  526. #else
  527. #define CROARING_ZERO_INITIALIZER \
  528. { 0 }
  529. #endif
  530. // We need portability.h to be included first,
  531. // but we also always want isadetection.h to be
  532. // included (right after).
  533. // See https://github.com/RoaringBitmap/CRoaring/issues/394
  534. // There is no scenario where we want portability.h to
  535. // be included, but not isadetection.h: the latter is a
  536. // strict requirement.
  537. #include <roaring/isadetection.h> // include it last!
  538. #endif /* INCLUDE_PORTABILITY_H_ */