X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2Ftest%2FConvTest.cpp;h=fd26293e4965ead7d7abb4c0002b1174d65c1872;hb=e64edf8025220bf74a2c38b8d38bd3a1d0a1e9eb;hp=ffe7934e23b125c80c448791e20bc02e8fa8b80d;hpb=b6a8eb2e3e7dae0c48cf1ace5299bc777b2753cd;p=folly.git diff --git a/folly/test/ConvTest.cpp b/folly/test/ConvTest.cpp index ffe7934e..fd26293e 100644 --- a/folly/test/ConvTest.cpp +++ b/folly/test/ConvTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2014 Facebook, Inc. + * Copyright 2016 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,29 +14,110 @@ * limitations under the License. */ -#include +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS 1 +#endif + +#include #include #include -#include #include +#include +#include #include +#include #include +#include using namespace std; using namespace folly; -static int8_t s8; -static uint8_t u8; -static int16_t s16; -static uint16_t u16; -static int32_t s32; -static uint32_t u32; -static int64_t s64; -static uint64_t u64; + +TEST(Conv, digits10) { + char buffer[100]; + uint64_t power; + + // first, some basic sniff tests + EXPECT_EQ( 1, digits10(0)); + EXPECT_EQ( 1, digits10(1)); + EXPECT_EQ( 1, digits10(9)); + EXPECT_EQ( 2, digits10(10)); + EXPECT_EQ( 2, digits10(99)); + EXPECT_EQ( 3, digits10(100)); + EXPECT_EQ( 3, digits10(999)); + EXPECT_EQ( 4, digits10(1000)); + EXPECT_EQ( 4, digits10(9999)); + EXPECT_EQ(20, digits10(18446744073709551615ULL)); + + // try the first X nonnegatives. + // Covers some more cases of 2^p, 10^p + for (uint64_t i = 0; i < 100000; i++) { + snprintf(buffer, sizeof(buffer), "%" PRIu64, i); + EXPECT_EQ(strlen(buffer), digits10(i)); + } + + // try powers of 2 + power = 1; + for (int p = 0; p < 64; p++) { + snprintf(buffer, sizeof(buffer), "%" PRIu64, power); + EXPECT_EQ(strlen(buffer), digits10(power)); + snprintf(buffer, sizeof(buffer), "%" PRIu64, power - 1); + EXPECT_EQ(strlen(buffer), digits10(power - 1)); + snprintf(buffer, sizeof(buffer), "%" PRIu64, power + 1); + EXPECT_EQ(strlen(buffer), digits10(power + 1)); + power *= 2; + } + + // try powers of 10 + power = 1; + for (int p = 0; p < 20; p++) { + snprintf(buffer, sizeof(buffer), "%" PRIu64, power); + EXPECT_EQ(strlen(buffer), digits10(power)); + snprintf(buffer, sizeof(buffer), "%" PRIu64, power - 1); + EXPECT_EQ(strlen(buffer), digits10(power - 1)); + snprintf(buffer, sizeof(buffer), "%" PRIu64, power + 1); + EXPECT_EQ(strlen(buffer), digits10(power + 1)); + power *= 10; + } +} + +// Test to(T) +TEST(Conv, Type2Type) { + bool boolV = true; + EXPECT_EQ(to(boolV), true); + + int intV = 42; + EXPECT_EQ(to(intV), 42); + + float floatV = 4.2f; + EXPECT_EQ(to(floatV), 4.2f); + + double doubleV = 0.42; + EXPECT_EQ(to(doubleV), 0.42); + + std::string stringV = "StdString"; + EXPECT_EQ(to(stringV), "StdString"); + + folly::fbstring fbStrV = "FBString"; + EXPECT_EQ(to(fbStrV), "FBString"); + + folly::StringPiece spV("StringPiece"); + EXPECT_EQ(to(spV), "StringPiece"); + + // Rvalues + EXPECT_EQ(to(true), true); + EXPECT_EQ(to(42), 42); + EXPECT_EQ(to(4.2f), 4.2f); + EXPECT_EQ(to(.42), .42); + EXPECT_EQ(to(std::string("Hello")), "Hello"); + EXPECT_EQ(to(folly::fbstring("hello")), "hello"); + EXPECT_EQ(to(folly::StringPiece("Forty Two")), + "Forty Two"); +} TEST(Conv, Integral2Integral) { // Same size, different signs - s64 = numeric_limits::max(); + int64_t s64 = numeric_limits::max(); EXPECT_EQ(to(s64), s64); s64 = numeric_limits::max(); @@ -44,7 +125,7 @@ TEST(Conv, Integral2Integral) { } TEST(Conv, Floating2Floating) { - float f1 = 1e3; + float f1 = 1e3f; double d1 = to(f1); EXPECT_EQ(f1, d1); @@ -124,6 +205,15 @@ void test128Bit2String() { svalue = 0; EXPECT_EQ(to(svalue), "0"); + value = ~__int128(0); + EXPECT_EQ(to(value), "340282366920938463463374607431768211455"); + + svalue = -(Uint(1) << 127); + EXPECT_EQ(to(svalue), "-170141183460469231731687303715884105728"); + + svalue = (Uint(1) << 127) - 1; + EXPECT_EQ(to(svalue), "170141183460469231731687303715884105727"); + // TODO: the following do not compile to<__int128> ... #if 0 @@ -142,8 +232,8 @@ void test128Bit2String() { #endif TEST(Conv, Integral2String) { - testIntegral2String(); - testIntegral2String(); + testIntegral2String(); + testIntegral2String(); #if FOLLY_HAVE_INT128_T test128Bit2String(); @@ -339,9 +429,9 @@ void testString2Integral() { } TEST(Conv, String2Integral) { - testString2Integral(); - testString2Integral(); - testString2Integral(); + testString2Integral(); + testString2Integral(); + testString2Integral(); // Testing the behavior of the StringPiece* API // StringPiece* normally parses as much valid data as it can, @@ -481,15 +571,32 @@ TEST(Conv, FBStringToString) { } TEST(Conv, StringPieceToDouble) { - string s = "2134123.125 zorro"; - StringPiece pc(s); - EXPECT_EQ(to(&pc), 2134123.125); - EXPECT_EQ(pc, " zorro"); - - EXPECT_THROW(to(StringPiece(s)), std::range_error); - EXPECT_EQ(to(StringPiece(s.data(), pc.data())), 2134123.125); + vector> strs{ + make_tuple("2134123.125 zorro", " zorro", 2134123.125), + make_tuple(" 2134123.125 zorro", " zorro", 2134123.125), + make_tuple(" 2134123.125 zorro", " zorro", 2134123.125), + make_tuple(" 2134123.125 zorro ", " zorro ", 2134123.125), + make_tuple("2134123.125zorro", "zorro", 2134123.125), + make_tuple("0 zorro", " zorro", 0.0), + make_tuple(" 0 zorro", " zorro", 0.0), + make_tuple(" 0 zorro", " zorro", 0.0), + make_tuple(" 0 zorro ", " zorro ", 0.0), + make_tuple("0zorro", "zorro", 0.0), + make_tuple("0.0 zorro", " zorro", 0.0), + make_tuple(" 0.0 zorro", " zorro", 0.0), + make_tuple(" 0.0 zorro", " zorro", 0.0), + make_tuple(" 0.0 zorro ", " zorro ", 0.0), + make_tuple("0.0zorro", "zorro", 0.0), + }; + for (const auto& s : strs) { + StringPiece pc(get<0>(s)); + EXPECT_EQ(get<2>(s), to(&pc)) << "\"" << get<0>(s) << "\""; + EXPECT_EQ(get<1>(s), pc); + EXPECT_THROW(to(StringPiece(get<0>(s))), std::range_error); + EXPECT_EQ(get<2>(s), to(StringPiece(get<0>(s), pc.data()))); + } -// Test NaN conversion + // Test NaN conversion try { to("not a number"); EXPECT_TRUE(false); @@ -556,6 +663,7 @@ TEST(Conv, DoubleToInt) { EXPECT_EQ(i, 42); try { auto i = to(42.1); + LOG(ERROR) << "to returned " << i << " instead of throwing"; EXPECT_TRUE(false); } catch (std::range_error& e) { //LOG(INFO) << e.what(); @@ -570,7 +678,9 @@ TEST(Conv, EnumToInt) { EXPECT_EQ(j, 42); try { auto i = to(y); - LOG(ERROR) << static_cast(i); + LOG(ERROR) << "to returned " + << static_cast(i) + << " instead of throwing"; EXPECT_TRUE(false); } catch (std::range_error& e) { //LOG(INFO) << e.what(); @@ -593,6 +703,9 @@ TEST(Conv, IntToEnum) { EXPECT_EQ(j, 100); try { auto i = to(5000000000L); + LOG(ERROR) << "to returned " + << static_cast(i) + << " instead of throwing"; EXPECT_TRUE(false); } catch (std::range_error& e) { //LOG(INFO) << e.what(); @@ -609,30 +722,24 @@ TEST(Conv, UnsignedEnum) { EXPECT_EQ(e, x); try { auto i = to(x); - LOG(ERROR) << to(x); + LOG(ERROR) << "to returned " << i << " instead of throwing"; EXPECT_TRUE(false); } catch (std::range_error& e) { } } -#if defined(__clang__) || __GNUC_PREREQ(4, 7) -// to and to(enum class) only supported in gcc 4.7 onwards - TEST(Conv, UnsignedEnumClass) { enum class E : uint32_t { x = 3000000000U }; auto u = to(E::x); EXPECT_GT(u, 0); EXPECT_EQ(u, 3000000000U); - auto s = to(E::x); - EXPECT_EQ("3000000000", s); - auto e = to(3000000000U); - EXPECT_EQ(e, E::x); - try { - auto i = to(E::x); - LOG(ERROR) << to(E::x); - EXPECT_TRUE(false); - } catch (std::range_error& e) { - } + EXPECT_EQ("3000000000", to(E::x)); + EXPECT_EQ(E::x, to(3000000000U)); + EXPECT_EQ(E::x, to("3000000000")); + E e; + EXPECT_TRUE(parseTo("3000000000", e).hasValue()); + EXPECT_EQ(E::x, e); + EXPECT_THROW(to(E::x), std::range_error); } // Multi-argument to uses toAppend, a different code path than @@ -644,7 +751,16 @@ TEST(Conv, EnumClassToString) { EXPECT_EQ("foo.65", to("foo.", A::z)); } -#endif // gcc 4.7 onwards +TEST(Conv, IntegralToBool) { + EXPECT_FALSE(to(0)); + EXPECT_FALSE(to(0ul)); + + EXPECT_TRUE(to(1)); + EXPECT_TRUE(to(1ul)); + + EXPECT_TRUE(to(-42)); + EXPECT_TRUE(to(42ul)); +} template void testStr2Bool() { @@ -710,6 +826,305 @@ TEST(Conv, StringToBool) { EXPECT_EQ(buf5, sp5.begin()); } +TEST(Conv, Transform) { + const std::vector in{1, 2, 3}; + std::vector out(in.size()); + std::transform(in.begin(), in.end(), out.begin(), to); + const std::vector ref{"1", "2", "3"}; + EXPECT_EQ(ref, out); +} + +TEST(Conv, FloatToInt) { + EXPECT_EQ(to(42.0f), 42); + EXPECT_EQ(to(-128.0f), int8_t(-128)); + EXPECT_THROW(to(-129.0), std::range_error); + EXPECT_THROW(to(127.001), std::range_error); + EXPECT_THROW(to(-0.0001), std::range_error); + EXPECT_THROW( + to(static_cast(std::numeric_limits::max())), + std::range_error); +} + +TEST(Conv, IntToFloat) { + EXPECT_EQ(to(42ULL), 42.0); + EXPECT_EQ(to(int8_t(-128)), -128.0); + EXPECT_THROW( + to(std::numeric_limits::max()), std::range_error); + EXPECT_THROW( + to(std::numeric_limits::max()), std::range_error); + EXPECT_THROW( + to(std::numeric_limits::min() + 1), std::range_error); +#if FOLLY_HAVE_INT128_T + EXPECT_THROW( + to(std::numeric_limits::max()), + std::range_error); + EXPECT_THROW( + to(std::numeric_limits<__int128>::max()), std::range_error); + EXPECT_THROW( + to(std::numeric_limits<__int128>::min() + 1), std::range_error); +#endif +} + +TEST(Conv, BoolToFloat) { + EXPECT_EQ(to(true), 1.0); + EXPECT_EQ(to(false), 0.0); +} + +TEST(Conv, FloatToBool) { + EXPECT_EQ(to(1.0), true); + EXPECT_EQ(to(0.0), false); + EXPECT_EQ(to(2.7), true); + EXPECT_EQ(to(std::numeric_limits::max()), true); + EXPECT_EQ(to(std::numeric_limits::min()), true); + EXPECT_EQ(to(std::numeric_limits::lowest()), true); + EXPECT_EQ(to(std::numeric_limits::quiet_NaN()), true); + EXPECT_EQ(to(std::numeric_limits::infinity()), true); + EXPECT_EQ(to(-std::numeric_limits::infinity()), true); +} + +namespace { + +template +void testConvError( + F&& expr, + const char* exprStr, + ConversionCode code, + const char* value, + bool quotedValue, + int line) { + std::string where = to(__FILE__, "(", line, "): "); + try { + auto res = expr(); + EXPECT_TRUE(false) << where << exprStr << " -> " << res; + } catch (const ConversionError& e) { + EXPECT_EQ(code, e.errorCode()) << where << exprStr; + std::string str(e.what()); + EXPECT_FALSE(str.empty()) << where << exprStr << " -> " << str; + auto pos = str.find(':'); + if (value) { + std::ostringstream exp; + exp << str.substr(0, pos) + ": "; + if (quotedValue) { + exp << "\"" << value << "\""; + } else { + exp << value; + } + EXPECT_EQ(exp.str(), str) << where << exprStr << " -> " << str; + } else { + EXPECT_EQ(pos, std::string::npos) << where << exprStr << " -> " << str; + } + } +} +} + +#define EXPECT_CONV_ERROR_QUOTE(expr, code, value, quoted) \ + testConvError( \ + [&] { return expr; }, \ + #expr, \ + ConversionCode::code, \ + value, \ + quoted, \ + __LINE__) + +#define EXPECT_CONV_ERROR(expr, code, value) \ + EXPECT_CONV_ERROR_QUOTE(expr, code, value, true) + +#define EXPECT_CONV_ERROR_STR(type, str, code) \ + EXPECT_CONV_ERROR(to(str), code, str) + +#define EXPECT_CONV_ERROR_STR_NOVAL(type, str, code) \ + EXPECT_CONV_ERROR(to(str), code, nullptr) + +TEST(Conv, ConversionErrorStrToBool) { + EXPECT_CONV_ERROR_STR_NOVAL(bool, StringPiece(), EMPTY_INPUT_STRING); + EXPECT_CONV_ERROR_STR_NOVAL(bool, "", EMPTY_INPUT_STRING); + EXPECT_CONV_ERROR_STR(bool, " ", EMPTY_INPUT_STRING); + EXPECT_CONV_ERROR_STR(bool, " 11 ", BOOL_OVERFLOW); + EXPECT_CONV_ERROR_STR(bool, "other ", BOOL_INVALID_VALUE); + EXPECT_CONV_ERROR_STR(bool, " bla", BOOL_INVALID_VALUE); + EXPECT_CONV_ERROR(to(" offbla"), NON_WHITESPACE_AFTER_END, "bla"); +} + +TEST(Conv, ConversionErrorStrToFloat) { + EXPECT_CONV_ERROR_STR_NOVAL(float, StringPiece(), EMPTY_INPUT_STRING); + EXPECT_CONV_ERROR_STR_NOVAL(float, "", EMPTY_INPUT_STRING); + EXPECT_CONV_ERROR_STR(float, " ", EMPTY_INPUT_STRING); + EXPECT_CONV_ERROR_STR(float, " junk", STRING_TO_FLOAT_ERROR); + EXPECT_CONV_ERROR(to(" 1bla"), NON_WHITESPACE_AFTER_END, "bla"); +} + +TEST(Conv, ConversionErrorStrToInt) { + // empty string handling + EXPECT_CONV_ERROR_STR_NOVAL(int, StringPiece(), EMPTY_INPUT_STRING); + EXPECT_CONV_ERROR_STR_NOVAL(int, "", EMPTY_INPUT_STRING); + EXPECT_CONV_ERROR_STR(int, " ", EMPTY_INPUT_STRING); + + // signed integers + EXPECT_CONV_ERROR_STR(int, " *", INVALID_LEADING_CHAR); + EXPECT_CONV_ERROR_STR(int, " +", NO_DIGITS); + EXPECT_CONV_ERROR_STR(int, " +*", NON_DIGIT_CHAR); + EXPECT_CONV_ERROR_STR(int8_t, " 128", POSITIVE_OVERFLOW); + EXPECT_CONV_ERROR_STR(int8_t, " -129", NEGATIVE_OVERFLOW); + EXPECT_CONV_ERROR_STR(int8_t, " 1000", POSITIVE_OVERFLOW); + EXPECT_CONV_ERROR_STR(int8_t, "-1000", NEGATIVE_OVERFLOW); + EXPECT_CONV_ERROR(to(" -13bla"), NON_WHITESPACE_AFTER_END, "bla"); + + // unsigned integers + EXPECT_CONV_ERROR_STR(unsigned, " -", NON_DIGIT_CHAR); + EXPECT_CONV_ERROR_STR(uint8_t, " 256", POSITIVE_OVERFLOW); + EXPECT_CONV_ERROR(to("42bla"), NON_WHITESPACE_AFTER_END, "bla"); +} + +#define EXPECT_CONV_ERROR_PP_VAL(type, str, code, val) \ + do { \ + StringPiece input(str); \ + EXPECT_CONV_ERROR(to(input.begin(), input.end()), code, val); \ + } while (0) + +#define EXPECT_CONV_ERROR_PP(type, str, code) \ + EXPECT_CONV_ERROR_PP_VAL(type, str, code, str) + +TEST(Conv, ConversionErrorPtrPairToInt) { + // signed integers + EXPECT_CONV_ERROR_PP(int, "", INVALID_LEADING_CHAR); + EXPECT_CONV_ERROR_PP(int, " ", INVALID_LEADING_CHAR); + EXPECT_CONV_ERROR_PP(int, "*", INVALID_LEADING_CHAR); + EXPECT_CONV_ERROR_PP(int, "+", NO_DIGITS); + EXPECT_CONV_ERROR_PP(int8_t, "128", POSITIVE_OVERFLOW); + EXPECT_CONV_ERROR_PP(int8_t, "-129", NEGATIVE_OVERFLOW); + EXPECT_CONV_ERROR_PP(int8_t, "1000", POSITIVE_OVERFLOW); + EXPECT_CONV_ERROR_PP(int8_t, "-1000", NEGATIVE_OVERFLOW); + EXPECT_CONV_ERROR_PP(int, "-junk", NON_DIGIT_CHAR); + + // unsigned integers + EXPECT_CONV_ERROR_PP(unsigned, "", NO_DIGITS); + EXPECT_CONV_ERROR_PP(uint8_t, "256", POSITIVE_OVERFLOW); + EXPECT_CONV_ERROR_PP(unsigned, "junk", NON_DIGIT_CHAR); +} + +namespace { + +template +std::string prefixWithType(V value) { + std::ostringstream oss; +#ifdef FOLLY_HAS_RTTI + oss << "(" << demangle(typeid(T)) << ") "; +#endif + oss << to(value); + return oss.str(); +} +} + +#define EXPECT_CONV_ERROR_ARITH(type, val, code) \ + EXPECT_CONV_ERROR_QUOTE( \ + to(val), code, prefixWithType(val).c_str(), false) + +TEST(Conv, ConversionErrorIntToInt) { + EXPECT_CONV_ERROR_ARITH(signed char, 128, ARITH_POSITIVE_OVERFLOW); + EXPECT_CONV_ERROR_ARITH(unsigned char, -1, ARITH_NEGATIVE_OVERFLOW); +} + +TEST(Conv, ConversionErrorFloatToFloat) { + EXPECT_CONV_ERROR_ARITH( + float, std::numeric_limits::max(), ARITH_POSITIVE_OVERFLOW); + EXPECT_CONV_ERROR_ARITH( + float, std::numeric_limits::lowest(), ARITH_NEGATIVE_OVERFLOW); +} + +TEST(Conv, ConversionErrorIntToFloat) { + EXPECT_CONV_ERROR_ARITH( + float, std::numeric_limits::max(), ARITH_LOSS_OF_PRECISION); +} + +TEST(Conv, ConversionErrorFloatToInt) { + EXPECT_CONV_ERROR_ARITH(int8_t, 65.5, ARITH_LOSS_OF_PRECISION); +} + +TEST(Conv, TryStringToBool) { + auto rv1 = folly::tryTo("xxxx"); + EXPECT_FALSE(rv1.hasValue()); + auto rv2 = folly::tryTo("false"); + EXPECT_TRUE(rv2.hasValue()); + EXPECT_FALSE(rv2.value()); + auto rv3 = folly::tryTo("yes"); + EXPECT_TRUE(rv3.hasValue()); + EXPECT_TRUE(rv3.value()); +} + +TEST(Conv, TryStringToInt) { + auto rv1 = folly::tryTo("1000000000000000000000000000000"); + EXPECT_FALSE(rv1.hasValue()); + auto rv2 = folly::tryTo("4711"); + EXPECT_TRUE(rv2.hasValue()); + EXPECT_EQ(rv2.value(), 4711); +} + +TEST(Conv, TryStringToFloat) { + auto rv1 = folly::tryTo(""); + EXPECT_FALSE(rv1.hasValue()); + auto rv2 = folly::tryTo("3.14"); + EXPECT_TRUE(rv2.hasValue()); + EXPECT_NEAR(rv2.value(), 3.14, 1e-5); +} + +TEST(Conv, TryStringToDouble) { + auto rv1 = folly::tryTo(""); + EXPECT_FALSE(rv1.hasValue()); + auto rv2 = folly::tryTo("3.14"); + EXPECT_TRUE(rv2.hasValue()); + EXPECT_NEAR(rv2.value(), 3.14, 1e-10); +} + +TEST(Conv, TryIntToInt) { + auto rv1 = folly::tryTo(256); + EXPECT_FALSE(rv1.hasValue()); + auto rv2 = folly::tryTo(255); + EXPECT_TRUE(rv2.hasValue()); + EXPECT_EQ(rv2.value(), 255); +} + +TEST(Conv, TryFloatToFloat) { + auto rv1 = folly::tryTo(1e100); + EXPECT_FALSE(rv1.hasValue()); + auto rv2 = folly::tryTo(25.5f); + EXPECT_TRUE(rv2.hasValue()); + EXPECT_NEAR(rv2.value(), 25.5, 1e-10); +} + +TEST(Conv, TryFloatToInt) { + auto rv1 = folly::tryTo(100.001); + EXPECT_FALSE(rv1.hasValue()); + auto rv2 = folly::tryTo(100.0); + EXPECT_TRUE(rv2.hasValue()); + EXPECT_EQ(rv2.value(), 100); +} + +TEST(Conv, TryIntToFloat) { + auto rv1 = folly::tryTo(std::numeric_limits::max()); + EXPECT_FALSE(rv1.hasValue()); + auto rv2 = folly::tryTo(1000ULL); + EXPECT_TRUE(rv2.hasValue()); + EXPECT_EQ(rv2.value(), 1000.0f); +} + +TEST(Conv, TryPtrPairToInt) { + StringPiece sp1("1000000000000000000000000000000"); + auto rv1 = folly::tryTo(sp1.begin(), sp1.end()); + EXPECT_FALSE(rv1.hasValue()); + StringPiece sp2("4711"); + auto rv2 = folly::tryTo(sp2.begin(), sp2.end()); + EXPECT_TRUE(rv2.hasValue()); + EXPECT_EQ(rv2.value(), 4711); + StringPiece sp3("-4711"); + auto rv3 = folly::tryTo(sp3.begin(), sp3.end()); + EXPECT_TRUE(rv3.hasValue()); + EXPECT_EQ(rv3.value(), -4711); + StringPiece sp4("4711"); + auto rv4 = folly::tryTo(sp4.begin(), sp4.end()); + EXPECT_TRUE(rv4.hasValue()); + EXPECT_EQ(rv4.value(), 4711); +} + TEST(Conv, NewUint64ToString) { char buf[21]; @@ -748,326 +1163,65 @@ TEST(Conv, NewUint64ToString) { #undef THE_GREAT_EXPECTATIONS } -//////////////////////////////////////////////////////////////////////////////// -// Benchmarks for ASCII to int conversion -//////////////////////////////////////////////////////////////////////////////// -// @author: Rajat Goel (rajat) - -static int64_t handwrittenAtoi(const char* start, const char* end) { - - bool positive = true; - int64_t retVal = 0; - - if (start == end) { - throw std::runtime_error("empty string"); - } - - while (start < end && isspace(*start)) { - ++start; - } +TEST(Conv, allocate_size) { + std::string str1 = "meh meh meh"; + std::string str2 = "zdech zdech zdech"; - switch (*start) { - case '-': - positive = false; - case '+': - ++start; - default:; - } + auto res1 = folly::to(str1, ".", str2); + EXPECT_EQ(res1, str1 + "." + str2); - while (start < end && *start >= '0' && *start <= '9') { - auto const newRetVal = retVal * 10 + (*start++ - '0'); - if (newRetVal < retVal) { - throw std::runtime_error("overflow"); - } - retVal = newRetVal; - } + std::string res2; //empty + toAppendFit(str1, str2, 1, &res2); + EXPECT_EQ(res2, str1 + str2 + "1"); - if (start != end) { - throw std::runtime_error("extra chars at the end"); - } - - return positive ? retVal : -retVal; + std::string res3; + toAppendDelimFit(",", str1, str2, &res3); + EXPECT_EQ(res3, str1 + "," + str2); } -static StringPiece pc1 = "1234567890123456789"; - -void handwrittenAtoiMeasure(unsigned int n, unsigned int digits) { - auto p = pc1.subpiece(pc1.size() - digits, digits); - FOR_EACH_RANGE (i, 0, n) { - doNotOptimizeAway(handwrittenAtoi(p.begin(), p.end())); +namespace my { +struct Dimensions { + int w, h; + std::tuple tuple_view() const { + return tie(w, h); } -} - -void follyAtoiMeasure(unsigned int n, unsigned int digits) { - auto p = pc1.subpiece(pc1.size() - digits, digits); - FOR_EACH_RANGE (i, 0, n) { - doNotOptimizeAway(folly::to(p.begin(), p.end())); + bool operator==(const Dimensions& other) const { + return this->tuple_view() == other.tuple_view(); } -} - -void clibAtoiMeasure(unsigned int n, unsigned int digits) { - auto p = pc1.subpiece(pc1.size() - digits, digits); - assert(*p.end() == 0); - static_assert(sizeof(long) == 8, "64-bit long assumed"); - FOR_EACH_RANGE (i, 0, n) { - doNotOptimizeAway(atol(p.begin())); - } -} - -void clibStrtoulMeasure(unsigned int n, unsigned int digits) { - auto p = pc1.subpiece(pc1.size() - digits, digits); - assert(*p.end() == 0); - char * endptr; - FOR_EACH_RANGE (i, 0, n) { - doNotOptimizeAway(strtoul(p.begin(), &endptr, 10)); - } -} - -void lexicalCastMeasure(unsigned int n, unsigned int digits) { - auto p = pc1.subpiece(pc1.size() - digits, digits); - assert(*p.end() == 0); - FOR_EACH_RANGE (i, 0, n) { - doNotOptimizeAway(boost::lexical_cast(p.begin())); - } -} - -// Benchmarks for unsigned to string conversion, raw - -unsigned u64ToAsciiTable(uint64_t value, char* dst) { - static const char digits[201] = - "00010203040506070809" - "10111213141516171819" - "20212223242526272829" - "30313233343536373839" - "40414243444546474849" - "50515253545556575859" - "60616263646566676869" - "70717273747576777879" - "80818283848586878889" - "90919293949596979899"; - - uint32_t const length = digits10(value); - uint32_t next = length - 1; - while (value >= 100) { - auto const i = (value % 100) * 2; - value /= 100; - dst[next] = digits[i + 1]; - dst[next - 1] = digits[i]; - next -= 2; - } - // Handle last 1-2 digits - if (value < 10) { - dst[next] = '0' + uint32_t(value); - } else { - auto i = uint32_t(value) * 2; - dst[next] = digits[i + 1]; - dst[next - 1] = digits[i]; - } - return length; -} - -void u64ToAsciiTableBM(unsigned int n, uint64_t value) { - // This is too fast, need to do 10 times per iteration - char buf[20]; - FOR_EACH_RANGE (i, 0, n) { - doNotOptimizeAway(u64ToAsciiTable(value + n, buf)); - } -} - -unsigned u64ToAsciiClassic(uint64_t value, char* dst) { - // Write backwards. - char* next = (char*)dst; - char* start = next; - do { - *next++ = '0' + (value % 10); - value /= 10; - } while (value != 0); - unsigned length = next - start; - - // Reverse in-place. - next--; - while (next > start) { - char swap = *next; - *next = *start; - *start = swap; - next--; - start++; - } - return length; -} +}; -void u64ToAsciiClassicBM(unsigned int n, uint64_t value) { - // This is too fast, need to do 10 times per iteration - char buf[20]; - FOR_EACH_RANGE (i, 0, n) { - doNotOptimizeAway(u64ToAsciiClassic(value + n, buf)); - } +Expected parseTo( + folly::StringPiece in, + Dimensions& out) { + return parseTo(in, out.w) + .then([](StringPiece sp) { return sp.removePrefix("x"), sp; }) + .then([&](StringPiece sp) { return parseTo(sp, out.h); }); } -void u64ToAsciiFollyBM(unsigned int n, uint64_t value) { - // This is too fast, need to do 10 times per iteration - char buf[20]; - FOR_EACH_RANGE (i, 0, n) { - doNotOptimizeAway(uint64ToBufferUnsafe(value + n, buf)); - } +template +void toAppend(const Dimensions& in, String* result) { + folly::toAppend(in.w, 'x', in.h, result); } -// Benchmark uitoa with string append - -void u2aAppendClassicBM(unsigned int n, uint64_t value) { - string s; - FOR_EACH_RANGE (i, 0, n) { - // auto buf = &s.back() + 1; - char buffer[20]; - s.append(buffer, u64ToAsciiClassic(value, buffer)); - doNotOptimizeAway(s.size()); - } +size_t estimateSpaceNeeded(const Dimensions&in) { + return 2000 + folly::estimateSpaceNeeded(in.w) + + folly::estimateSpaceNeeded(in.h); } - -void u2aAppendFollyBM(unsigned int n, uint64_t value) { - string s; - FOR_EACH_RANGE (i, 0, n) { - // auto buf = &s.back() + 1; - char buffer[20]; - s.append(buffer, uint64ToBufferUnsafe(value, buffer)); - doNotOptimizeAway(s.size()); - } } -template -struct StringIdenticalToBM { - void operator()(unsigned int n, size_t len) const { - String s; - BENCHMARK_SUSPEND { s.append(len, '0'); } - FOR_EACH_RANGE (i, 0, n) { - String result = to(s); - doNotOptimizeAway(result.size()); - } - } -}; - -template -struct StringVariadicToBM { - void operator()(unsigned int n, size_t len) const { - String s; - BENCHMARK_SUSPEND { s.append(len, '0'); } - FOR_EACH_RANGE (i, 0, n) { - String result = to(s, nullptr); - doNotOptimizeAway(result.size()); - } - } -}; - -static size_t bigInt = 11424545345345; -static size_t smallInt = 104; -static char someString[] = "this is some nice string"; -static char otherString[] = "this is a long string, so it's not so nice"; -static char reallyShort[] = "meh"; -static std::string stdString = "std::strings are very nice"; -static float fValue = 1.2355; -static double dValue = 345345345.435; - -BENCHMARK(preallocateTestNoFloat, n) { - for (int i=0; i < n; ++i) { - auto val1 = to(bigInt, someString, stdString, otherString); - auto val3 = to(reallyShort, smallInt); - auto val2 = to(bigInt, stdString); - auto val4 = to(bigInt, stdString, dValue, otherString); - auto val5 = to(bigInt, someString, reallyShort); - } +TEST(Conv, custom_kkproviders) { + my::Dimensions expected{7, 8}; + EXPECT_EQ(expected, folly::to("7x8")); + auto str = folly::to(expected); + EXPECT_EQ("7x8", str); + // make sure above implementation of estimateSpaceNeeded() is used. + EXPECT_GT(str.capacity(), 2000); + EXPECT_LT(str.capacity(), 2500); } -BENCHMARK(preallocateTestFloat, n) { - for (int i=0; i < n; ++i) { - auto val1 = to(stdString, ',', fValue, dValue); - auto val2 = to(stdString, ',', dValue); - } -} -BENCHMARK_DRAW_LINE(); - -static const StringIdenticalToBM stringIdenticalToBM; -static const StringVariadicToBM stringVariadicToBM; -static const StringIdenticalToBM fbstringIdenticalToBM; -static const StringVariadicToBM fbstringVariadicToBM; - -#define DEFINE_BENCHMARK_GROUP(n) \ - BENCHMARK_PARAM(u64ToAsciiClassicBM, n); \ - BENCHMARK_RELATIVE_PARAM(u64ToAsciiTableBM, n); \ - BENCHMARK_RELATIVE_PARAM(u64ToAsciiFollyBM, n); \ - BENCHMARK_DRAW_LINE(); - -DEFINE_BENCHMARK_GROUP(1); -DEFINE_BENCHMARK_GROUP(12); -DEFINE_BENCHMARK_GROUP(123); -DEFINE_BENCHMARK_GROUP(1234); -DEFINE_BENCHMARK_GROUP(12345); -DEFINE_BENCHMARK_GROUP(123456); -DEFINE_BENCHMARK_GROUP(1234567); -DEFINE_BENCHMARK_GROUP(12345678); -DEFINE_BENCHMARK_GROUP(123456789); -DEFINE_BENCHMARK_GROUP(1234567890); -DEFINE_BENCHMARK_GROUP(12345678901); -DEFINE_BENCHMARK_GROUP(123456789012); -DEFINE_BENCHMARK_GROUP(1234567890123); -DEFINE_BENCHMARK_GROUP(12345678901234); -DEFINE_BENCHMARK_GROUP(123456789012345); -DEFINE_BENCHMARK_GROUP(1234567890123456); -DEFINE_BENCHMARK_GROUP(12345678901234567); -DEFINE_BENCHMARK_GROUP(123456789012345678); -DEFINE_BENCHMARK_GROUP(1234567890123456789); -DEFINE_BENCHMARK_GROUP(12345678901234567890U); - -#undef DEFINE_BENCHMARK_GROUP - -#define DEFINE_BENCHMARK_GROUP(n) \ - BENCHMARK_PARAM(clibAtoiMeasure, n); \ - BENCHMARK_RELATIVE_PARAM(lexicalCastMeasure, n); \ - BENCHMARK_RELATIVE_PARAM(handwrittenAtoiMeasure, n); \ - BENCHMARK_RELATIVE_PARAM(follyAtoiMeasure, n); \ - BENCHMARK_DRAW_LINE(); - -DEFINE_BENCHMARK_GROUP(1); -DEFINE_BENCHMARK_GROUP(2); -DEFINE_BENCHMARK_GROUP(3); -DEFINE_BENCHMARK_GROUP(4); -DEFINE_BENCHMARK_GROUP(5); -DEFINE_BENCHMARK_GROUP(6); -DEFINE_BENCHMARK_GROUP(7); -DEFINE_BENCHMARK_GROUP(8); -DEFINE_BENCHMARK_GROUP(9); -DEFINE_BENCHMARK_GROUP(10); -DEFINE_BENCHMARK_GROUP(11); -DEFINE_BENCHMARK_GROUP(12); -DEFINE_BENCHMARK_GROUP(13); -DEFINE_BENCHMARK_GROUP(14); -DEFINE_BENCHMARK_GROUP(15); -DEFINE_BENCHMARK_GROUP(16); -DEFINE_BENCHMARK_GROUP(17); -DEFINE_BENCHMARK_GROUP(18); -DEFINE_BENCHMARK_GROUP(19); - -#undef DEFINE_BENCHMARK_GROUP - -#define DEFINE_BENCHMARK_GROUP(T, n) \ - BENCHMARK_PARAM(T ## VariadicToBM, n); \ - BENCHMARK_RELATIVE_PARAM(T ## IdenticalToBM, n); \ - BENCHMARK_DRAW_LINE(); - -DEFINE_BENCHMARK_GROUP(string, 32); -DEFINE_BENCHMARK_GROUP(string, 1024); -DEFINE_BENCHMARK_GROUP(string, 32768); -DEFINE_BENCHMARK_GROUP(fbstring, 32); -DEFINE_BENCHMARK_GROUP(fbstring, 1024); -DEFINE_BENCHMARK_GROUP(fbstring, 32768); - -#undef DEFINE_BENCHMARK_GROUP - -int main(int argc, char** argv) { - testing::InitGoogleTest(&argc, argv); - gflags::ParseCommandLineFlags(&argc, &argv, true); - auto ret = RUN_ALL_TESTS(); - if (!ret && FLAGS_benchmark) { - folly::runBenchmarks(); - } - return ret; +TEST(Conv, TryToThenWithVoid) { + auto x = tryTo("42").then([](int) {}); + EXPECT_TRUE(x.hasValue()); + Unit u = x.value(); + (void)u; }