Coverage Report

Created: 2025-05-20 19:11

/root/doris/be/src/gutil/int128.h
Line
Count
Source (jump to first uncovered line)
1
// Copyright 2004 Google Inc.
2
// All Rights Reserved.
3
//
4
5
#pragma once
6
7
#include <iosfwd>
8
using std::ostream;
9
#include "gutil/integral_types.h"
10
11
struct uint128_pod;
12
13
// An unsigned 128-bit integer type. Thread-compatible.
14
class uint128 {
15
public:
16
    uint128(); // Sets to 0, but don't trust on this behavior.
17
    uint128(uint64 top, uint64 bottom);
18
#ifndef SWIG
19
    uint128(int bottom);
20
    uint128(uint32 bottom); // Top 96 bits = 0
21
#endif
22
    uint128(uint64 bottom); // hi_ = 0
23
    uint128(const uint128& val);
24
    uint128(const uint128_pod& val);
25
26
    void Initialize(uint64 top, uint64 bottom);
27
28
    uint128& operator=(const uint128& b);
29
30
    // Arithmetic operators.
31
    // TODO: division, etc.
32
    uint128& operator+=(const uint128& b);
33
    uint128& operator-=(const uint128& b);
34
    uint128& operator*=(const uint128& b);
35
    uint128 operator++(int);
36
    uint128 operator--(int);
37
    uint128& operator<<=(int);
38
    uint128& operator>>=(int);
39
    uint128& operator&=(const uint128& b);
40
    uint128& operator|=(const uint128& b);
41
    uint128& operator^=(const uint128& b);
42
    uint128& operator++();
43
    uint128& operator--();
44
45
    friend uint64 Uint128Low64(const uint128& v);
46
    friend uint64 Uint128High64(const uint128& v);
47
48
    // We add "std::" to avoid including all of port.h.
49
    friend std::ostream& operator<<(std::ostream& o, const uint128& b);
50
51
private:
52
    // Little-endian memory order optimizations can benefit from
53
    // having lo_ first, hi_ last.
54
    // See util/endian/endian.h and Load128/Store128 for storing a uint128.
55
    uint64 lo_;
56
    uint64 hi_;
57
58
    // Not implemented, just declared for catching automatic type conversions.
59
    uint128(uint8);
60
    uint128(uint16);
61
    uint128(float v);
62
    uint128(double v);
63
};
64
65
// This is a POD form of uint128 which can be used for static variables which
66
// need to be operated on as uint128.
67
struct uint128_pod {
68
    // Note: The ordering of fields is different than 'class uint128' but the
69
    // same as its 2-arg constructor.  This enables more obvious initialization
70
    // of static instances, which is the primary reason for this struct in the
71
    // first place.  This does not seem to defeat any optimizations wrt
72
    // operations involving this struct.
73
    uint64 hi;
74
    uint64 lo;
75
};
76
77
extern const uint128_pod kuint128max;
78
79
// allow uint128 to be logged
80
extern std::ostream& operator<<(std::ostream& o, const uint128& b);
81
82
// Methods to access low and high pieces of 128-bit value.
83
// Defined externally from uint128 to facilitate conversion
84
// to native 128-bit types when compilers support them.
85
23.6k
inline uint64 Uint128Low64(const uint128& v) {
86
23.6k
    return v.lo_;
87
23.6k
}
88
47.2k
inline uint64 Uint128High64(const uint128& v) {
89
47.2k
    return v.hi_;
90
47.2k
}
91
92
// TODO: perhaps it would be nice to have int128, a signed 128-bit type?
93
94
// --------------------------------------------------------------------------
95
//                      Implementation details follow
96
// --------------------------------------------------------------------------
97
0
inline bool operator==(const uint128& lhs, const uint128& rhs) {
98
0
    return (Uint128Low64(lhs) == Uint128Low64(rhs) && Uint128High64(lhs) == Uint128High64(rhs));
99
0
}
100
0
inline bool operator!=(const uint128& lhs, const uint128& rhs) {
101
0
    return !(lhs == rhs);
102
0
}
103
0
inline uint128& uint128::operator=(const uint128& b) {
104
0
    lo_ = b.lo_;
105
0
    hi_ = b.hi_;
106
0
    return *this;
107
0
}
108
109
inline uint128::uint128() : lo_(0), hi_(0) {}
110
23.6k
inline uint128::uint128(uint64 top, uint64 bottom) : lo_(bottom), hi_(top) {}
111
0
inline uint128::uint128(const uint128& v) : lo_(v.lo_), hi_(v.hi_) {}
112
inline uint128::uint128(const uint128_pod& v) : lo_(v.lo), hi_(v.hi) {}
113
inline uint128::uint128(uint64 bottom) : lo_(bottom), hi_(0) {}
114
#ifndef SWIG
115
inline uint128::uint128(uint32 bottom) : lo_(bottom), hi_(0) {}
116
inline uint128::uint128(int bottom) : lo_(bottom), hi_(0) {
117
    if (bottom < 0) {
118
        --hi_;
119
    }
120
}
121
#endif
122
0
inline void uint128::Initialize(uint64 top, uint64 bottom) {
123
0
    hi_ = top;
124
0
    lo_ = bottom;
125
0
}
126
127
// Comparison operators.
128
129
#define CMP128(op)                                                    \
130
0
    inline bool operator op(const uint128& lhs, const uint128& rhs) { \
131
0
        return (Uint128High64(lhs) == Uint128High64(rhs))             \
132
0
                       ? (Uint128Low64(lhs) op Uint128Low64(rhs))     \
133
0
                       : (Uint128High64(lhs) op Uint128High64(rhs));  \
134
0
    }
Unexecuted instantiation: _ZltRK7uint128S1_
Unexecuted instantiation: _ZgtRK7uint128S1_
Unexecuted instantiation: _ZgeRK7uint128S1_
Unexecuted instantiation: _ZleRK7uint128S1_
135
136
CMP128(<)
137
CMP128(>)
138
CMP128(>=)
139
CMP128(<=)
140
141
#undef CMP128
142
143
// Unary operators
144
145
0
inline uint128 operator-(const uint128& val) {
146
0
    const uint64 hi_flip = ~Uint128High64(val);
147
0
    const uint64 lo_flip = ~Uint128Low64(val);
148
0
    const uint64 lo_add = lo_flip + 1;
149
0
    if (lo_add < lo_flip) {
150
0
        return uint128(hi_flip + 1, lo_add);
151
0
    }
152
0
    return uint128(hi_flip, lo_add);
153
0
}
154
155
0
inline bool operator!(const uint128& val) {
156
0
    return !Uint128High64(val) && !Uint128Low64(val);
157
0
}
158
159
// Logical operators.
160
161
0
inline uint128 operator~(const uint128& val) {
162
0
    return uint128(~Uint128High64(val), ~Uint128Low64(val));
163
0
}
164
165
#define LOGIC128(op)                                                     \
166
0
    inline uint128 operator op(const uint128& lhs, const uint128& rhs) { \
167
0
        return uint128(Uint128High64(lhs) op Uint128High64(rhs),         \
168
0
                       Uint128Low64(lhs) op Uint128Low64(rhs));          \
169
0
    }
Unexecuted instantiation: _ZorRK7uint128S1_
Unexecuted instantiation: _ZanRK7uint128S1_
Unexecuted instantiation: _ZeoRK7uint128S1_
170
171
LOGIC128(|)
172
LOGIC128(&)
173
LOGIC128(^)
174
175
#undef LOGIC128
176
177
#define LOGICASSIGN128(op)                                       \
178
0
    inline uint128& uint128::operator op(const uint128& other) { \
179
0
        hi_ op other.hi_;                                        \
180
0
        lo_ op other.lo_;                                        \
181
0
        return *this;                                            \
182
0
    }
Unexecuted instantiation: _ZN7uint128oRERKS_
Unexecuted instantiation: _ZN7uint128aNERKS_
Unexecuted instantiation: _ZN7uint128eOERKS_
183
184
LOGICASSIGN128(|=)
185
LOGICASSIGN128(&=)
186
LOGICASSIGN128(^=)
187
188
#undef LOGICASSIGN128
189
190
// Shift operators.
191
192
0
inline uint128 operator<<(const uint128& val, int amount) {
193
0
    // uint64 shifts of >= 64 are undefined, so we will need some special-casing.
194
0
    if (amount < 64) {
195
0
        if (amount == 0) {
196
0
            return val;
197
0
        }
198
0
        uint64 new_hi = (Uint128High64(val) << amount) | (Uint128Low64(val) >> (64 - amount));
199
0
        uint64 new_lo = Uint128Low64(val) << amount;
200
0
        return uint128(new_hi, new_lo);
201
0
    } else if (amount < 128) {
202
0
        return uint128(Uint128Low64(val) << (amount - 64), 0);
203
0
    } else {
204
0
        return uint128(0, 0);
205
0
    }
206
0
}
207
208
0
inline uint128 operator>>(const uint128& val, int amount) {
209
0
    // uint64 shifts of >= 64 are undefined, so we will need some special-casing.
210
0
    if (amount < 64) {
211
0
        if (amount == 0) {
212
0
            return val;
213
0
        }
214
0
        uint64 new_hi = Uint128High64(val) >> amount;
215
0
        uint64 new_lo = (Uint128Low64(val) >> amount) | (Uint128High64(val) << (64 - amount));
216
0
        return uint128(new_hi, new_lo);
217
0
    } else if (amount < 128) {
218
0
        return uint128(0, Uint128High64(val) >> (amount - 64));
219
0
    } else {
220
0
        return uint128(0, 0);
221
0
    }
222
0
}
223
224
0
inline uint128& uint128::operator<<=(int amount) {
225
0
    // uint64 shifts of >= 64 are undefined, so we will need some special-casing.
226
0
    if (amount < 64) {
227
0
        if (amount != 0) {
228
0
            hi_ = (hi_ << amount) | (lo_ >> (64 - amount));
229
0
            lo_ = lo_ << amount;
230
0
        }
231
0
    } else if (amount < 128) {
232
0
        hi_ = lo_ << (amount - 64);
233
0
        lo_ = 0;
234
0
    } else {
235
0
        hi_ = 0;
236
0
        lo_ = 0;
237
0
    }
238
0
    return *this;
239
0
}
240
241
0
inline uint128& uint128::operator>>=(int amount) {
242
0
    // uint64 shifts of >= 64 are undefined, so we will need some special-casing.
243
0
    if (amount < 64) {
244
0
        if (amount != 0) {
245
0
            lo_ = (lo_ >> amount) | (hi_ << (64 - amount));
246
0
            hi_ = hi_ >> amount;
247
0
        }
248
0
    } else if (amount < 128) {
249
0
        hi_ = 0;
250
0
        lo_ = hi_ >> (amount - 64);
251
0
    } else {
252
0
        hi_ = 0;
253
0
        lo_ = 0;
254
0
    }
255
0
    return *this;
256
0
}
257
258
0
inline uint128 operator+(const uint128& lhs, const uint128& rhs) {
259
0
    return uint128(lhs) += rhs;
260
0
}
261
262
0
inline uint128 operator-(const uint128& lhs, const uint128& rhs) {
263
0
    return uint128(lhs) -= rhs;
264
0
}
265
266
0
inline uint128 operator*(const uint128& lhs, const uint128& rhs) {
267
0
    return uint128(lhs) *= rhs;
268
0
}
269
270
0
inline uint128& uint128::operator+=(const uint128& b) {
271
0
    hi_ += b.hi_;
272
0
    uint64 lolo = lo_ + b.lo_;
273
0
    if (lolo < lo_) ++hi_;
274
0
    lo_ = lolo;
275
0
    return *this;
276
0
}
277
278
0
inline uint128& uint128::operator-=(const uint128& b) {
279
0
    hi_ -= b.hi_;
280
0
    if (b.lo_ > lo_) --hi_;
281
0
    lo_ -= b.lo_;
282
0
    return *this;
283
0
}
284
285
0
inline uint128& uint128::operator*=(const uint128& b) {
286
0
    uint64 a96 = hi_ >> 32;
287
0
    uint64 a64 = hi_ & 0xffffffffu;
288
0
    uint64 a32 = lo_ >> 32;
289
0
    uint64 a00 = lo_ & 0xffffffffu;
290
0
    uint64 b96 = b.hi_ >> 32;
291
0
    uint64 b64 = b.hi_ & 0xffffffffu;
292
0
    uint64 b32 = b.lo_ >> 32;
293
0
    uint64 b00 = b.lo_ & 0xffffffffu;
294
0
    // multiply [a96 .. a00] x [b96 .. b00]
295
0
    // terms higher than c96 disappear off the high side
296
0
    // terms c96 and c64 are safe to ignore carry bit
297
0
    uint64 c96 = a96 * b00 + a64 * b32 + a32 * b64 + a00 * b96;
298
0
    uint64 c64 = a64 * b00 + a32 * b32 + a00 * b64;
299
0
    this->hi_ = (c96 << 32) + c64;
300
0
    this->lo_ = 0;
301
0
    // add terms after this one at a time to capture carry
302
0
    *this += uint128(a32 * b00) << 32;
303
0
    *this += uint128(a00 * b32) << 32;
304
0
    *this += a00 * b00;
305
0
    return *this;
306
0
}
307
308
0
inline uint128 uint128::operator++(int) {
309
0
    uint128 tmp(*this);
310
0
    *this += 1;
311
0
    return tmp;
312
0
}
313
314
0
inline uint128 uint128::operator--(int) {
315
0
    uint128 tmp(*this);
316
0
    *this -= 1;
317
0
    return tmp;
318
0
}
319
320
0
inline uint128& uint128::operator++() {
321
0
    *this += 1;
322
0
    return *this;
323
0
}
324
325
0
inline uint128& uint128::operator--() {
326
0
    *this -= 1;
327
0
    return *this;
328
0
}