YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

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