Skip to content

Commit 24a5eb0

Browse files
Abseil Teamcopybara-github
authored andcommitted
Add support for absl::(u)int128 in FastIntToBuffer()
PiperOrigin-RevId: 849837011 Change-Id: I3b742863d328f01a2461f2b876b2aaf04ce9cd5f
1 parent 5d365d3 commit 24a5eb0

File tree

3 files changed

+140
-16
lines changed

3 files changed

+140
-16
lines changed

absl/strings/numbers.cc

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,18 @@ inline uint64_t PrepareEightDigits(uint32_t i) {
242242
return tens;
243243
}
244244

245+
246+
// Encodes v to buffer as 16 digits padded with leading zeros.
247+
// Pre-condition: v must be < 10^16.
248+
inline char* EncodePadded16(uint64_t v, char* absl_nonnull buffer) {
249+
constexpr uint64_t k1e8 = 100000000;
250+
uint32_t hi = static_cast<uint32_t>(v / k1e8);
251+
uint32_t lo = static_cast<uint32_t>(v % k1e8);
252+
little_endian::Store64(buffer, PrepareEightDigits(hi) + kEightZeroBytes);
253+
little_endian::Store64(buffer + 8, PrepareEightDigits(lo) + kEightZeroBytes);
254+
return buffer + 16;
255+
}
256+
245257
inline ABSL_ATTRIBUTE_ALWAYS_INLINE char* absl_nonnull EncodeFullU32(
246258
uint32_t n, char* absl_nonnull out_str) {
247259
if (n < 10) {
@@ -265,19 +277,19 @@ inline ABSL_ATTRIBUTE_ALWAYS_INLINE char* absl_nonnull EncodeFullU32(
265277
return out_str + sizeof(bottom);
266278
}
267279

268-
inline ABSL_ATTRIBUTE_ALWAYS_INLINE char* EncodeFullU64(uint64_t i,
269-
char* buffer) {
280+
inline ABSL_ATTRIBUTE_ALWAYS_INLINE char* absl_nonnull EncodeFullU64(
281+
uint64_t i, char* absl_nonnull buffer) {
270282
if (i <= std::numeric_limits<uint32_t>::max()) {
271283
return EncodeFullU32(static_cast<uint32_t>(i), buffer);
272284
}
273285
uint32_t mod08;
274286
if (i < 1'0000'0000'0000'0000ull) {
275287
uint32_t div08 = static_cast<uint32_t>(i / 100'000'000ull);
276-
mod08 = static_cast<uint32_t>(i % 100'000'000ull);
288+
mod08 = static_cast<uint32_t>(i % 100'000'000ull);
277289
buffer = EncodeFullU32(div08, buffer);
278290
} else {
279291
uint64_t div08 = i / 100'000'000ull;
280-
mod08 = static_cast<uint32_t>(i % 100'000'000ull);
292+
mod08 = static_cast<uint32_t>(i % 100'000'000ull);
281293
uint32_t div016 = static_cast<uint32_t>(div08 / 100'000'000ull);
282294
uint32_t div08mod08 = static_cast<uint32_t>(div08 % 100'000'000ull);
283295
uint64_t mid_result = PrepareEightDigits(div08mod08) + kEightZeroBytes;
@@ -290,6 +302,30 @@ inline ABSL_ATTRIBUTE_ALWAYS_INLINE char* EncodeFullU64(uint64_t i,
290302
return buffer + sizeof(mod_result);
291303
}
292304

305+
inline ABSL_ATTRIBUTE_ALWAYS_INLINE char* absl_nonnull EncodeFullU128(
306+
uint128 i, char* absl_nonnull buffer) {
307+
if (absl::Uint128High64(i) == 0) {
308+
return EncodeFullU64(absl::Uint128Low64(i), buffer);
309+
}
310+
// We divide the number into 16-digit chunks because `EncodePadded16` is
311+
// optimized to handle 16 digits at a time (as two 8-digit chunks).
312+
constexpr uint64_t k1e16 = uint64_t{10'000'000'000'000'000};
313+
uint128 high = i / k1e16;
314+
uint64_t low = absl::Uint128Low64(i % k1e16);
315+
uint64_t mid = absl::Uint128Low64(high % k1e16);
316+
high /= k1e16;
317+
318+
if (high == 0) {
319+
buffer = EncodeFullU64(mid, buffer);
320+
buffer = EncodePadded16(low, buffer);
321+
} else {
322+
buffer = EncodeFullU64(absl::Uint128Low64(high), buffer);
323+
buffer = EncodePadded16(mid, buffer);
324+
buffer = EncodePadded16(low, buffer);
325+
}
326+
return buffer;
327+
}
328+
293329
} // namespace
294330

295331
void numbers_internal::PutTwoDigits(uint32_t i, char* absl_nonnull buf) {
@@ -345,6 +381,25 @@ char* absl_nonnull numbers_internal::FastIntToBuffer(
345381
return buffer;
346382
}
347383

384+
char* absl_nonnull numbers_internal::FastIntToBuffer(
385+
uint128 i, char* absl_nonnull buffer) {
386+
buffer = EncodeFullU128(i, buffer);
387+
*buffer = '\0';
388+
return buffer;
389+
}
390+
391+
char* absl_nonnull numbers_internal::FastIntToBuffer(
392+
int128 i, char* absl_nonnull buffer) {
393+
uint128 u = static_cast<uint128>(i);
394+
if (i < 0) {
395+
*buffer++ = '-';
396+
u = -u;
397+
}
398+
buffer = EncodeFullU128(u, buffer);
399+
*buffer = '\0';
400+
return buffer;
401+
}
402+
348403
// Given a 128-bit number expressed as a pair of uint64_t, high half first,
349404
// return that number multiplied by the given 32-bit value. If the result is
350405
// too large to fit in a 128-bit number, divide it by 2 until it fits.

absl/strings/numbers.h

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,9 @@ bool safe_strtou64_base(absl::string_view text, uint64_t* absl_nonnull value,
174174
bool safe_strtou128_base(absl::string_view text,
175175
absl::uint128* absl_nonnull value, int base);
176176

177-
static const int kFastToBufferSize = 32;
178-
static const int kSixDigitsToBufferSize = 16;
177+
inline constexpr int kFastToBuffer128Size = 41;
178+
inline constexpr int kFastToBufferSize = 32;
179+
inline constexpr int kSixDigitsToBufferSize = 16;
179180

180181
// Helper function for fast formatting of floating-point values.
181182
// The result is the same as printf's "%g", a.k.a. "%.6g"; that is, six
@@ -188,7 +189,8 @@ size_t SixDigitsToBuffer(double d, char* absl_nonnull buffer);
188189
// WARNING: These functions may write more characters than necessary, because
189190
// they are intended for speed. All functions take an output buffer
190191
// as an argument and return a pointer to the last byte they wrote, which is the
191-
// terminating '\0'. At most `kFastToBufferSize` bytes are written.
192+
// terminating '\0'. The maximum size written is `kFastToBufferSize` for 64-bit
193+
// integers or less, and `kFastToBuffer128Size` for 128-bit integers.
192194
char* absl_nonnull FastIntToBuffer(int32_t i, char* absl_nonnull buffer)
193195
ABSL_INTERNAL_NEED_MIN_SIZE(buffer, kFastToBufferSize);
194196
char* absl_nonnull FastIntToBuffer(uint32_t n, char* absl_nonnull out_str)
@@ -197,25 +199,36 @@ char* absl_nonnull FastIntToBuffer(int64_t i, char* absl_nonnull buffer)
197199
ABSL_INTERNAL_NEED_MIN_SIZE(buffer, kFastToBufferSize);
198200
char* absl_nonnull FastIntToBuffer(uint64_t i, char* absl_nonnull buffer)
199201
ABSL_INTERNAL_NEED_MIN_SIZE(buffer, kFastToBufferSize);
200-
201-
// For enums and integer types that are not an exact match for the types above,
202-
// use templates to call the appropriate one of the four overloads above.
202+
char* absl_nonnull FastIntToBuffer(int128 i, char* absl_nonnull buffer)
203+
ABSL_INTERNAL_NEED_MIN_SIZE(buffer, kFastToBuffer128Size);
204+
char* absl_nonnull FastIntToBuffer(uint128 i, char* absl_nonnull buffer)
205+
ABSL_INTERNAL_NEED_MIN_SIZE(buffer, kFastToBuffer128Size);
206+
207+
// For enums and integer types that are up to 128 bits and are not an exact
208+
// match for the types above, use templates to call the appropriate one of the
209+
// four overloads above.
203210
template <typename int_type>
204-
char* absl_nonnull FastIntToBuffer(int_type i, char* absl_nonnull buffer)
205-
ABSL_INTERNAL_NEED_MIN_SIZE(buffer, kFastToBufferSize) {
206-
static_assert(sizeof(i) <= 64 / 8,
207-
"FastIntToBuffer works only with 64-bit-or-less integers.");
211+
char* absl_nonnull FastIntToBuffer(int_type i,
212+
char* absl_nonnull buffer)
213+
ABSL_INTERNAL_NEED_MIN_SIZE(
214+
buffer, (sizeof(int_type) > 8 ? kFastToBuffer128Size
215+
: kFastToBufferSize)) {
208216
// These conditions are constexpr bools to suppress MSVC warning C4127.
209217
constexpr bool kIsSigned = is_signed<int_type>();
210218
constexpr bool kUse64Bit = sizeof(i) > 32 / 8;
219+
constexpr bool kUse128Bit = sizeof(i) > 64 / 8;
211220
if (kIsSigned) {
212-
if (kUse64Bit) {
221+
if constexpr (kUse128Bit) {
222+
return FastIntToBuffer(static_cast<int128>(i), buffer);
223+
} else if constexpr (kUse64Bit) {
213224
return FastIntToBuffer(static_cast<int64_t>(i), buffer);
214225
} else {
215226
return FastIntToBuffer(static_cast<int32_t>(i), buffer);
216227
}
217228
} else {
218-
if (kUse64Bit) {
229+
if constexpr (kUse128Bit) {
230+
return FastIntToBuffer(static_cast<uint128>(i), buffer);
231+
} else if constexpr (kUse64Bit) {
219232
return FastIntToBuffer(static_cast<uint64_t>(i), buffer);
220233
} else {
221234
return FastIntToBuffer(static_cast<uint32_t>(i), buffer);

absl/strings/numbers_test.cc

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ struct MyInteger {
159159

160160
typedef MyInteger<int64_t> MyInt64;
161161
typedef MyInteger<uint64_t> MyUInt64;
162+
typedef MyInteger<absl::uint128> MyUInt128;
163+
typedef MyInteger<absl::int128> MyInt128;
162164

163165
void CheckInt32(int32_t x) {
164166
char buffer[absl::numbers_internal::kFastToBufferSize];
@@ -212,6 +214,32 @@ void CheckUInt64(uint64_t x) {
212214
EXPECT_EQ(expected, std::string(&buffer[1], my_actual)) << " Input " << x;
213215
}
214216

217+
void CheckUInt128(absl::uint128 x) {
218+
char buffer[absl::numbers_internal::kFastToBuffer128Size];
219+
char* actual = absl::numbers_internal::FastIntToBuffer(x, buffer);
220+
std::string s;
221+
absl::strings_internal::OStringStream strm(&s);
222+
strm << x;
223+
EXPECT_EQ(s, std::string(buffer, actual)) << " Input " << s;
224+
225+
char* my_actual =
226+
absl::numbers_internal::FastIntToBuffer(MyUInt128(x), buffer);
227+
EXPECT_EQ(s, std::string(buffer, my_actual)) << " Input " << s;
228+
}
229+
230+
void CheckInt128(absl::int128 x) {
231+
char buffer[absl::numbers_internal::kFastToBuffer128Size];
232+
char* actual = absl::numbers_internal::FastIntToBuffer(x, buffer);
233+
std::string s;
234+
absl::strings_internal::OStringStream strm(&s);
235+
strm << x;
236+
EXPECT_EQ(s, std::string(buffer, actual)) << " Input " << s;
237+
238+
char* my_actual =
239+
absl::numbers_internal::FastIntToBuffer(MyInt128(x), buffer);
240+
EXPECT_EQ(s, std::string(buffer, my_actual)) << " Input " << s;
241+
}
242+
215243
void CheckHex64(uint64_t v) {
216244
char expected[16 + 1];
217245
std::string actual = absl::StrCat(absl::Hex(v, absl::kZeroPad16));
@@ -251,6 +279,34 @@ TEST(Numbers, TestFastPrints) {
251279
CheckUInt64(uint64_t{1000000000000000000});
252280
CheckUInt64(uint64_t{1199999999999999999});
253281
CheckUInt64(std::numeric_limits<uint64_t>::max());
282+
CheckUInt128(0);
283+
CheckUInt128(1);
284+
CheckUInt128(9);
285+
CheckUInt128(10);
286+
CheckUInt128(99);
287+
CheckUInt128(100);
288+
CheckUInt128(std::numeric_limits<uint64_t>::max());
289+
CheckUInt128(absl::uint128(std::numeric_limits<uint64_t>::max()) + 1);
290+
CheckUInt128(absl::MakeUint128(1, 0));
291+
absl::uint128 k1e16 = 10000000000000000ULL;
292+
CheckUInt128(k1e16 - 1);
293+
CheckUInt128(k1e16);
294+
CheckUInt128(k1e16 + 1);
295+
CheckUInt128(k1e16 * k1e16 - 1);
296+
CheckUInt128(k1e16 * k1e16);
297+
CheckUInt128(k1e16 * k1e16 + 1);
298+
CheckUInt128(absl::Uint128Max() - 1);
299+
CheckUInt128(absl::Uint128Max());
300+
301+
CheckInt128(0);
302+
CheckInt128(1);
303+
CheckInt128(-1);
304+
CheckInt128(10);
305+
CheckInt128(-10);
306+
CheckInt128(absl::Int128Max());
307+
CheckInt128(absl::Int128Min());
308+
CheckInt128(absl::MakeInt128(-1, 1));
309+
CheckInt128(absl::MakeInt128(-1, std::numeric_limits<uint64_t>::max()));
254310

255311
for (int i = 0; i < 10000; i++) {
256312
CheckHex64(i);

0 commit comments

Comments
 (0)