Skip to content

Commit b604419

Browse files
authored
[C++] Refactor uint128 (#8416)
* [C++] Refactor uint128 This makes it compatible with absl::uint128 * Remove use of absl * Add header to test * Exclude typetraits tests from gcc < 5
1 parent 0ebbd7d commit b604419

File tree

4 files changed

+132
-212
lines changed

4 files changed

+132
-212
lines changed

src/google/protobuf/stubs/int128.cc

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,15 @@
3333
#include <iomanip>
3434
#include <ostream> // NOLINT(readability/streams)
3535
#include <sstream>
36+
#include <string>
3637

3738
#include <google/protobuf/stubs/logging.h>
3839

3940
#include <google/protobuf/port_def.inc>
4041

4142
namespace google {
4243
namespace protobuf {
43-
44-
const uint128_pod kuint128max = {
45-
static_cast<uint64>(PROTOBUF_LONGLONG(0xFFFFFFFFFFFFFFFF)),
46-
static_cast<uint64>(PROTOBUF_LONGLONG(0xFFFFFFFFFFFFFFFF))
47-
};
44+
namespace int128_internal {
4845

4946
// Returns the 0-based position of the last set bit (i.e., most significant bit)
5047
// in the given uint64. The argument may not be 0.
@@ -188,6 +185,14 @@ std::ostream& operator<<(std::ostream& o, const uint128& b) {
188185
return o << rep;
189186
}
190187

188+
void VerifyValidShift(std::string op, int amount) {
189+
// Shifting more than 127 is UB in Abseil, just crash for now to verify
190+
// callers don't depend on it returning 0.
191+
GOOGLE_CHECK_LT(amount, 128) << "Error executing operator " << op
192+
<< ": shifts of more than 127 are undefined";
193+
}
194+
195+
} // namespace int128_internal
191196
} // namespace protobuf
192197
} // namespace google
193198

src/google/protobuf/stubs/int128.h

Lines changed: 58 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -33,38 +33,33 @@
3333
#include <google/protobuf/stubs/common.h>
3434

3535
#include <iosfwd>
36+
#include <limits>
37+
#include <string>
3638

3739
#include <google/protobuf/port_def.inc>
3840

3941
namespace google {
4042
namespace protobuf {
41-
42-
struct uint128_pod;
43-
44-
// TODO(xiaofeng): Define GOOGLE_PROTOBUF_HAS_CONSTEXPR when constexpr is
45-
// available.
46-
#ifdef GOOGLE_PROTOBUF_HAS_CONSTEXPR
47-
# define UINT128_CONSTEXPR constexpr
48-
#else
49-
# define UINT128_CONSTEXPR
50-
#endif
43+
namespace int128_internal {
5144

5245
// An unsigned 128-bit integer type. Thread-compatible.
5346
class PROTOBUF_EXPORT uint128 {
5447
public:
55-
UINT128_CONSTEXPR uint128(); // Sets to 0, but don't trust on this behavior.
56-
UINT128_CONSTEXPR uint128(uint64 top, uint64 bottom);
48+
uint128() = default;
49+
50+
private:
51+
// Use `MakeUint128` instead.
52+
constexpr uint128(uint64 top, uint64 bottom);
53+
54+
public:
5755
#ifndef SWIG
58-
UINT128_CONSTEXPR uint128(int bottom);
59-
UINT128_CONSTEXPR uint128(uint32 bottom); // Top 96 bits = 0
56+
constexpr uint128(int bottom);
57+
constexpr uint128(uint32 bottom); // Top 96 bits = 0
6058
#endif
61-
UINT128_CONSTEXPR uint128(uint64 bottom); // hi_ = 0
62-
UINT128_CONSTEXPR uint128(const uint128_pod &val);
59+
constexpr uint128(uint64 bottom); // hi_ = 0
6360

6461
// Trivial copy constructor, assignment operator and destructor.
6562

66-
void Initialize(uint64 top, uint64 bottom);
67-
6863
// Arithmetic operators.
6964
uint128& operator+=(const uint128& b);
7065
uint128& operator-=(const uint128& b);
@@ -82,8 +77,10 @@ class PROTOBUF_EXPORT uint128 {
8277
uint128& operator++();
8378
uint128& operator--();
8479

85-
friend uint64 Uint128Low64(const uint128& v);
86-
friend uint64 Uint128High64(const uint128& v);
80+
friend constexpr uint64 Uint128Low64(const uint128& v);
81+
friend constexpr uint64 Uint128High64(const uint128& v);
82+
83+
friend constexpr uint128 MakeUint128(uint64_t high, uint64_t low);
8784

8885
// We add "std::" to avoid including all of port.h.
8986
PROTOBUF_EXPORT friend std::ostream& operator<<(std::ostream& o,
@@ -100,35 +97,25 @@ class PROTOBUF_EXPORT uint128 {
10097
uint64 hi_;
10198

10299
// Not implemented, just declared for catching automatic type conversions.
103-
uint128(uint8);
104-
uint128(uint16);
105-
uint128(float v);
106-
uint128(double v);
107-
};
108-
109-
// This is a POD form of uint128 which can be used for static variables which
110-
// need to be operated on as uint128.
111-
struct uint128_pod {
112-
// Note: The ordering of fields is different than 'class uint128' but the
113-
// same as its 2-arg constructor. This enables more obvious initialization
114-
// of static instances, which is the primary reason for this struct in the
115-
// first place. This does not seem to defeat any optimizations wrt
116-
// operations involving this struct.
117-
uint64 hi;
118-
uint64 lo;
100+
uint128(uint8) = delete;
101+
uint128(uint16) = delete;
102+
uint128(float v) = delete;
103+
uint128(double v) = delete;
119104
};
120105

121-
PROTOBUF_EXPORT extern const uint128_pod kuint128max;
122-
123106
// allow uint128 to be logged
124107
PROTOBUF_EXPORT extern std::ostream& operator<<(std::ostream& o,
125108
const uint128& b);
126109

127110
// Methods to access low and high pieces of 128-bit value.
128111
// Defined externally from uint128 to facilitate conversion
129112
// to native 128-bit types when compilers support them.
130-
inline uint64 Uint128Low64(const uint128& v) { return v.lo_; }
131-
inline uint64 Uint128High64(const uint128& v) { return v.hi_; }
113+
inline constexpr uint64 Uint128Low64(const uint128& v) { return v.lo_; }
114+
inline constexpr uint64 Uint128High64(const uint128& v) { return v.hi_; }
115+
116+
constexpr uint128 MakeUint128(uint64_t high, uint64_t low) {
117+
return uint128(high, low);
118+
}
132119

133120
// TODO: perhaps it would be nice to have int128, a signed 128-bit type?
134121

@@ -143,27 +130,17 @@ inline bool operator!=(const uint128& lhs, const uint128& rhs) {
143130
return !(lhs == rhs);
144131
}
145132

146-
inline UINT128_CONSTEXPR uint128::uint128() : lo_(0), hi_(0) {}
147-
inline UINT128_CONSTEXPR uint128::uint128(uint64 top, uint64 bottom)
133+
inline constexpr uint128::uint128(uint64 top, uint64 bottom)
148134
: lo_(bottom), hi_(top) {}
149-
inline UINT128_CONSTEXPR uint128::uint128(const uint128_pod& v)
150-
: lo_(v.lo), hi_(v.hi) {}
151-
inline UINT128_CONSTEXPR uint128::uint128(uint64 bottom)
135+
inline constexpr uint128::uint128(uint64 bottom)
152136
: lo_(bottom), hi_(0) {}
153137
#ifndef SWIG
154-
inline UINT128_CONSTEXPR uint128::uint128(uint32 bottom)
138+
inline constexpr uint128::uint128(uint32 bottom)
155139
: lo_(bottom), hi_(0) {}
156-
inline UINT128_CONSTEXPR uint128::uint128(int bottom)
140+
inline constexpr uint128::uint128(int bottom)
157141
: lo_(bottom), hi_(static_cast<int64>((bottom < 0) ? -1 : 0)) {}
158142
#endif
159143

160-
#undef UINT128_CONSTEXPR
161-
162-
inline void uint128::Initialize(uint64 top, uint64 bottom) {
163-
hi_ = top;
164-
lo_ = bottom;
165-
}
166-
167144
// Comparison operators.
168145

169146
#define CMP128(op) \
@@ -187,9 +164,9 @@ inline uint128 operator-(const uint128& val) {
187164
const uint64 lo_flip = ~Uint128Low64(val);
188165
const uint64 lo_add = lo_flip + 1;
189166
if (lo_add < lo_flip) {
190-
return uint128(hi_flip + 1, lo_add);
167+
return MakeUint128(hi_flip + 1, lo_add);
191168
}
192-
return uint128(hi_flip, lo_add);
169+
return MakeUint128(hi_flip, lo_add);
193170
}
194171

195172
inline bool operator!(const uint128& val) {
@@ -199,13 +176,13 @@ inline bool operator!(const uint128& val) {
199176
// Logical operators.
200177

201178
inline uint128 operator~(const uint128& val) {
202-
return uint128(~Uint128High64(val), ~Uint128Low64(val));
179+
return MakeUint128(~Uint128High64(val), ~Uint128Low64(val));
203180
}
204181

205182
#define LOGIC128(op) \
206183
inline uint128 operator op(const uint128& lhs, const uint128& rhs) { \
207-
return uint128(Uint128High64(lhs) op Uint128High64(rhs), \
208-
Uint128Low64(lhs) op Uint128Low64(rhs)); \
184+
return MakeUint128(Uint128High64(lhs) op Uint128High64(rhs), \
185+
Uint128Low64(lhs) op Uint128Low64(rhs)); \
209186
}
210187

211188
LOGIC128(|)
@@ -229,7 +206,11 @@ LOGICASSIGN128(^=)
229206

230207
// Shift operators.
231208

209+
void VerifyValidShift(std::string op, int amount);
210+
232211
inline uint128 operator<<(const uint128& val, int amount) {
212+
VerifyValidShift("<<", amount);
213+
233214
// uint64 shifts of >= 64 are undefined, so we will need some special-casing.
234215
if (amount < 64) {
235216
if (amount == 0) {
@@ -238,15 +219,14 @@ inline uint128 operator<<(const uint128& val, int amount) {
238219
uint64 new_hi = (Uint128High64(val) << amount) |
239220
(Uint128Low64(val) >> (64 - amount));
240221
uint64 new_lo = Uint128Low64(val) << amount;
241-
return uint128(new_hi, new_lo);
242-
} else if (amount < 128) {
243-
return uint128(Uint128Low64(val) << (amount - 64), 0);
244-
} else {
245-
return uint128(0, 0);
222+
return MakeUint128(new_hi, new_lo);
246223
}
224+
return MakeUint128(Uint128Low64(val) << (amount - 64), 0);
247225
}
248226

249227
inline uint128 operator>>(const uint128& val, int amount) {
228+
VerifyValidShift(">>", amount);
229+
250230
// uint64 shifts of >= 64 are undefined, so we will need some special-casing.
251231
if (amount < 64) {
252232
if (amount == 0) {
@@ -255,12 +235,10 @@ inline uint128 operator>>(const uint128& val, int amount) {
255235
uint64 new_hi = Uint128High64(val) >> amount;
256236
uint64 new_lo = (Uint128Low64(val) >> amount) |
257237
(Uint128High64(val) << (64 - amount));
258-
return uint128(new_hi, new_lo);
259-
} else if (amount < 128) {
260-
return uint128(0, Uint128High64(val) >> (amount - 64));
261-
} else {
262-
return uint128(0, 0);
238+
return MakeUint128(new_hi, new_lo);
263239
}
240+
241+
return MakeUint128(0, Uint128High64(val) >> (amount - 64));
264242
}
265243

266244
inline uint128& uint128::operator<<=(int amount) {
@@ -379,6 +357,17 @@ inline uint128& uint128::operator--() {
379357
return *this;
380358
}
381359

360+
constexpr uint128 Uint128Max() {
361+
return MakeUint128((std::numeric_limits<uint64>::max)(),
362+
(std::numeric_limits<uint64>::max)());
363+
}
364+
365+
} // namespace int128_internal
366+
367+
using google::protobuf::int128_internal::uint128;
368+
using google::protobuf::int128_internal::Uint128Max;
369+
using google::protobuf::int128_internal::MakeUint128;
370+
382371
} // namespace protobuf
383372
} // namespace google
384373

0 commit comments

Comments
 (0)