]>
git.proxmox.com Git - ceph.git/blob - ceph/src/rocksdb/util/math128.h
1 // Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
2 // This source code is licensed under both the GPLv2 (found in the
3 // COPYING file in the root directory) and Apache 2.0 License
4 // (found in the LICENSE.Apache file in the root directory).
8 #include "util/coding_lean.h"
11 #ifdef TEST_UINT128_COMPAT
12 #undef HAVE_UINT128_EXTENSION
15 namespace ROCKSDB_NAMESPACE
{
17 // Unsigned128 is a 128 bit value supporting (at least) bitwise operators,
18 // shifts, and comparisons. __uint128_t is not always available.
20 #ifdef HAVE_UINT128_EXTENSION
21 using Unsigned128
= __uint128_t
;
27 inline Unsigned128() {
28 static_assert(sizeof(Unsigned128
) == 2 * sizeof(uint64_t),
29 "unexpected overhead in representation");
34 inline Unsigned128(uint64_t lower
) {
39 inline Unsigned128(uint64_t lower
, uint64_t upper
) {
44 explicit operator uint64_t() { return lo
; }
46 explicit operator uint32_t() { return static_cast<uint32_t>(lo
); }
48 explicit operator uint16_t() { return static_cast<uint16_t>(lo
); }
50 explicit operator uint8_t() { return static_cast<uint8_t>(lo
); }
53 inline Unsigned128
operator<<(const Unsigned128
& lhs
, unsigned shift
) {
58 rv
.hi
= lhs
.lo
<< (shift
& 63);
60 uint64_t tmp
= lhs
.lo
;
62 // Ensure shift==0 shifts away everything. (This avoids another
63 // conditional branch on shift == 0.)
64 tmp
= tmp
>> 1 >> (63 - shift
);
65 rv
.hi
= tmp
| (lhs
.hi
<< shift
);
70 inline Unsigned128
& operator<<=(Unsigned128
& lhs
, unsigned shift
) {
75 inline Unsigned128
operator>>(const Unsigned128
& lhs
, unsigned shift
) {
80 rv
.lo
= lhs
.hi
>> (shift
& 63);
82 uint64_t tmp
= lhs
.hi
;
84 // Ensure shift==0 shifts away everything
85 tmp
= tmp
<< 1 << (63 - shift
);
86 rv
.lo
= tmp
| (lhs
.lo
>> shift
);
91 inline Unsigned128
& operator>>=(Unsigned128
& lhs
, unsigned shift
) {
96 inline Unsigned128
operator&(const Unsigned128
& lhs
, const Unsigned128
& rhs
) {
97 return Unsigned128(lhs
.lo
& rhs
.lo
, lhs
.hi
& rhs
.hi
);
100 inline Unsigned128
& operator&=(Unsigned128
& lhs
, const Unsigned128
& rhs
) {
105 inline Unsigned128
operator|(const Unsigned128
& lhs
, const Unsigned128
& rhs
) {
106 return Unsigned128(lhs
.lo
| rhs
.lo
, lhs
.hi
| rhs
.hi
);
109 inline Unsigned128
& operator|=(Unsigned128
& lhs
, const Unsigned128
& rhs
) {
114 inline Unsigned128
operator^(const Unsigned128
& lhs
, const Unsigned128
& rhs
) {
115 return Unsigned128(lhs
.lo
^ rhs
.lo
, lhs
.hi
^ rhs
.hi
);
118 inline Unsigned128
& operator^=(Unsigned128
& lhs
, const Unsigned128
& rhs
) {
123 inline Unsigned128
operator~(const Unsigned128
& v
) {
124 return Unsigned128(~v
.lo
, ~v
.hi
);
127 inline bool operator==(const Unsigned128
& lhs
, const Unsigned128
& rhs
) {
128 return lhs
.lo
== rhs
.lo
&& lhs
.hi
== rhs
.hi
;
131 inline bool operator!=(const Unsigned128
& lhs
, const Unsigned128
& rhs
) {
132 return lhs
.lo
!= rhs
.lo
|| lhs
.hi
!= rhs
.hi
;
135 inline bool operator>(const Unsigned128
& lhs
, const Unsigned128
& rhs
) {
136 return lhs
.hi
> rhs
.hi
|| (lhs
.hi
== rhs
.hi
&& lhs
.lo
> rhs
.lo
);
139 inline bool operator<(const Unsigned128
& lhs
, const Unsigned128
& rhs
) {
140 return lhs
.hi
< rhs
.hi
|| (lhs
.hi
== rhs
.hi
&& lhs
.lo
< rhs
.lo
);
143 inline bool operator>=(const Unsigned128
& lhs
, const Unsigned128
& rhs
) {
144 return lhs
.hi
> rhs
.hi
|| (lhs
.hi
== rhs
.hi
&& lhs
.lo
>= rhs
.lo
);
147 inline bool operator<=(const Unsigned128
& lhs
, const Unsigned128
& rhs
) {
148 return lhs
.hi
< rhs
.hi
|| (lhs
.hi
== rhs
.hi
&& lhs
.lo
<= rhs
.lo
);
152 inline uint64_t Lower64of128(Unsigned128 v
) {
153 #ifdef HAVE_UINT128_EXTENSION
154 return static_cast<uint64_t>(v
);
160 inline uint64_t Upper64of128(Unsigned128 v
) {
161 #ifdef HAVE_UINT128_EXTENSION
162 return static_cast<uint64_t>(v
>> 64);
168 // This generally compiles down to a single fast instruction on 64-bit.
169 // This doesn't really make sense as operator* because it's not a
170 // general 128x128 multiply and provides more output than 64x64 multiply.
171 inline Unsigned128
Multiply64to128(uint64_t a
, uint64_t b
) {
172 #ifdef HAVE_UINT128_EXTENSION
173 return Unsigned128
{a
} * Unsigned128
{b
};
175 // Full decomposition
176 // NOTE: GCC seems to fully understand this code as 64-bit x 64-bit
177 // -> 128-bit multiplication and optimize it appropriately.
178 uint64_t tmp
= uint64_t{b
& 0xffffFFFF} * uint64_t{a
& 0xffffFFFF};
179 uint64_t lower
= tmp
& 0xffffFFFF;
181 tmp
+= uint64_t{b
& 0xffffFFFF} * uint64_t{a
>> 32};
182 // Avoid overflow: first add lower 32 of tmp2, and later upper 32
183 uint64_t tmp2
= uint64_t{b
>> 32} * uint64_t{a
& 0xffffFFFF};
184 tmp
+= tmp2
& 0xffffFFFF;
188 tmp
+= uint64_t{b
>> 32} * uint64_t{a
>> 32};
189 return Unsigned128(lower
, tmp
);
194 inline int FloorLog2(Unsigned128 v
) {
195 if (Upper64of128(v
) == 0) {
196 return FloorLog2(Lower64of128(v
));
198 return FloorLog2(Upper64of128(v
)) + 64;
203 inline int CountTrailingZeroBits(Unsigned128 v
) {
204 if (Lower64of128(v
) != 0) {
205 return CountTrailingZeroBits(Lower64of128(v
));
207 return CountTrailingZeroBits(Upper64of128(v
)) + 64;
212 inline int BitsSetToOne(Unsigned128 v
) {
213 return BitsSetToOne(Lower64of128(v
)) + BitsSetToOne(Upper64of128(v
));
217 inline int BitParity(Unsigned128 v
) {
218 return BitParity(Lower64of128(v
) ^ Upper64of128(v
));
222 inline Unsigned128
EndianSwapValue(Unsigned128 v
) {
223 return (Unsigned128
{EndianSwapValue(Lower64of128(v
))} << 64) |
224 EndianSwapValue(Upper64of128(v
));
228 inline Unsigned128
ReverseBits(Unsigned128 v
) {
229 return (Unsigned128
{ReverseBits(Lower64of128(v
))} << 64) |
230 ReverseBits(Upper64of128(v
));
234 inline Unsigned128
DownwardInvolution(Unsigned128 v
) {
235 return (Unsigned128
{DownwardInvolution(Upper64of128(v
))} << 64) |
236 DownwardInvolution(Upper64of128(v
) ^ Lower64of128(v
));
239 template <typename T
>
240 struct IsUnsignedUpTo128
241 : std::integral_constant
<bool, std::is_unsigned
<T
>::value
||
242 std::is_same
<T
, Unsigned128
>::value
> {};
244 inline void EncodeFixed128(char* dst
, Unsigned128 value
) {
245 EncodeFixed64(dst
, Lower64of128(value
));
246 EncodeFixed64(dst
+ 8, Upper64of128(value
));
249 inline Unsigned128
DecodeFixed128(const char* ptr
) {
250 Unsigned128 rv
= DecodeFixed64(ptr
+ 8);
251 return (rv
<< 64) | DecodeFixed64(ptr
);
254 // A version of EncodeFixed* for generic algorithms. Likely to be used
255 // with Unsigned128, so lives here for now.
256 template <typename T
>
257 inline void EncodeFixedGeneric(char* /*dst*/, T
/*value*/) {
258 // Unfortunately, GCC does not appear to optimize this simple code down
259 // to a trivial load on Intel:
262 // for (size_t i = 0; i < sizeof(T); ++i) {
263 // ret_val |= (static_cast<T>(static_cast<unsigned char>(ptr[i])) << (8 *
268 // But does unroll the loop, and does optimize manually unrolled version
269 // for specific sizes down to a trivial load. I have no idea why it doesn't
270 // do both on this code.
272 // So instead, we rely on specializations
273 static_assert(sizeof(T
) == 0, "No specialization provided for this type");
277 inline void EncodeFixedGeneric(char* dst
, uint16_t value
) {
278 return EncodeFixed16(dst
, value
);
281 inline void EncodeFixedGeneric(char* dst
, uint32_t value
) {
282 return EncodeFixed32(dst
, value
);
285 inline void EncodeFixedGeneric(char* dst
, uint64_t value
) {
286 return EncodeFixed64(dst
, value
);
289 inline void EncodeFixedGeneric(char* dst
, Unsigned128 value
) {
290 return EncodeFixed128(dst
, value
);
293 // A version of EncodeFixed* for generic algorithms.
294 template <typename T
>
295 inline T
DecodeFixedGeneric(const char* /*dst*/) {
296 static_assert(sizeof(T
) == 0, "No specialization provided for this type");
300 inline uint16_t DecodeFixedGeneric(const char* dst
) {
301 return DecodeFixed16(dst
);
304 inline uint32_t DecodeFixedGeneric(const char* dst
) {
305 return DecodeFixed32(dst
);
308 inline uint64_t DecodeFixedGeneric(const char* dst
) {
309 return DecodeFixed64(dst
);
312 inline Unsigned128
DecodeFixedGeneric(const char* dst
) {
313 return DecodeFixed128(dst
);
316 } // namespace ROCKSDB_NAMESPACE