Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/common/bitutils.h
4214 views
1
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <[email protected]>
2
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
3
4
#pragma once
5
6
#include "types.h"
7
8
#include <cstdint>
9
#include <type_traits>
10
11
#ifdef _MSC_VER
12
#include <stdlib.h>
13
#endif
14
15
// Zero-extending helper
16
template<typename TReturn, typename TValue>
17
ALWAYS_INLINE constexpr TReturn ZeroExtend(TValue value)
18
{
19
return static_cast<TReturn>(static_cast<typename std::make_unsigned<TReturn>::type>(
20
static_cast<typename std::make_unsigned<TValue>::type>(value)));
21
}
22
// Sign-extending helper
23
template<typename TReturn, typename TValue>
24
ALWAYS_INLINE constexpr TReturn SignExtend(TValue value)
25
{
26
return static_cast<TReturn>(
27
static_cast<typename std::make_signed<TReturn>::type>(static_cast<typename std::make_signed<TValue>::type>(value)));
28
}
29
30
// Type-specific helpers
31
template<typename TValue>
32
ALWAYS_INLINE constexpr u16 ZeroExtend16(TValue value)
33
{
34
return ZeroExtend<u16, TValue>(value);
35
}
36
template<typename TValue>
37
ALWAYS_INLINE constexpr u32 ZeroExtend32(TValue value)
38
{
39
return ZeroExtend<u32, TValue>(value);
40
}
41
template<typename TValue>
42
ALWAYS_INLINE constexpr u64 ZeroExtend64(TValue value)
43
{
44
return ZeroExtend<u64, TValue>(value);
45
}
46
template<typename TValue>
47
ALWAYS_INLINE constexpr u16 SignExtend16(TValue value)
48
{
49
return SignExtend<u16, TValue>(value);
50
}
51
template<typename TValue>
52
ALWAYS_INLINE constexpr u32 SignExtend32(TValue value)
53
{
54
return SignExtend<u32, TValue>(value);
55
}
56
template<typename TValue>
57
ALWAYS_INLINE constexpr u64 SignExtend64(TValue value)
58
{
59
return SignExtend<u64, TValue>(value);
60
}
61
template<typename TValue>
62
ALWAYS_INLINE constexpr u8 Truncate8(TValue value)
63
{
64
return static_cast<u8>(static_cast<typename std::make_unsigned<decltype(value)>::type>(value));
65
}
66
template<typename TValue>
67
ALWAYS_INLINE constexpr u16 Truncate16(TValue value)
68
{
69
return static_cast<u16>(static_cast<typename std::make_unsigned<decltype(value)>::type>(value));
70
}
71
template<typename TValue>
72
ALWAYS_INLINE constexpr u32 Truncate32(TValue value)
73
{
74
return static_cast<u32>(static_cast<typename std::make_unsigned<decltype(value)>::type>(value));
75
}
76
77
// BCD helpers
78
ALWAYS_INLINE constexpr u8 BinaryToBCD(u8 value)
79
{
80
return ((value / 10) << 4) + (value % 10);
81
}
82
ALWAYS_INLINE constexpr u8 PackedBCDToBinary(u8 value)
83
{
84
return ((value >> 4) * 10) + (value % 16);
85
}
86
ALWAYS_INLINE constexpr u8 IsValidBCDDigit(u8 digit)
87
{
88
return (digit <= 9);
89
}
90
ALWAYS_INLINE constexpr u8 IsValidPackedBCD(u8 value)
91
{
92
return IsValidBCDDigit(value & 0x0F) && IsValidBCDDigit(value >> 4);
93
}
94
95
// Boolean to integer
96
ALWAYS_INLINE constexpr u8 BoolToUInt8(bool value)
97
{
98
return static_cast<u8>(value);
99
}
100
ALWAYS_INLINE constexpr u16 BoolToUInt16(bool value)
101
{
102
return static_cast<u16>(value);
103
}
104
ALWAYS_INLINE constexpr u32 BoolToUInt32(bool value)
105
{
106
return static_cast<u32>(value);
107
}
108
ALWAYS_INLINE constexpr u64 BoolToUInt64(bool value)
109
{
110
return static_cast<u64>(value);
111
}
112
ALWAYS_INLINE constexpr float BoolToFloat(bool value)
113
{
114
return static_cast<float>(value);
115
}
116
117
// Integer to boolean
118
template<typename TValue>
119
ALWAYS_INLINE constexpr bool ConvertToBool(TValue value)
120
{
121
return static_cast<bool>(value);
122
}
123
124
// Unsafe integer to boolean
125
template<typename TValue>
126
ALWAYS_INLINE bool ConvertToBoolUnchecked(TValue value)
127
{
128
// static_assert(sizeof(uint8) == sizeof(bool));
129
bool ret;
130
std::memcpy(&ret, &value, sizeof(bool));
131
return ret;
132
}
133
134
// Generic sign extension
135
template<int NBITS, typename T>
136
ALWAYS_INLINE constexpr T SignExtendN(T value)
137
{
138
// http://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend
139
constexpr int shift = 8 * sizeof(T) - NBITS;
140
return static_cast<T>((static_cast<std::make_signed_t<T>>(value) << shift) >> shift);
141
}
142
143
/// Returns the number of zero bits before the first set bit, going MSB->LSB.
144
template<typename T>
145
ALWAYS_INLINE unsigned CountLeadingZeros(T value)
146
{
147
#ifdef _MSC_VER
148
if constexpr (sizeof(value) >= sizeof(u64))
149
{
150
unsigned long index;
151
_BitScanReverse64(&index, ZeroExtend64(value));
152
return static_cast<unsigned>(index) ^ static_cast<unsigned>((sizeof(value) * 8u) - 1u);
153
}
154
else
155
{
156
unsigned long index;
157
_BitScanReverse(&index, ZeroExtend32(value));
158
return static_cast<unsigned>(index) ^ static_cast<unsigned>((sizeof(value) * 8u) - 1u);
159
}
160
#else
161
if constexpr (sizeof(value) >= sizeof(u64))
162
return static_cast<unsigned>(__builtin_clzl(ZeroExtend64(value)));
163
else if constexpr (sizeof(value) == sizeof(u32))
164
return static_cast<unsigned>(__builtin_clz(ZeroExtend32(value)));
165
else
166
return static_cast<unsigned>(__builtin_clz(ZeroExtend32(value))) & static_cast<unsigned>((sizeof(value) * 8u) - 1u);
167
#endif
168
}
169
170
/// Returns the number of zero bits before the first set bit, going LSB->MSB.
171
template<typename T>
172
ALWAYS_INLINE unsigned CountTrailingZeros(T value)
173
{
174
#ifdef _MSC_VER
175
if constexpr (sizeof(value) >= sizeof(u64))
176
{
177
unsigned long index;
178
_BitScanForward64(&index, ZeroExtend64(value));
179
return index;
180
}
181
else
182
{
183
unsigned long index;
184
_BitScanForward(&index, ZeroExtend32(value));
185
return index;
186
}
187
#else
188
if constexpr (sizeof(value) >= sizeof(u64))
189
return static_cast<unsigned>(__builtin_ctzl(ZeroExtend64(value)));
190
else
191
return static_cast<unsigned>(__builtin_ctz(ZeroExtend32(value)));
192
#endif
193
}
194
195
// C++23-like std::byteswap()
196
template<typename T>
197
ALWAYS_INLINE T ByteSwap(T value)
198
{
199
if constexpr (std::is_signed_v<T>)
200
{
201
return static_cast<T>(ByteSwap(std::make_unsigned_t<T>(value)));
202
}
203
else if constexpr (std::is_same_v<T, std::uint16_t>)
204
{
205
#ifdef _MSC_VER
206
return _byteswap_ushort(value);
207
#else
208
return __builtin_bswap16(value);
209
#endif
210
}
211
else if constexpr (std::is_same_v<T, std::uint32_t>)
212
{
213
#ifdef _MSC_VER
214
return _byteswap_ulong(value);
215
#else
216
return __builtin_bswap32(value);
217
#endif
218
}
219
else if constexpr (std::is_same_v<T, std::uint64_t>)
220
{
221
#ifdef _MSC_VER
222
return _byteswap_uint64(value);
223
#else
224
return __builtin_bswap64(value);
225
#endif
226
}
227
}
228
229