Commit 89a3d4a7 authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook GitHub Bot

to_ascii, to_ascii_size

Summary:
Generic ascii-ification functions `to_ascii` and `to_ascii_size` for varying unsigned integer types over varying integer bases.

Has minimal dependencies. Is explicitly async-signal-safe.

Reviewed By: kennyyu

Differential Revision: D26593764

fbshipit-source-id: b1432bee8ca8eb0a5304943c4e231e201425f14a
parent 2f6126e6
......@@ -793,6 +793,7 @@ if (BUILD_TESTS)
TEST lang_r_value_reference_wrapper_test
SOURCES RValueReferenceWrapperTest.cpp
TEST lang_safe_assert_test SOURCES SafeAssertTest.cpp
TEST lang_to_ascii_test SOURCES ToAsciiTest.cpp
TEST lang_type_info_test SOURCES TypeInfoTest.cpp
TEST lang_uncaught_exceptions_test SOURCES UncaughtExceptionsTest.cpp
......
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/lang/ToAscii.h>
namespace folly {
namespace detail {
template to_ascii_array<8, to_ascii_alphabet_lower>::data_type_ const
to_ascii_array<8, to_ascii_alphabet_lower>::data;
template to_ascii_array<10, to_ascii_alphabet_lower>::data_type_ const
to_ascii_array<10, to_ascii_alphabet_lower>::data;
template to_ascii_array<16, to_ascii_alphabet_lower>::data_type_ const
to_ascii_array<16, to_ascii_alphabet_lower>::data;
template to_ascii_array<8, to_ascii_alphabet_upper>::data_type_ const
to_ascii_array<8, to_ascii_alphabet_upper>::data;
template to_ascii_array<10, to_ascii_alphabet_upper>::data_type_ const
to_ascii_array<10, to_ascii_alphabet_upper>::data;
template to_ascii_array<16, to_ascii_alphabet_upper>::data_type_ const
to_ascii_array<16, to_ascii_alphabet_upper>::data;
template to_ascii_table<8, to_ascii_alphabet_lower>::data_type_ const
to_ascii_table<8, to_ascii_alphabet_lower>::data;
template to_ascii_table<10, to_ascii_alphabet_lower>::data_type_ const
to_ascii_table<10, to_ascii_alphabet_lower>::data;
template to_ascii_table<16, to_ascii_alphabet_lower>::data_type_ const
to_ascii_table<16, to_ascii_alphabet_lower>::data;
template to_ascii_table<8, to_ascii_alphabet_upper>::data_type_ const
to_ascii_table<8, to_ascii_alphabet_upper>::data;
template to_ascii_table<10, to_ascii_alphabet_upper>::data_type_ const
to_ascii_table<10, to_ascii_alphabet_upper>::data;
template to_ascii_table<16, to_ascii_alphabet_upper>::data_type_ const
to_ascii_table<16, to_ascii_alphabet_upper>::data;
template to_ascii_powers<8, uint64_t>::data_type_ const
to_ascii_powers<8, uint64_t>::data;
template to_ascii_powers<10, uint64_t>::data_type_ const
to_ascii_powers<10, uint64_t>::data;
template to_ascii_powers<16, uint64_t>::data_type_ const
to_ascii_powers<16, uint64_t>::data;
} // namespace detail
} // namespace folly
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <cstring>
#include <folly/ConstexprMath.h>
#include <folly/Likely.h>
#include <folly/Portability.h>
#include <folly/Utility.h>
#include <folly/lang/Align.h>
#include <folly/lang/CArray.h>
#if _MSC_VER
#include <intrin.h>
#endif
namespace folly {
// to_ascii_alphabet
//
// Used implicity by to_ascii_lower and to_ascii_upper below.
//
// This alphabet translates digits to 0-9,a-z or 0-9,A-Z. The largest supported
// base is 36; operator() presumes an argument less than that.
//
// Alternative alphabets may be used with to_ascii_with provided they match
// the constructibility/destructibility and the interface of this one.
template <bool Upper>
struct to_ascii_alphabet {
// operator()
//
// Translates a single digit to 0-9,a-z or 0-9,A-Z.
//
// async-signal-safe
constexpr char operator()(uint8_t b) const {
return b < 10 ? '0' + b : (Upper ? 'A' : 'a') + (b - 10);
}
};
using to_ascii_alphabet_lower = to_ascii_alphabet<false>;
using to_ascii_alphabet_upper = to_ascii_alphabet<true>;
namespace detail {
FOLLY_ERASE int to_ascii_port_clzll(uint64_t v) {
#if _MSC_VER
return __lzcnt64(v);
#else
return __builtin_clzll(v);
#endif
}
template <uint64_t Base, typename Alphabet>
struct to_ascii_array {
using data_type_ = c_array<uint8_t, Base>;
static constexpr data_type_ data_() {
data_type_ result{};
Alphabet alpha;
for (size_t i = 0; i < Base; ++i) {
result.data[i] = alpha(i);
}
return result;
}
// @lint-ignore CLANGTIDY
static data_type_ const data;
constexpr char operator()(uint8_t index) const { // also an alphabet
return data.data[index];
}
};
template <uint64_t Base, typename Alphabet>
alignas(hardware_constructive_interference_size)
typename to_ascii_array<Base, Alphabet>::data_type_ const
to_ascii_array<Base, Alphabet>::data =
to_ascii_array<Base, Alphabet>::data_();
extern template to_ascii_array<8, to_ascii_alphabet_lower>::data_type_ const
to_ascii_array<8, to_ascii_alphabet_lower>::data;
extern template to_ascii_array<10, to_ascii_alphabet_lower>::data_type_ const
to_ascii_array<10, to_ascii_alphabet_lower>::data;
extern template to_ascii_array<16, to_ascii_alphabet_lower>::data_type_ const
to_ascii_array<16, to_ascii_alphabet_lower>::data;
extern template to_ascii_array<8, to_ascii_alphabet_upper>::data_type_ const
to_ascii_array<8, to_ascii_alphabet_upper>::data;
extern template to_ascii_array<10, to_ascii_alphabet_upper>::data_type_ const
to_ascii_array<10, to_ascii_alphabet_upper>::data;
extern template to_ascii_array<16, to_ascii_alphabet_upper>::data_type_ const
to_ascii_array<16, to_ascii_alphabet_upper>::data;
template <uint64_t Base, typename Alphabet>
struct to_ascii_table {
using data_type_ = c_array<uint16_t, Base * Base>;
static constexpr data_type_ data_() {
data_type_ result{};
Alphabet alpha;
for (size_t i = 0; i < Base * Base; ++i) {
result.data[i] = //
(alpha(uint8_t(i / Base)) << (kIsLittleEndian ? 0 : 8)) |
(alpha(uint8_t(i % Base)) << (kIsLittleEndian ? 8 : 0));
}
return result;
}
// @lint-ignore CLANGTIDY
static data_type_ const data;
};
template <uint64_t Base, typename Alphabet>
alignas(hardware_constructive_interference_size)
typename to_ascii_table<Base, Alphabet>::data_type_ const
to_ascii_table<Base, Alphabet>::data =
to_ascii_table<Base, Alphabet>::data_();
extern template to_ascii_table<8, to_ascii_alphabet_lower>::data_type_ const
to_ascii_table<8, to_ascii_alphabet_lower>::data;
extern template to_ascii_table<10, to_ascii_alphabet_lower>::data_type_ const
to_ascii_table<10, to_ascii_alphabet_lower>::data;
extern template to_ascii_table<16, to_ascii_alphabet_lower>::data_type_ const
to_ascii_table<16, to_ascii_alphabet_lower>::data;
extern template to_ascii_table<8, to_ascii_alphabet_upper>::data_type_ const
to_ascii_table<8, to_ascii_alphabet_upper>::data;
extern template to_ascii_table<10, to_ascii_alphabet_upper>::data_type_ const
to_ascii_table<10, to_ascii_alphabet_upper>::data;
extern template to_ascii_table<16, to_ascii_alphabet_upper>::data_type_ const
to_ascii_table<16, to_ascii_alphabet_upper>::data;
template <uint64_t Base, typename I>
struct to_ascii_powers {
static constexpr size_t size_(I v) {
return 1 + (v < Base ? 0 : size_(v / Base));
}
static constexpr size_t const size = size_(~I(0));
using data_type_ = c_array<I, size>;
static constexpr data_type_ data_() {
data_type_ result{};
for (size_t i = 0; i < size; ++i) {
result.data[i] = constexpr_pow(Base, i);
}
return result;
}
// @lint-ignore CLANGTIDY
static data_type_ const data;
};
template <uint64_t Base, typename I>
constexpr size_t const to_ascii_powers<Base, I>::size;
template <uint64_t Base, typename I>
alignas(hardware_constructive_interference_size)
typename to_ascii_powers<Base, I>::data_type_ const
to_ascii_powers<Base, I>::data = to_ascii_powers<Base, I>::data_();
extern template to_ascii_powers<8, uint64_t>::data_type_ const
to_ascii_powers<8, uint64_t>::data;
extern template to_ascii_powers<10, uint64_t>::data_type_ const
to_ascii_powers<10, uint64_t>::data;
extern template to_ascii_powers<16, uint64_t>::data_type_ const
to_ascii_powers<16, uint64_t>::data;
template <uint64_t Base>
FOLLY_ALWAYS_INLINE size_t to_ascii_size_imuls(uint64_t v) {
using powers = to_ascii_powers<Base, uint64_t>;
uint64_t p = 1;
for (size_t i = 0u; i < powers::size; ++i, p *= Base) {
if (FOLLY_UNLIKELY(v < p)) {
return i + size_t(i == 0);
}
}
return powers::size;
}
template <uint64_t Base>
FOLLY_ALWAYS_INLINE size_t to_ascii_size_idivs(uint64_t v) {
size_t i = 1;
while (v >= Base) {
i += 1;
v /= Base;
}
return i;
}
template <uint64_t Base>
FOLLY_ALWAYS_INLINE size_t to_ascii_size_array(uint64_t v) {
using powers = to_ascii_powers<Base, uint64_t>;
for (size_t i = 0u; i < powers::size; ++i) {
if (FOLLY_LIKELY(v < powers::data.data[i])) {
return i + size_t(i == 0);
}
}
return powers::size;
}
// For some architectures, we can get a little help from clzll, the "count
// leading zeros" builtin, which is backed by a single performant instruction.
//
// Note that the compiler implements __builtin_clzll on all architectures, but
// only emits a single clzll instruction when the architecture has one.
//
// This implementation may be faster than the basic ones in the general case
// because the time taken to compute this one is constant for non-zero v,
// whereas the basic ones take time proportional to log<2>(v). Whether this one
// is actually faster depends on the emitted code for this implementation and
// on whether the loops in the basic implementations are unrolled.
template <uint64_t Base>
FOLLY_ALWAYS_INLINE size_t to_ascii_size_clzll(uint64_t v) {
using powers = to_ascii_powers<Base, uint64_t>;
// clzll is undefined for 0; must special case this
if (FOLLY_UNLIKELY(!v)) {
return 1;
}
// log2 is approx log<2>(v)
size_t const vlog2 = 64 - to_ascii_port_clzll(v);
// handle directly when Base is power-of-two
if (!(Base & (Base - 1))) {
constexpr auto const blog2 = constexpr_log2(Base);
return vlog2 / blog2 + size_t(vlog2 % blog2 != 0);
}
// blog2r is approx 1 / log<2>(Base), used in log change-of-base just below
constexpr auto const blog2r = 8. / constexpr_log2(constexpr_pow(Base, 8));
// vlogb is approx log<Base>(v) = log<2>(v) / log<2>(Base)
auto const vlogb = vlog2 * size_t(blog2r * 256) / 256;
// return vlogb, adjusted if necessary
return vlogb + size_t(vlogb < powers::size && v >= powers::data.data[vlogb]);
}
template <uint64_t Base>
FOLLY_ALWAYS_INLINE size_t to_ascii_size(uint64_t v) {
return kIsArchAmd64 && !(Base & (Base - 1)) //
? to_ascii_size_clzll<Base>(v)
: to_ascii_size_array<Base>(v);
}
// The straightforward implementation, assuming the size known in advance.
//
// The straightforward implementation without the size known in advance would
// entail emitting the bytes backward and then reversing them at the end, once
// the size is known.
template <uint64_t Base, typename Alphabet>
FOLLY_ALWAYS_INLINE void to_ascii_with_basic(
char* out, size_t size, uint64_t v) {
Alphabet const xlate;
for (auto pos = size - 1; pos; --pos) {
// keep /, % together so a peephole optimization computes them together
auto const q = v / Base;
auto const r = v % Base;
out[pos] = xlate(r);
v = q;
}
out[0] = xlate(v);
}
template <uint64_t Base, typename Alphabet>
FOLLY_ALWAYS_INLINE size_t to_ascii_with_basic(char* out, uint64_t v) {
auto const size = to_ascii_size<Base>(v);
to_ascii_with_basic<Base, Alphabet>(out, size, v);
return size;
}
// A variant of the straightforward implementation, but using a lookup table.
template <uint64_t Base, typename Alphabet>
FOLLY_ALWAYS_INLINE void to_ascii_with_array(
char* out, size_t size, uint64_t v) {
using array = to_ascii_array<Base, Alphabet>; // also an alphabet
to_ascii_with_basic<Base, array>(out, size, v);
}
template <uint64_t Base, typename Alphabet>
FOLLY_ALWAYS_INLINE size_t to_ascii_with_array(char* out, uint64_t v) {
auto const size = to_ascii_size<Base>(v);
to_ascii_with_array<Base, Alphabet>(out, size, v);
return size;
}
// A trickier implementation which performs half as many divides as the other,
// more straightforward, implementation. On modern hardware, the divides are
// the bottleneck (even when the compiler emits a complicated sequence of add,
// sub, and mul instructions with special constants to simulate a divide by a
// fixed denominator).
//
// The downside of this implementation is that the emitted code is larger,
// especially when the divide is simulated, which affects inlining decisions.
template <uint64_t Base, typename Alphabet>
FOLLY_ALWAYS_INLINE void to_ascii_with_table(
char* out, size_t size, uint64_t v) {
using table = to_ascii_table<Base, Alphabet>;
auto pos = size - 2;
while (FOLLY_UNLIKELY(v >= Base * Base)) {
// keep /, % together so a peephole optimization computes them together
auto const q = v / (Base * Base);
auto const r = v % (Base * Base);
auto const val = table::data.data[size_t(r)];
std::memcpy(out + pos, &val, 2);
pos -= 2;
v = q;
}
auto const val = table::data.data[size_t(v)];
if (FOLLY_UNLIKELY(size % 2 == 0)) {
std::memcpy(out, &val, 2);
} else {
*out = val >> (kIsLittleEndian ? 8 : 0);
}
}
template <uint64_t Base, typename Alphabet>
FOLLY_ALWAYS_INLINE size_t to_ascii_with_table(char* out, uint64_t v) {
auto const size = to_ascii_size<Base>(v);
to_ascii_with_table<Base, Alphabet>(out, size, v);
return size;
}
} // namespace detail
// to_ascii_size_max
//
// The maximum size buffer that might be required to hold the ascii-encoded
// representation of any value of unsigned type I in base Base.
//
// In base 10, u64 requires at most 20 bytes, u32 at most 10, u16 at most 5,
// and u8 at most 3.
//
// async-signal-safe
template <uint64_t Base, typename I>
constexpr size_t to_ascii_size_max() {
return detail::to_ascii_powers<Base, I>::size;
}
// to_ascii_size_max_decimal
//
// An alias to to_ascii_size_max<10>.
//
// async-signal-safe
template <typename I>
constexpr size_t to_ascii_size_max_decimal() {
return to_ascii_size_max<10, I>();
}
// to_ascii_size
//
// Returns the number of digits in the base Base representation of a uint64_t.
// Useful for preallocating buffers, etc.
//
// async-signal-safe
template <uint64_t Base>
size_t to_ascii_size(uint64_t v) {
return detail::to_ascii_size<Base>(v);
}
// to_ascii_size_decimal
//
// An alias to to_ascii_size<10>.
//
// async-signal-safe
inline size_t to_ascii_size_decimal(uint64_t v) {
return to_ascii_size<10>(v);
}
// to_ascii_with
//
// Copies the digits of v, in base Base, translated with Alphabet, into buffer
// and returns the number of bytes written.
//
// Does *not* append a null terminator. It is the caller's responsibility to
// append a null terminator if one is required.
//
// Assumes buffer points to at least to_ascii_size<Base>(v) bytes of writable
// memory. It is the caller's responsibility to provide a writable buffer with
// the required min size.
//
// async-signal-safe
template <uint64_t Base, typename Alphabet>
size_t to_ascii_with(char* out, uint64_t v) {
return detail::to_ascii_with_table<Base, Alphabet>(out, v);
}
// to_ascii_lower
//
// Composes to_ascii_with with to_ascii_alphabet_lower.
//
// async-signal-safe
template <uint64_t Base>
size_t to_ascii_lower(char* out, uint64_t v) {
return to_ascii_with<Base, to_ascii_alphabet_lower>(out, v);
}
// to_ascii_upper
//
// Composes to_ascii_with with to_ascii_alphabet_upper.
//
// async-signal-safe
template <uint64_t Base>
size_t to_ascii_upper(char* out, uint64_t v) {
return to_ascii_with<Base, to_ascii_alphabet_upper>(out, v);
}
// to_ascii_decimal
//
// An alias to to_ascii<10, false>.
//
// async-signals-afe
inline size_t to_ascii_decimal(char* out, uint64_t v) {
return to_ascii_lower<10>(out, v);
}
} // namespace folly
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/lang/ToAscii.h>
#define __STDC_FORMAT_MACROS
#include <cinttypes>
#include <fmt/format.h>
#include <folly/Benchmark.h>
#include <folly/lang/Keep.h>
#include <folly/portability/FmtCompile.h>
#if __has_include(<charconv>)
#include <charconv>
#endif
using abc = folly::to_ascii_alphabet_lower;
#define FOLLY_TO_ASCII_BENCH_CHECK_SIZE(impl, base) \
extern "C" FOLLY_KEEP size_t check_to_ascii_size_##impl##_##base( \
uint64_t v) { \
return folly::detail::to_ascii_size_##impl<base>(v); \
}
#define FOLLY_TO_ASCII_BENCH_CHECK_WITH(impl, base) \
extern "C" FOLLY_KEEP void check_bound_to_ascii_with_##impl##_##base( \
char* buf, size_t size, uint64_t v) { \
folly::detail::to_ascii_with_##impl<base, abc>(buf, size, v); \
} \
extern "C" FOLLY_KEEP size_t check_return_to_ascii_with_##impl##_##base( \
char* buf, uint64_t v) { \
return folly::detail::to_ascii_with_##impl<base, abc>(buf, v); \
}
FOLLY_TO_ASCII_BENCH_CHECK_SIZE(imuls, 16)
FOLLY_TO_ASCII_BENCH_CHECK_SIZE(imuls, 10)
FOLLY_TO_ASCII_BENCH_CHECK_SIZE(imuls, 8)
FOLLY_TO_ASCII_BENCH_CHECK_SIZE(idivs, 16)
FOLLY_TO_ASCII_BENCH_CHECK_SIZE(idivs, 10)
FOLLY_TO_ASCII_BENCH_CHECK_SIZE(idivs, 8)
FOLLY_TO_ASCII_BENCH_CHECK_SIZE(array, 16)
FOLLY_TO_ASCII_BENCH_CHECK_SIZE(array, 10)
FOLLY_TO_ASCII_BENCH_CHECK_SIZE(array, 8)
FOLLY_TO_ASCII_BENCH_CHECK_SIZE(clzll, 16)
FOLLY_TO_ASCII_BENCH_CHECK_SIZE(clzll, 10)
FOLLY_TO_ASCII_BENCH_CHECK_SIZE(clzll, 8)
FOLLY_TO_ASCII_BENCH_CHECK_WITH(basic, 16)
FOLLY_TO_ASCII_BENCH_CHECK_WITH(basic, 10)
FOLLY_TO_ASCII_BENCH_CHECK_WITH(basic, 8)
FOLLY_TO_ASCII_BENCH_CHECK_WITH(array, 16)
FOLLY_TO_ASCII_BENCH_CHECK_WITH(array, 10)
FOLLY_TO_ASCII_BENCH_CHECK_WITH(array, 8)
FOLLY_TO_ASCII_BENCH_CHECK_WITH(table, 16)
FOLLY_TO_ASCII_BENCH_CHECK_WITH(table, 10)
FOLLY_TO_ASCII_BENCH_CHECK_WITH(table, 8)
namespace {
template <uint64_t Base>
struct inputs;
template <>
struct inputs<10> {
static constexpr uint64_t const data[21] = {
0,
1,
12,
123,
1234,
12345,
123456,
1234567,
12345678,
123456789,
1234567890,
12345678901,
123456789012,
1234567890123,
12345678901234,
123456789012345,
1234567890123456,
12345678901234567,
123456789012345678,
1234567890123456789,
12345678901234567890u,
};
};
constexpr uint64_t const inputs<10>::data[21];
template <>
struct inputs<16> {
static constexpr uint64_t const data[17] = {
0x0,
0x1,
0x12,
0x123,
0x1234,
0x12345,
0x123456,
0x1234567,
0x12345678,
0x123456789,
0x123456789a,
0x123456789ab,
0x123456789abc,
0x123456789abcd,
0x123456789abcde,
0x123456789abcdef,
0x123456789abcdef0,
};
};
constexpr uint64_t const inputs<16>::data[17];
} // namespace
static void fill_zero(char* out, size_t size, size_t index) {
(void)index;
std::memset(out, 0, size);
}
template <typename... A>
static void call_to_chars(FOLLY_MAYBE_UNUSED A... a) {
#if __cpp_lib_to_chars >= 201611L
std::to_chars(a...);
#endif
}
template <uint64_t Base, typename F>
static void to_ascii_size_go(size_t iters, size_t index, F f) {
while (iters--) {
auto const v = inputs<Base>::data[index] + (iters % 8);
auto const size = [&]() FOLLY_NOINLINE { return f(v); }();
folly::doNotOptimizeAway(size);
}
}
template <uint64_t Base, typename F>
static void to_ascii_go(size_t iters, size_t index, F f) {
char out[1 + folly::to_ascii_size_max<Base, uint64_t>()];
while (iters--) {
auto const v = inputs<Base>::data[index] + (iters % 8);
auto const size = folly::to_ascii_size<Base>(v);
[&]() FOLLY_NOINLINE { f(&out[0], size, v); }();
}
folly::doNotOptimizeAway(out);
}
// to_ascii_size<10>
static void fmt_formatted_size_10(size_t iters, size_t index) {
to_ascii_size_go<10>(iters, index, [](auto v) {
return fmt::formatted_size(FOLLY_FMT_COMPILE("{}"), v);
});
}
static void to_ascii_size_imuls_10(size_t iters, size_t index) {
to_ascii_size_go<10>(iters, index, [](auto v) {
return folly::detail::to_ascii_size_imuls<10>(v);
});
}
static void to_ascii_size_idivs_10(size_t iters, size_t index) {
to_ascii_size_go<10>(iters, index, [](auto v) {
return folly::detail::to_ascii_size_idivs<10>(v);
});
}
static void to_ascii_size_array_10(size_t iters, size_t index) {
to_ascii_size_go<10>(iters, index, [](auto v) {
return folly::detail::to_ascii_size_array<10>(v);
});
}
static void to_ascii_size_clzll_10(size_t iters, size_t index) {
to_ascii_size_go<10>(iters, index, [](auto v) {
return folly::detail::to_ascii_size_clzll<10>(v);
});
}
#define BENCHMARK_GROUP(index) \
BENCHMARK_DRAW_LINE(); \
BENCHMARK_PARAM(fmt_formatted_size_10, index) \
BENCHMARK_PARAM(to_ascii_size_imuls_10, index) \
BENCHMARK_PARAM(to_ascii_size_idivs_10, index) \
BENCHMARK_PARAM(to_ascii_size_array_10, index) \
BENCHMARK_PARAM(to_ascii_size_clzll_10, index)
BENCHMARK_GROUP(1)
BENCHMARK_GROUP(2)
BENCHMARK_GROUP(3)
BENCHMARK_GROUP(4)
BENCHMARK_GROUP(5)
BENCHMARK_GROUP(6)
BENCHMARK_GROUP(7)
BENCHMARK_GROUP(8)
BENCHMARK_GROUP(9)
BENCHMARK_GROUP(10)
BENCHMARK_GROUP(11)
BENCHMARK_GROUP(12)
BENCHMARK_GROUP(13)
BENCHMARK_GROUP(14)
BENCHMARK_GROUP(15)
BENCHMARK_GROUP(16)
BENCHMARK_GROUP(17)
BENCHMARK_GROUP(18)
BENCHMARK_GROUP(19)
BENCHMARK_GROUP(20)
#undef BENCHMARK_GROUP
BENCHMARK_DRAW_LINE();
// to_ascii_size<16>
static void fmt_formatted_size_16(size_t iters, size_t index) {
to_ascii_size_go<16>(iters, index, [](auto v) {
return fmt::formatted_size(FOLLY_FMT_COMPILE("{:x}"), v);
});
}
static void to_ascii_size_imuls_16(size_t iters, size_t index) {
to_ascii_size_go<16>(iters, index, [](auto v) {
return folly::detail::to_ascii_size_imuls<16>(v);
});
}
static void to_ascii_size_idivs_16(size_t iters, size_t index) {
to_ascii_size_go<16>(iters, index, [](auto v) {
return folly::detail::to_ascii_size_idivs<16>(v);
});
}
static void to_ascii_size_array_16(size_t iters, size_t index) {
to_ascii_size_go<16>(iters, index, [](auto v) {
return folly::detail::to_ascii_size_array<16>(v);
});
}
static void to_ascii_size_clzll_16(size_t iters, size_t index) {
to_ascii_size_go<16>(iters, index, [](auto v) {
return folly::detail::to_ascii_size_clzll<16>(v);
});
}
#define BENCHMARK_GROUP(index) \
BENCHMARK_DRAW_LINE(); \
BENCHMARK_PARAM(fmt_formatted_size_16, index) \
BENCHMARK_PARAM(to_ascii_size_imuls_16, index) \
BENCHMARK_PARAM(to_ascii_size_idivs_16, index) \
BENCHMARK_PARAM(to_ascii_size_array_16, index) \
BENCHMARK_PARAM(to_ascii_size_clzll_16, index)
BENCHMARK_GROUP(1)
BENCHMARK_GROUP(2)
BENCHMARK_GROUP(3)
BENCHMARK_GROUP(4)
BENCHMARK_GROUP(5)
BENCHMARK_GROUP(6)
BENCHMARK_GROUP(7)
BENCHMARK_GROUP(8)
BENCHMARK_GROUP(9)
BENCHMARK_GROUP(10)
BENCHMARK_GROUP(11)
BENCHMARK_GROUP(12)
BENCHMARK_GROUP(13)
BENCHMARK_GROUP(14)
BENCHMARK_GROUP(15)
BENCHMARK_GROUP(16)
#undef BENCHMARK_GROUP
BENCHMARK_DRAW_LINE();
// to_ascii<10>
static void fill_zero_10(size_t iters, size_t index) {
to_ascii_go<10>(iters, index, [](auto out, auto size, auto v) {
fill_zero(out, size, v);
});
}
static void libc_snprintf_10(size_t iters, size_t index) {
to_ascii_go<10>(iters, index, [](auto out, auto size, auto v) {
std::snprintf(out, size, "%" PRIu64, v);
});
}
static void std_to_string_10(size_t iters, size_t index) {
to_ascii_go<10>(iters, index, [](auto out, auto size, auto v) {
auto result = std::to_string(v);
std::memcpy(out, &result[0], size);
});
}
static void std_to_chars__10(size_t iters, size_t index) {
to_ascii_go<10>(iters, index, [](auto out, auto size, auto v) {
call_to_chars(out, out + size, v, 10);
});
}
static void fmt_format_to_10(size_t iters, size_t index) {
to_ascii_go<10>(iters, index, [](auto out, auto size, auto v) {
(void)size;
fmt::format_to(out, FMT_COMPILE("{}"), v);
});
}
static void to_ascii_basic_10(size_t iters, size_t index) {
to_ascii_go<10>(iters, index, [](auto out, auto size, auto v) {
folly::detail::to_ascii_with_basic<10, abc>(out, size, v);
});
}
static void to_ascii_array_10(size_t iters, size_t index) {
to_ascii_go<10>(iters, index, [](auto out, auto size, auto v) {
folly::detail::to_ascii_with_array<10, abc>(out, size, v);
});
}
static void to_ascii_table_10(size_t iters, size_t index) {
to_ascii_go<10>(iters, index, [](auto out, auto size, auto v) {
folly::detail::to_ascii_with_table<10, abc>(out, size, v);
});
}
#define BENCHMARK_GROUP(index) \
BENCHMARK_DRAW_LINE(); \
BENCHMARK_PARAM(fill_zero_10, index) \
BENCHMARK_PARAM(libc_snprintf_10, index) \
BENCHMARK_PARAM(std_to_string_10, index) \
BENCHMARK_PARAM(std_to_chars__10, index) \
BENCHMARK_PARAM(fmt_format_to_10, index) \
BENCHMARK_PARAM(to_ascii_basic_10, index) \
BENCHMARK_PARAM(to_ascii_array_10, index) \
BENCHMARK_PARAM(to_ascii_table_10, index)
BENCHMARK_GROUP(1)
BENCHMARK_GROUP(2)
BENCHMARK_GROUP(3)
BENCHMARK_GROUP(4)
BENCHMARK_GROUP(5)
BENCHMARK_GROUP(6)
BENCHMARK_GROUP(7)
BENCHMARK_GROUP(8)
BENCHMARK_GROUP(9)
BENCHMARK_GROUP(10)
BENCHMARK_GROUP(11)
BENCHMARK_GROUP(12)
BENCHMARK_GROUP(13)
BENCHMARK_GROUP(14)
BENCHMARK_GROUP(15)
BENCHMARK_GROUP(16)
BENCHMARK_GROUP(17)
BENCHMARK_GROUP(18)
BENCHMARK_GROUP(19)
BENCHMARK_GROUP(20)
#undef BENCHMARK_GROUP
BENCHMARK_DRAW_LINE();
// to_ascii<16>
static void fill_zero_16(size_t iters, size_t index) {
to_ascii_go<16>(iters, index, [](auto out, auto size, auto v) {
fill_zero(out, size, v);
});
}
static void libc_snprintf_16(size_t iters, size_t index) {
to_ascii_go<16>(iters, index, [](auto out, auto size, auto v) {
std::snprintf(out, size, "%" PRIu64 "x", v);
});
}
static void std_to_chars__16(size_t iters, size_t index) {
to_ascii_go<10>(iters, index, [](auto out, auto size, auto v) {
call_to_chars(out, out + size, v, 16);
});
}
static void fmt_format_to_16(size_t iters, size_t index) {
to_ascii_go<16>(iters, index, [](auto out, auto size, auto v) {
(void)size;
fmt::format_to(out, FMT_COMPILE("{:x}"), v);
});
}
static void to_ascii_basic_16(size_t iters, size_t index) {
to_ascii_go<16>(iters, index, [](auto out, auto size, auto v) {
folly::detail::to_ascii_with_basic<16, abc>(out, size, v);
});
}
static void to_ascii_array_16(size_t iters, size_t index) {
to_ascii_go<16>(iters, index, [](auto out, auto size, auto v) {
folly::detail::to_ascii_with_array<16, abc>(out, size, v);
});
}
static void to_ascii_table_16(size_t iters, size_t index) {
to_ascii_go<16>(iters, index, [](auto out, auto size, auto v) {
folly::detail::to_ascii_with_table<16, abc>(out, size, v);
});
}
#define BENCHMARK_GROUP(index) \
BENCHMARK_DRAW_LINE(); \
BENCHMARK_PARAM(fill_zero_16, index) \
BENCHMARK_PARAM(libc_snprintf_16, index) \
BENCHMARK_PARAM(std_to_chars__16, index) \
BENCHMARK_PARAM(fmt_format_to_16, index) \
BENCHMARK_PARAM(to_ascii_basic_16, index) \
BENCHMARK_PARAM(to_ascii_array_16, index) \
BENCHMARK_PARAM(to_ascii_table_16, index)
BENCHMARK_GROUP(1)
BENCHMARK_GROUP(2)
BENCHMARK_GROUP(3)
BENCHMARK_GROUP(4)
BENCHMARK_GROUP(5)
BENCHMARK_GROUP(6)
BENCHMARK_GROUP(7)
BENCHMARK_GROUP(8)
BENCHMARK_GROUP(9)
BENCHMARK_GROUP(10)
BENCHMARK_GROUP(11)
BENCHMARK_GROUP(12)
BENCHMARK_GROUP(13)
BENCHMARK_GROUP(14)
BENCHMARK_GROUP(15)
BENCHMARK_GROUP(16)
#undef BENCHMARK_GROUP
int main(int argc, char** argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
folly::runBenchmarks();
return 0;
}
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/lang/ToAscii.h>
#include <string>
#include <folly/portability/GTest.h>
template <typename F>
static void to_ascii_size_16(F _) {
EXPECT_EQ(1, _(0x0));
EXPECT_EQ(1, _(0x1));
EXPECT_EQ(1, _(0xf));
EXPECT_EQ(2, _(0x10));
EXPECT_EQ(2, _(0x11));
EXPECT_EQ(2, _(0xff));
EXPECT_EQ(3, _(0x100));
EXPECT_EQ(3, _(0x101));
EXPECT_EQ(15, _(0xfffffffffffffff));
EXPECT_EQ(16, _(0x1000000000000000));
EXPECT_EQ(16, _(0x1000000000000001));
EXPECT_EQ(16, _(0xffffffffffffffff));
}
template <typename F>
static void to_ascii_size_10(F _) {
EXPECT_EQ(1, _(0));
EXPECT_EQ(1, _(1));
EXPECT_EQ(1, _(9));
EXPECT_EQ(2, _(10));
EXPECT_EQ(2, _(99));
EXPECT_EQ(3, _(100));
EXPECT_EQ(3, _(999));
EXPECT_EQ(4, _(1000));
EXPECT_EQ(4, _(9999));
EXPECT_EQ(20, _(18446744073709551615u));
}
template <typename F>
static void to_ascii_size_8(F _) {
EXPECT_EQ(1, _(00));
EXPECT_EQ(1, _(01));
EXPECT_EQ(1, _(07));
EXPECT_EQ(2, _(010));
EXPECT_EQ(2, _(011));
EXPECT_EQ(2, _(077));
EXPECT_EQ(3, _(0100));
EXPECT_EQ(3, _(0101));
EXPECT_EQ(21, _(0777777777777777777777));
EXPECT_EQ(22, _(01000000000000000000000));
EXPECT_EQ(22, _(01000000000000000000001));
EXPECT_EQ(22, _(01777777777777777777777));
}
template <typename F>
static void to_ascii_size_u64_10_compare(F _) {
// the first X nonnegatives
for (uint64_t i = 0; i < 100000; i++) {
EXPECT_EQ(std::to_string(i).size(), _(i));
}
// all powers of 2
for (auto p = 0; p < 64; p++) {
for (auto n : {-1, 0, 1}) {
auto const v = folly::constexpr_pow(uint64_t(2), p) + n;
EXPECT_EQ(std::to_string(v).size(), _(v));
}
}
// all powers of 10
for (auto p = 0; p < 20; p++) {
for (auto n : {-1, 0, 1}) {
auto const v = folly::constexpr_pow(uint64_t(2), p) + n;
EXPECT_EQ(std::to_string(v).size(), _(v));
}
}
}
struct ToAsciiTest : testing::Test {};
TEST_F(ToAsciiTest, to_ascii_powers_u64_16) {
using _ = folly::detail::to_ascii_powers<16, uint64_t>;
EXPECT_EQ(16, _::size);
EXPECT_EQ(1, _::data.data[0]);
EXPECT_EQ(16, _::data.data[1]);
EXPECT_EQ(256, _::data.data[2]);
}
TEST_F(ToAsciiTest, to_ascii_powers_u64_10) {
using _ = folly::detail::to_ascii_powers<10, uint64_t>;
EXPECT_EQ(20, _::size);
EXPECT_EQ(1, _::data.data[0]);
EXPECT_EQ(10, _::data.data[1]);
EXPECT_EQ(100, _::data.data[2]);
}
TEST_F(ToAsciiTest, to_ascii_powers_u64_8) {
using _ = folly::detail::to_ascii_powers<8, uint64_t>;
EXPECT_EQ(22, _::size);
EXPECT_EQ(1, _::data.data[0]);
EXPECT_EQ(8, _::data.data[1]);
EXPECT_EQ(64, _::data.data[2]);
}
TEST_F(ToAsciiTest, to_ascii_size_max_u64_16) {
constexpr auto const actual = folly::to_ascii_size_max<16, uint64_t>();
EXPECT_EQ(16, actual);
}
TEST_F(ToAsciiTest, to_ascii_size_max_u64_10) {
constexpr auto const actual = folly::to_ascii_size_max<10, uint64_t>();
EXPECT_EQ(20, actual);
}
TEST_F(ToAsciiTest, to_ascii_size_max_u64_8) {
constexpr auto const actual = folly::to_ascii_size_max<8, uint64_t>();
EXPECT_EQ(22, actual);
}
TEST_F(ToAsciiTest, to_ascii_size_imuls_16) {
to_ascii_size_16(folly::detail::to_ascii_size_imuls<16>);
}
TEST_F(ToAsciiTest, to_ascii_size_idivs_16) {
to_ascii_size_16(folly::detail::to_ascii_size_idivs<16>);
}
TEST_F(ToAsciiTest, to_ascii_size_array_16) {
to_ascii_size_16(folly::detail::to_ascii_size_array<16>);
}
TEST_F(ToAsciiTest, to_ascii_size_clzll_16) {
to_ascii_size_16(folly::detail::to_ascii_size_clzll<16>);
}
TEST_F(ToAsciiTest, to_ascii_size_imuls_10) {
to_ascii_size_10(folly::detail::to_ascii_size_imuls<10>);
}
TEST_F(ToAsciiTest, to_ascii_size_idivs_10) {
to_ascii_size_10(folly::detail::to_ascii_size_idivs<10>);
}
TEST_F(ToAsciiTest, to_ascii_size_array_10) {
to_ascii_size_10(folly::detail::to_ascii_size_array<10>);
}
TEST_F(ToAsciiTest, to_ascii_size_clzll_10) {
to_ascii_size_10(folly::detail::to_ascii_size_clzll<10>);
}
TEST_F(ToAsciiTest, to_ascii_size_imuls_8) {
to_ascii_size_8(folly::detail::to_ascii_size_imuls<8>);
}
TEST_F(ToAsciiTest, to_ascii_size_idivs_8) {
to_ascii_size_8(folly::detail::to_ascii_size_idivs<8>);
}
TEST_F(ToAsciiTest, to_ascii_size_array_8) {
to_ascii_size_8(folly::detail::to_ascii_size_array<8>);
}
TEST_F(ToAsciiTest, to_ascii_size_clzll_8) {
to_ascii_size_8(folly::detail::to_ascii_size_clzll<8>);
}
TEST_F(ToAsciiTest, to_ascii_size_imuls_10_compare) {
to_ascii_size_u64_10_compare(folly::detail::to_ascii_size_imuls<10>);
}
TEST_F(ToAsciiTest, to_ascii_size_idivs_10_compare) {
to_ascii_size_u64_10_compare(folly::detail::to_ascii_size_idivs<10>);
}
TEST_F(ToAsciiTest, to_ascii_size_array_10_compare) {
to_ascii_size_u64_10_compare(folly::detail::to_ascii_size_array<10>);
}
TEST_F(ToAsciiTest, to_ascii_size_clzll_10_compare) {
to_ascii_size_u64_10_compare(folly::detail::to_ascii_size_clzll<10>);
}
template <uint64_t Base>
struct inputs;
template <>
struct inputs<10> {
static constexpr uint64_t const data[22] = {
0,
1,
12,
123,
1234,
12345,
123456,
1234567,
12345678,
123456789,
1234567890,
12345678901,
123456789012,
1234567890123,
12345678901234,
123456789012345,
1234567890123456,
12345678901234567,
123456789012345678,
1234567890123456789,
-2ull,
-1ull,
};
};
constexpr uint64_t const inputs<10>::data[22];
template <uint64_t Base, typename F>
static void to_ascii_compare(F _) {
for (auto const n : inputs<Base>::data) {
auto const expected = std::to_string(n);
SCOPED_TRACE(expected);
std::string actual(folly::to_ascii_size_max<Base, uint64_t>(), '\0');
auto const size = _(&actual[0], n);
EXPECT_EQ(expected.size(), size);
EXPECT_EQ(expected, actual.substr(0, size));
}
}
using abc = folly::to_ascii_alphabet_lower;
TEST_F(ToAsciiTest, to_ascii_basic_10_compare) {
to_ascii_compare<10>([](auto out, auto v) {
return folly::detail::to_ascii_with_basic<10, abc>(out, v);
});
}
TEST_F(ToAsciiTest, to_ascii_array_10_compare) {
to_ascii_compare<10>([](auto out, auto v) {
return folly::detail::to_ascii_with_array<10, abc>(out, v);
});
}
TEST_F(ToAsciiTest, to_ascii_table_10_compare) {
to_ascii_compare<10>([](auto out, auto v) {
return folly::detail::to_ascii_with_table<10, abc>(out, v);
});
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment