RapidFuzz
Loading...
Searching...
No Matches
intrinsics.hpp
1/* SPDX-License-Identifier: MIT */
2/* Copyright © 2021 Max Bachmann */
3
4#pragma once
5
6#include <bitset>
7#include <cassert>
8#include <cstddef>
9#include <limits>
10#include <rapidfuzz/details/config.hpp>
11#include <stdint.h>
12#include <type_traits>
13
14#if defined(_MSC_VER) && !defined(__clang__)
15# include <intrin.h>
16#endif
17
18namespace rapidfuzz {
19namespace detail {
20
21template <typename T>
22T bit_mask_lsb(size_t n)
23{
24 T mask = static_cast<T>(-1);
25 if (n < sizeof(T) * 8) {
26 mask += static_cast<T>(static_cast<T>(1) << n);
27 }
28 return mask;
29}
30
31template <typename T>
32bool bittest(T a, int bit)
33{
34 return (a >> bit) & 1;
35}
36
37/*
38 * shift right without undefined behavior for shifts > bit width
39 */
40template <typename U>
41constexpr uint64_t shr64(uint64_t a, U shift)
42{
43 return (shift < 64) ? a >> shift : 0;
44}
45
46/*
47 * shift left without undefined behavior for shifts > bit width
48 */
49template <typename U>
50constexpr uint64_t shl64(uint64_t a, U shift)
51{
52 return (shift < 64) ? a << shift : 0;
53}
54
55RAPIDFUZZ_CONSTEXPR_CXX14 uint64_t addc64(uint64_t a, uint64_t b, uint64_t carryin, uint64_t* carryout)
56{
57 /* todo should use _addcarry_u64 when available */
58 a += carryin;
59 *carryout = a < carryin;
60 a += b;
61 *carryout |= a < b;
62 return a;
63}
64
65template <typename T, typename U>
66RAPIDFUZZ_CONSTEXPR_CXX14 T ceil_div(T a, U divisor)
67{
68 T _div = static_cast<T>(divisor);
69 return a / _div + static_cast<T>(a % _div != 0);
70}
71
72static inline size_t popcount(uint64_t x)
73{
74 return std::bitset<64>(x).count();
75}
76
77static inline size_t popcount(uint32_t x)
78{
79 return std::bitset<32>(x).count();
80}
81
82static inline size_t popcount(uint16_t x)
83{
84 return std::bitset<16>(x).count();
85}
86
87static inline size_t popcount(uint8_t x)
88{
89 static constexpr uint8_t bit_count[256] = {
90 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
91 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
92 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
93 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
94 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
95 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
96 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
97 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8};
98 return bit_count[x];
99}
100
101template <typename T>
102RAPIDFUZZ_CONSTEXPR_CXX14 T rotl(T x, unsigned int n)
103{
104 unsigned int num_bits = std::numeric_limits<T>::digits;
105 assert(n < num_bits);
106 unsigned int count_mask = num_bits - 1;
107
108#if _MSC_VER && !defined(__clang__)
109# pragma warning(push)
110/* unary minus operator applied to unsigned type, result still unsigned */
111# pragma warning(disable : 4146)
112#endif
113 return (x << n) | (x >> (-n & count_mask));
114#if _MSC_VER && !defined(__clang__)
115# pragma warning(pop)
116#endif
117}
118
122template <typename T>
123constexpr T blsi(T a)
124{
125#if _MSC_VER && !defined(__clang__)
126# pragma warning(push)
127/* unary minus operator applied to unsigned type, result still unsigned */
128# pragma warning(disable : 4146)
129#endif
130 return a & -a;
131#if _MSC_VER && !defined(__clang__)
132# pragma warning(pop)
133#endif
134}
135
139template <typename T>
140constexpr T blsr(T x)
141{
142 return x & (x - 1);
143}
144
149template <typename T>
150constexpr T blsmsk(T a)
151{
152 return a ^ (a - 1);
153}
154
155#if defined(_MSC_VER) && !defined(__clang__)
156static inline unsigned int countr_zero(uint32_t x)
157{
158 unsigned long trailing_zero = 0;
159 _BitScanForward(&trailing_zero, x);
160 return trailing_zero;
161}
162
163# if defined(_M_ARM) || defined(_M_X64)
164static inline unsigned int countr_zero(uint64_t x)
165{
166 unsigned long trailing_zero = 0;
167 _BitScanForward64(&trailing_zero, x);
168 return trailing_zero;
169}
170# else
171static inline unsigned int countr_zero(uint64_t x)
172{
173 uint32_t msh = (uint32_t)(x >> 32);
174 uint32_t lsh = (uint32_t)(x & 0xFFFFFFFF);
175 if (lsh != 0) return countr_zero(lsh);
176 return 32 + countr_zero(msh);
177}
178# endif
179
180#else /* gcc / clang */
181static inline unsigned int countr_zero(uint32_t x)
182{
183 return static_cast<unsigned int>(__builtin_ctz(x));
184}
185
186static inline unsigned int countr_zero(uint64_t x)
187{
188 return static_cast<unsigned int>(__builtin_ctzll(x));
189}
190#endif
191
192static inline unsigned int countr_zero(uint16_t x)
193{
194 return countr_zero(static_cast<uint32_t>(x));
195}
196
197static inline unsigned int countr_zero(uint8_t x)
198{
199 return countr_zero(static_cast<uint32_t>(x));
200}
201
202template <typename T, T N, T Pos = 0, bool IsEmpty = (N == 0)>
203struct UnrollImpl;
204
205template <typename T, T N, T Pos>
206struct UnrollImpl<T, N, Pos, false> {
207 template <typename F>
208 static void call(F&& f)
209 {
210 f(Pos);
211 UnrollImpl<T, N - 1, Pos + 1>::call(std::forward<F>(f));
212 }
213};
214
215template <typename T, T N, T Pos>
216struct UnrollImpl<T, N, Pos, true> {
217 template <typename F>
218 static void call(F&&)
219 {}
220};
221
222template <typename T, T N, class F>
223RAPIDFUZZ_CONSTEXPR_CXX14 void unroll(F&& f)
224{
225 UnrollImpl<T, N>::call(f);
226}
227
228} // namespace detail
229} // namespace rapidfuzz