| /* |
| * Copyright 2015 Facebook, Inc. |
| * |
| * 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/String.h> |
| |
| #include <string> |
| #include <cstdarg> |
| #include <random> |
| #include <boost/algorithm/string.hpp> |
| #include <gtest/gtest.h> |
| |
| #include <folly/Benchmark.h> |
| |
| using namespace folly; |
| using namespace std; |
| |
| namespace { |
| |
| /* For some reason, GCC 4.9 doesn't implicitly cast 'const char[]' to |
| * 'const char*' to folly::StringPiece. This cast helps it along. |
| */ |
| inline const char* to_ccs(const char ra[]) |
| { |
| return static_cast<const char*>(ra); |
| } |
| |
| } /* anonymous namespace */ |
| |
| TEST(StringPrintf, BasicTest) { |
| EXPECT_EQ("abc", stringPrintf("%s", "abc")); |
| EXPECT_EQ("abc", stringPrintf("%sbc", "a")); |
| EXPECT_EQ("abc", stringPrintf("a%sc", "b")); |
| EXPECT_EQ("abc", stringPrintf("ab%s", "c")); |
| |
| EXPECT_EQ("abc", stringPrintf("abc")); |
| } |
| |
| TEST(StringPrintf, NumericFormats) { |
| EXPECT_EQ("12", stringPrintf("%d", 12)); |
| EXPECT_EQ("5000000000", stringPrintf("%ld", 5000000000UL)); |
| EXPECT_EQ("5000000000", stringPrintf("%ld", 5000000000L)); |
| EXPECT_EQ("-5000000000", stringPrintf("%ld", -5000000000L)); |
| EXPECT_EQ("-1", stringPrintf("%d", 0xffffffff)); |
| EXPECT_EQ("-1", stringPrintf("%ld", 0xffffffffffffffff)); |
| EXPECT_EQ("-1", stringPrintf("%ld", 0xffffffffffffffffUL)); |
| |
| EXPECT_EQ("7.7", stringPrintf("%1.1f", 7.7)); |
| EXPECT_EQ("7.7", stringPrintf("%1.1lf", 7.7)); |
| EXPECT_EQ("7.70000000000000018", |
| stringPrintf("%.17f", 7.7)); |
| EXPECT_EQ("7.70000000000000018", |
| stringPrintf("%.17lf", 7.7)); |
| } |
| |
| TEST(StringPrintf, Appending) { |
| string s; |
| stringAppendf(&s, "a%s", "b"); |
| stringAppendf(&s, "%c", 'c'); |
| EXPECT_EQ(s, "abc"); |
| stringAppendf(&s, " %d", 123); |
| EXPECT_EQ(s, "abc 123"); |
| } |
| |
| void vprintfCheck(const char* expected, const char* fmt, ...) { |
| va_list apOrig; |
| va_start(apOrig, fmt); |
| SCOPE_EXIT { |
| va_end(apOrig); |
| }; |
| va_list ap; |
| va_copy(ap, apOrig); |
| SCOPE_EXIT { |
| va_end(ap); |
| }; |
| |
| // Check both APIs for calling stringVPrintf() |
| EXPECT_EQ(expected, stringVPrintf(fmt, ap)); |
| va_end(ap); |
| va_copy(ap, apOrig); |
| |
| std::string out; |
| stringVPrintf(&out, fmt, ap); |
| va_end(ap); |
| va_copy(ap, apOrig); |
| EXPECT_EQ(expected, out); |
| |
| // Check stringVAppendf() as well |
| std::string prefix = "foobar"; |
| out = prefix; |
| EXPECT_EQ(prefix + expected, stringVAppendf(&out, fmt, ap)); |
| va_end(ap); |
| va_copy(ap, apOrig); |
| } |
| |
| void vprintfError(const char* fmt, ...) { |
| va_list ap; |
| va_start(ap, fmt); |
| SCOPE_EXIT { |
| va_end(ap); |
| }; |
| |
| // OSX's sprintf family does not return a negative number on a bad format |
| // string, but Linux does. It's unclear to me which behavior is more |
| // correct. |
| #ifdef HAVE_VSNPRINTF_ERRORS |
| EXPECT_THROW({stringVPrintf(fmt, ap);}, |
| std::runtime_error); |
| #endif |
| } |
| |
| TEST(StringPrintf, VPrintf) { |
| vprintfCheck("foo", "%s", "foo"); |
| vprintfCheck("long string requiring reallocation 1 2 3 0x12345678", |
| "%s %s %d %d %d %#x", |
| "long string", "requiring reallocation", 1, 2, 3, 0x12345678); |
| vprintfError("bogus%", "foo"); |
| } |
| |
| TEST(StringPrintf, VariousSizes) { |
| // Test a wide variety of output sizes, making sure to cross the |
| // vsnprintf buffer boundary implementation detail. |
| for (int i = 0; i < 4096; ++i) { |
| string expected(i + 1, 'a'); |
| expected = "X" + expected + "X"; |
| string result = stringPrintf("%s", expected.c_str()); |
| EXPECT_EQ(expected.size(), result.size()); |
| EXPECT_EQ(expected, result); |
| } |
| |
| EXPECT_EQ("abc12345678910111213141516171819202122232425xyz", |
| stringPrintf("abc%d%d%d%d%d%d%d%d%d%d%d%d%d%d" |
| "%d%d%d%d%d%d%d%d%d%d%dxyz", |
| 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, |
| 17, 18, 19, 20, 21, 22, 23, 24, 25)); |
| } |
| |
| TEST(StringPrintf, oldStringPrintfTests) { |
| EXPECT_EQ(string("a/b/c/d"), |
| stringPrintf("%s/%s/%s/%s", "a", "b", "c", "d")); |
| |
| EXPECT_EQ(string(" 5 10"), |
| stringPrintf("%5d %5d", 5, 10)); |
| |
| // check printing w/ a big buffer |
| for (int size = (1 << 8); size <= (1 << 15); size <<= 1) { |
| string a(size, 'z'); |
| string b = stringPrintf("%s", a.c_str()); |
| EXPECT_EQ(a.size(), b.size()); |
| } |
| } |
| |
| TEST(StringPrintf, oldStringAppendf) { |
| string s = "hello"; |
| stringAppendf(&s, "%s/%s/%s/%s", "a", "b", "c", "d"); |
| EXPECT_EQ(string("helloa/b/c/d"), s); |
| } |
| |
| // A simple benchmark that tests various output sizes for a simple |
| // input; the goal is to measure the output buffer resize code cost. |
| void stringPrintfOutputSize(int iters, int param) { |
| string buffer; |
| BENCHMARK_SUSPEND { buffer.resize(param, 'x'); } |
| |
| for (int64_t i = 0; i < iters; ++i) { |
| string s = stringPrintf("msg: %d, %d, %s", 10, 20, buffer.c_str()); |
| } |
| } |
| |
| // The first few of these tend to fit in the inline buffer, while the |
| // subsequent ones cross that limit, trigger a second vsnprintf, and |
| // exercise a different codepath. |
| BENCHMARK_PARAM(stringPrintfOutputSize, 1) |
| BENCHMARK_PARAM(stringPrintfOutputSize, 4) |
| BENCHMARK_PARAM(stringPrintfOutputSize, 16) |
| BENCHMARK_PARAM(stringPrintfOutputSize, 64) |
| BENCHMARK_PARAM(stringPrintfOutputSize, 256) |
| BENCHMARK_PARAM(stringPrintfOutputSize, 1024) |
| |
| // Benchmark simple stringAppendf behavior to show a pathology Lovro |
| // reported (t5735468). |
| BENCHMARK(stringPrintfAppendfBenchmark, iters) { |
| for (unsigned int i = 0; i < iters; ++i) { |
| string s; |
| BENCHMARK_SUSPEND { s.reserve(300000); } |
| for (int j = 0; j < 300000; ++j) { |
| stringAppendf(&s, "%d", 1); |
| } |
| } |
| } |
| |
| TEST(Escape, cEscape) { |
| EXPECT_EQ("hello world", cEscape<std::string>("hello world")); |
| EXPECT_EQ("hello \\\\world\\\" goodbye", |
| cEscape<std::string>("hello \\world\" goodbye")); |
| EXPECT_EQ("hello\\nworld", cEscape<std::string>("hello\nworld")); |
| EXPECT_EQ("hello\\377\\376", cEscape<std::string>("hello\xff\xfe")); |
| } |
| |
| TEST(Escape, cUnescape) { |
| EXPECT_EQ("hello world", cUnescape<std::string>("hello world")); |
| EXPECT_EQ("hello \\world\" goodbye", |
| cUnescape<std::string>("hello \\\\world\\\" goodbye")); |
| EXPECT_EQ("hello\nworld", cUnescape<std::string>("hello\\nworld")); |
| EXPECT_EQ("hello\nworld", cUnescape<std::string>("hello\\012world")); |
| EXPECT_EQ("hello\nworld", cUnescape<std::string>("hello\\x0aworld")); |
| EXPECT_EQ("hello\xff\xfe", cUnescape<std::string>("hello\\377\\376")); |
| EXPECT_EQ("hello\xff\xfe", cUnescape<std::string>("hello\\xff\\xfe")); |
| |
| EXPECT_THROW({cUnescape<std::string>("hello\\");}, |
| std::invalid_argument); |
| EXPECT_THROW({cUnescape<std::string>("hello\\x");}, |
| std::invalid_argument); |
| EXPECT_THROW({cUnescape<std::string>("hello\\q");}, |
| std::invalid_argument); |
| } |
| |
| TEST(Escape, uriEscape) { |
| EXPECT_EQ("hello%2c%20%2fworld", uriEscape<std::string>("hello, /world")); |
| EXPECT_EQ("hello%2c%20/world", uriEscape<std::string>("hello, /world", |
| UriEscapeMode::PATH)); |
| EXPECT_EQ("hello%2c+%2fworld", uriEscape<std::string>("hello, /world", |
| UriEscapeMode::QUERY)); |
| EXPECT_EQ( |
| "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.~", |
| uriEscape<std::string>( |
| "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.~") |
| ); |
| } |
| |
| TEST(Escape, uriUnescape) { |
| EXPECT_EQ("hello, /world", uriUnescape<std::string>("hello, /world")); |
| EXPECT_EQ("hello, /world", uriUnescape<std::string>("hello%2c%20%2fworld")); |
| EXPECT_EQ("hello,+/world", uriUnescape<std::string>("hello%2c+%2fworld")); |
| EXPECT_EQ("hello, /world", uriUnescape<std::string>("hello%2c+%2fworld", |
| UriEscapeMode::QUERY)); |
| EXPECT_EQ("hello/", uriUnescape<std::string>("hello%2f")); |
| EXPECT_EQ("hello/", uriUnescape<std::string>("hello%2F")); |
| EXPECT_THROW({uriUnescape<std::string>("hello%");}, |
| std::invalid_argument); |
| EXPECT_THROW({uriUnescape<std::string>("hello%2");}, |
| std::invalid_argument); |
| EXPECT_THROW({uriUnescape<std::string>("hello%2g");}, |
| std::invalid_argument); |
| } |
| |
| namespace { |
| void expectPrintable(StringPiece s) { |
| for (char c : s) { |
| EXPECT_LE(32, c); |
| EXPECT_GE(127, c); |
| } |
| } |
| } // namespace |
| |
| TEST(Escape, uriEscapeAllCombinations) { |
| char c[3]; |
| c[2] = '\0'; |
| StringPiece in(c, 2); |
| fbstring tmp; |
| fbstring out; |
| for (int i = 0; i < 256; ++i) { |
| c[0] = i; |
| for (int j = 0; j < 256; ++j) { |
| c[1] = j; |
| tmp.clear(); |
| out.clear(); |
| uriEscape(in, tmp); |
| expectPrintable(tmp); |
| uriUnescape(tmp, out); |
| EXPECT_EQ(in, out); |
| } |
| } |
| } |
| |
| namespace { |
| bool isHex(int v) { |
| return ((v >= '0' && v <= '9') || |
| (v >= 'A' && v <= 'F') || |
| (v >= 'a' && v <= 'f')); |
| } |
| } // namespace |
| |
| TEST(Escape, uriUnescapePercentDecoding) { |
| char c[4] = {'%', '\0', '\0', '\0'}; |
| StringPiece in(c, 3); |
| fbstring out; |
| unsigned int expected = 0; |
| for (int i = 0; i < 256; ++i) { |
| c[1] = i; |
| for (int j = 0; j < 256; ++j) { |
| c[2] = j; |
| if (isHex(i) && isHex(j)) { |
| out.clear(); |
| uriUnescape(in, out); |
| EXPECT_EQ(1, out.size()); |
| EXPECT_EQ(1, sscanf(c + 1, "%x", &expected)); |
| unsigned char v = out[0]; |
| EXPECT_EQ(expected, v); |
| } else { |
| EXPECT_THROW({uriUnescape(in, out);}, std::invalid_argument); |
| } |
| } |
| } |
| } |
| |
| namespace { |
| fbstring cbmString; |
| fbstring cbmEscapedString; |
| fbstring cEscapedString; |
| fbstring cUnescapedString; |
| const size_t kCBmStringLength = 64 << 10; |
| const uint32_t kCPrintablePercentage = 90; |
| |
| fbstring uribmString; |
| fbstring uribmEscapedString; |
| fbstring uriEscapedString; |
| fbstring uriUnescapedString; |
| const size_t kURIBmStringLength = 256; |
| const uint32_t kURIPassThroughPercentage = 50; |
| |
| void initBenchmark() { |
| std::mt19937 rnd; |
| |
| // C escape |
| std::uniform_int_distribution<uint32_t> printable(32, 126); |
| std::uniform_int_distribution<uint32_t> nonPrintable(0, 160); |
| std::uniform_int_distribution<uint32_t> percentage(0, 99); |
| |
| cbmString.reserve(kCBmStringLength); |
| for (size_t i = 0; i < kCBmStringLength; ++i) { |
| unsigned char c; |
| if (percentage(rnd) < kCPrintablePercentage) { |
| c = printable(rnd); |
| } else { |
| c = nonPrintable(rnd); |
| // Generate characters in both non-printable ranges: |
| // 0..31 and 127..255 |
| if (c >= 32) { |
| c += (126 - 32) + 1; |
| } |
| } |
| cbmString.push_back(c); |
| } |
| |
| cbmEscapedString = cEscape<fbstring>(cbmString); |
| |
| // URI escape |
| std::uniform_int_distribution<uint32_t> passthrough('a', 'z'); |
| std::string encodeChars = " ?!\"',+[]"; |
| std::uniform_int_distribution<uint32_t> encode(0, encodeChars.size() - 1); |
| |
| uribmString.reserve(kURIBmStringLength); |
| for (size_t i = 0; i < kURIBmStringLength; ++i) { |
| unsigned char c; |
| if (percentage(rnd) < kURIPassThroughPercentage) { |
| c = passthrough(rnd); |
| } else { |
| c = encodeChars[encode(rnd)]; |
| } |
| uribmString.push_back(c); |
| } |
| |
| uribmEscapedString = uriEscape<fbstring>(uribmString); |
| } |
| |
| BENCHMARK(BM_cEscape, iters) { |
| while (iters--) { |
| cEscapedString = cEscape<fbstring>(cbmString); |
| doNotOptimizeAway(cEscapedString.size()); |
| } |
| } |
| |
| BENCHMARK(BM_cUnescape, iters) { |
| while (iters--) { |
| cUnescapedString = cUnescape<fbstring>(cbmEscapedString); |
| doNotOptimizeAway(cUnescapedString.size()); |
| } |
| } |
| |
| BENCHMARK(BM_uriEscape, iters) { |
| while (iters--) { |
| uriEscapedString = uriEscape<fbstring>(uribmString); |
| doNotOptimizeAway(uriEscapedString.size()); |
| } |
| } |
| |
| BENCHMARK(BM_uriUnescape, iters) { |
| while (iters--) { |
| uriUnescapedString = uriUnescape<fbstring>(uribmEscapedString); |
| doNotOptimizeAway(uriUnescapedString.size()); |
| } |
| } |
| |
| } // namespace |
| |
| namespace { |
| |
| double pow2(int exponent) { |
| return double(int64_t(1) << exponent); |
| } |
| |
| } // namespace |
| struct PrettyTestCase{ |
| std::string prettyString; |
| double realValue; |
| PrettyType prettyType; |
| }; |
| |
| PrettyTestCase prettyTestCases[] = |
| { |
| {string("8.53e+07 s "), 85.3e6, PRETTY_TIME}, |
| {string("8.53e+07 s "), 85.3e6, PRETTY_TIME}, |
| {string("85.3 ms"), 85.3e-3, PRETTY_TIME}, |
| {string("85.3 us"), 85.3e-6, PRETTY_TIME}, |
| {string("85.3 ns"), 85.3e-9, PRETTY_TIME}, |
| {string("85.3 ps"), 85.3e-12, PRETTY_TIME}, |
| {string("8.53e-14 s "), 85.3e-15, PRETTY_TIME}, |
| |
| {string("0 s "), 0, PRETTY_TIME}, |
| {string("1 s "), 1.0, PRETTY_TIME}, |
| {string("1 ms"), 1.0e-3, PRETTY_TIME}, |
| {string("1 us"), 1.0e-6, PRETTY_TIME}, |
| {string("1 ns"), 1.0e-9, PRETTY_TIME}, |
| {string("1 ps"), 1.0e-12, PRETTY_TIME}, |
| |
| // check bytes printing |
| {string("853 B "), 853., PRETTY_BYTES}, |
| {string("833 kB"), 853.e3, PRETTY_BYTES}, |
| {string("813.5 MB"), 853.e6, PRETTY_BYTES}, |
| {string("7.944 GB"), 8.53e9, PRETTY_BYTES}, |
| {string("794.4 GB"), 853.e9, PRETTY_BYTES}, |
| {string("775.8 TB"), 853.e12, PRETTY_BYTES}, |
| |
| {string("0 B "), 0, PRETTY_BYTES}, |
| {string("1 B "), pow2(0), PRETTY_BYTES}, |
| {string("1 kB"), pow2(10), PRETTY_BYTES}, |
| {string("1 MB"), pow2(20), PRETTY_BYTES}, |
| {string("1 GB"), pow2(30), PRETTY_BYTES}, |
| {string("1 TB"), pow2(40), PRETTY_BYTES}, |
| |
| {string("853 B "), 853., PRETTY_BYTES_IEC}, |
| {string("833 KiB"), 853.e3, PRETTY_BYTES_IEC}, |
| {string("813.5 MiB"), 853.e6, PRETTY_BYTES_IEC}, |
| {string("7.944 GiB"), 8.53e9, PRETTY_BYTES_IEC}, |
| {string("794.4 GiB"), 853.e9, PRETTY_BYTES_IEC}, |
| {string("775.8 TiB"), 853.e12, PRETTY_BYTES_IEC}, |
| |
| {string("0 B "), 0, PRETTY_BYTES_IEC}, |
| {string("1 B "), pow2(0), PRETTY_BYTES_IEC}, |
| {string("1 KiB"), pow2(10), PRETTY_BYTES_IEC}, |
| {string("1 MiB"), pow2(20), PRETTY_BYTES_IEC}, |
| {string("1 GiB"), pow2(30), PRETTY_BYTES_IEC}, |
| {string("1 TiB"), pow2(40), PRETTY_BYTES_IEC}, |
| |
| // check bytes metric printing |
| {string("853 B "), 853., PRETTY_BYTES_METRIC}, |
| {string("853 kB"), 853.e3, PRETTY_BYTES_METRIC}, |
| {string("853 MB"), 853.e6, PRETTY_BYTES_METRIC}, |
| {string("8.53 GB"), 8.53e9, PRETTY_BYTES_METRIC}, |
| {string("853 GB"), 853.e9, PRETTY_BYTES_METRIC}, |
| {string("853 TB"), 853.e12, PRETTY_BYTES_METRIC}, |
| |
| {string("0 B "), 0, PRETTY_BYTES_METRIC}, |
| {string("1 B "), 1.0, PRETTY_BYTES_METRIC}, |
| {string("1 kB"), 1.0e+3, PRETTY_BYTES_METRIC}, |
| {string("1 MB"), 1.0e+6, PRETTY_BYTES_METRIC}, |
| |
| {string("1 GB"), 1.0e+9, PRETTY_BYTES_METRIC}, |
| {string("1 TB"), 1.0e+12, PRETTY_BYTES_METRIC}, |
| |
| // check metric-units (powers of 1000) printing |
| {string("853 "), 853., PRETTY_UNITS_METRIC}, |
| {string("853 k"), 853.e3, PRETTY_UNITS_METRIC}, |
| {string("853 M"), 853.e6, PRETTY_UNITS_METRIC}, |
| {string("8.53 bil"), 8.53e9, PRETTY_UNITS_METRIC}, |
| {string("853 bil"), 853.e9, PRETTY_UNITS_METRIC}, |
| {string("853 tril"), 853.e12, PRETTY_UNITS_METRIC}, |
| |
| // check binary-units (powers of 1024) printing |
| {string("0 "), 0, PRETTY_UNITS_BINARY}, |
| {string("1 "), pow2(0), PRETTY_UNITS_BINARY}, |
| {string("1 k"), pow2(10), PRETTY_UNITS_BINARY}, |
| {string("1 M"), pow2(20), PRETTY_UNITS_BINARY}, |
| {string("1 G"), pow2(30), PRETTY_UNITS_BINARY}, |
| {string("1 T"), pow2(40), PRETTY_UNITS_BINARY}, |
| |
| {string("1023 "), pow2(10) - 1, PRETTY_UNITS_BINARY}, |
| {string("1024 k"), pow2(20) - 1, PRETTY_UNITS_BINARY}, |
| {string("1024 M"), pow2(30) - 1, PRETTY_UNITS_BINARY}, |
| {string("1024 G"), pow2(40) - 1, PRETTY_UNITS_BINARY}, |
| |
| {string("0 "), 0, PRETTY_UNITS_BINARY_IEC}, |
| {string("1 "), pow2(0), PRETTY_UNITS_BINARY_IEC}, |
| {string("1 Ki"), pow2(10), PRETTY_UNITS_BINARY_IEC}, |
| {string("1 Mi"), pow2(20), PRETTY_UNITS_BINARY_IEC}, |
| {string("1 Gi"), pow2(30), PRETTY_UNITS_BINARY_IEC}, |
| {string("1 Ti"), pow2(40), PRETTY_UNITS_BINARY_IEC}, |
| |
| {string("1023 "), pow2(10) - 1, PRETTY_UNITS_BINARY_IEC}, |
| {string("1024 Ki"), pow2(20) - 1, PRETTY_UNITS_BINARY_IEC}, |
| {string("1024 Mi"), pow2(30) - 1, PRETTY_UNITS_BINARY_IEC}, |
| {string("1024 Gi"), pow2(40) - 1, PRETTY_UNITS_BINARY_IEC}, |
| |
| //check border SI cases |
| |
| {string("1 Y"), 1e24, PRETTY_SI}, |
| {string("10 Y"), 1e25, PRETTY_SI}, |
| {string("1 y"), 1e-24, PRETTY_SI}, |
| {string("10 y"), 1e-23, PRETTY_SI}, |
| |
| // check that negative values work |
| {string("-85.3 s "), -85.3, PRETTY_TIME}, |
| {string("-85.3 ms"), -85.3e-3, PRETTY_TIME}, |
| {string("-85.3 us"), -85.3e-6, PRETTY_TIME}, |
| {string("-85.3 ns"), -85.3e-9, PRETTY_TIME}, |
| // end of test |
| {string("endoftest"), 0, PRETTY_NUM_TYPES} |
| }; |
| |
| TEST(PrettyPrint, Basic) { |
| for (int i = 0; prettyTestCases[i].prettyType != PRETTY_NUM_TYPES; ++i){ |
| const PrettyTestCase& prettyTest = prettyTestCases[i]; |
| EXPECT_EQ(prettyTest.prettyString, |
| prettyPrint(prettyTest.realValue, prettyTest.prettyType)); |
| } |
| } |
| |
| TEST(PrettyToDouble, Basic) { |
| // check manually created tests |
| for (int i = 0; prettyTestCases[i].prettyType != PRETTY_NUM_TYPES; ++i){ |
| PrettyTestCase testCase = prettyTestCases[i]; |
| PrettyType formatType = testCase.prettyType; |
| double x = testCase.realValue; |
| std::string testString = testCase.prettyString; |
| double recoveredX; |
| try{ |
| recoveredX = prettyToDouble(testString, formatType); |
| } catch (std::range_error &ex){ |
| EXPECT_TRUE(false); |
| } |
| double relativeError = fabs(x) < 1e-5 ? (x-recoveredX) : |
| (x - recoveredX) / x; |
| EXPECT_NEAR(0, relativeError, 1e-3); |
| } |
| |
| // checks for compatibility with prettyPrint over the whole parameter space |
| for (int i = 0 ; i < PRETTY_NUM_TYPES; ++i){ |
| PrettyType formatType = static_cast<PrettyType>(i); |
| for (double x = 1e-18; x < 1e40; x *= 1.9){ |
| bool addSpace = static_cast<PrettyType> (i) == PRETTY_SI; |
| for (int it = 0; it < 2; ++it, addSpace = true){ |
| double recoveredX; |
| try{ |
| recoveredX = prettyToDouble(prettyPrint(x, formatType, addSpace), |
| formatType); |
| } catch (std::range_error &ex){ |
| EXPECT_TRUE(false); |
| } |
| double relativeError = (x - recoveredX) / x; |
| EXPECT_NEAR(0, relativeError, 1e-3); |
| } |
| } |
| } |
| |
| // check for incorrect values |
| EXPECT_THROW(prettyToDouble("10Mx", PRETTY_SI), std::range_error); |
| EXPECT_THROW(prettyToDouble("10 Mx", PRETTY_SI), std::range_error); |
| EXPECT_THROW(prettyToDouble("10 M x", PRETTY_SI), std::range_error); |
| |
| StringPiece testString = "10Mx"; |
| EXPECT_DOUBLE_EQ(prettyToDouble(&testString, PRETTY_UNITS_METRIC), 10e6); |
| EXPECT_EQ(testString, to_ccs("x")); |
| } |
| |
| TEST(PrettyPrint, HexDump) { |
| std::string a("abc\x00\x02\xa0", 6); // embedded NUL |
| EXPECT_EQ( |
| "00000000 61 62 63 00 02 a0 " |
| "|abc... |\n", |
| hexDump(a.data(), a.size())); |
| |
| a = "abcdefghijklmnopqrstuvwxyz"; |
| EXPECT_EQ( |
| "00000000 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 " |
| "|abcdefghijklmnop|\n" |
| "00000010 71 72 73 74 75 76 77 78 79 7a " |
| "|qrstuvwxyz |\n", |
| hexDump(a.data(), a.size())); |
| } |
| |
| TEST(System, errnoStr) { |
| errno = EACCES; |
| EXPECT_EQ(EACCES, errno); |
| EXPECT_EQ(EACCES, errno); // twice to make sure EXPECT_EQ doesn't change it |
| |
| fbstring expected = strerror(ENOENT); |
| |
| errno = EACCES; |
| EXPECT_EQ(expected, errnoStr(ENOENT)); |
| // Ensure that errno isn't changed |
| EXPECT_EQ(EACCES, errno); |
| |
| // Per POSIX, all errno values are positive, so -1 is invalid |
| errnoStr(-1); |
| |
| // Ensure that errno isn't changed |
| EXPECT_EQ(EACCES, errno); |
| } |
| |
| namespace { |
| |
| template<template<class,class> class VectorType> |
| void splitTest() { |
| VectorType<string,std::allocator<string> > parts; |
| |
| folly::split(',', "a,b,c", parts); |
| EXPECT_EQ(parts.size(), 3); |
| EXPECT_EQ(parts[0], "a"); |
| EXPECT_EQ(parts[1], "b"); |
| EXPECT_EQ(parts[2], "c"); |
| parts.clear(); |
| |
| folly::split(',', StringPiece("a,b,c"), parts); |
| EXPECT_EQ(parts.size(), 3); |
| EXPECT_EQ(parts[0], "a"); |
| EXPECT_EQ(parts[1], "b"); |
| EXPECT_EQ(parts[2], "c"); |
| parts.clear(); |
| |
| folly::split(',', string("a,b,c"), parts); |
| EXPECT_EQ(parts.size(), 3); |
| EXPECT_EQ(parts[0], "a"); |
| EXPECT_EQ(parts[1], "b"); |
| EXPECT_EQ(parts[2], "c"); |
| parts.clear(); |
| |
| folly::split(',', "a,,c", parts); |
| EXPECT_EQ(parts.size(), 3); |
| EXPECT_EQ(parts[0], "a"); |
| EXPECT_EQ(parts[1], ""); |
| EXPECT_EQ(parts[2], "c"); |
| parts.clear(); |
| |
| folly::split(',', string("a,,c"), parts); |
| EXPECT_EQ(parts.size(), 3); |
| EXPECT_EQ(parts[0], "a"); |
| EXPECT_EQ(parts[1], ""); |
| EXPECT_EQ(parts[2], "c"); |
| parts.clear(); |
| |
| folly::split(',', "a,,c", parts, true); |
| EXPECT_EQ(parts.size(), 2); |
| EXPECT_EQ(parts[0], "a"); |
| EXPECT_EQ(parts[1], "c"); |
| parts.clear(); |
| |
| folly::split(',', string("a,,c"), parts, true); |
| EXPECT_EQ(parts.size(), 2); |
| EXPECT_EQ(parts[0], "a"); |
| EXPECT_EQ(parts[1], "c"); |
| parts.clear(); |
| |
| folly::split(',', string(",,a,,c,,,"), parts, true); |
| EXPECT_EQ(parts.size(), 2); |
| EXPECT_EQ(parts[0], "a"); |
| EXPECT_EQ(parts[1], "c"); |
| parts.clear(); |
| |
| // test multiple split w/o clear |
| folly::split(',', ",,a,,c,,,", parts, true); |
| EXPECT_EQ(parts.size(), 2); |
| EXPECT_EQ(parts[0], "a"); |
| EXPECT_EQ(parts[1], "c"); |
| folly::split(',', ",,a,,c,,,", parts, true); |
| EXPECT_EQ(parts.size(), 4); |
| EXPECT_EQ(parts[2], "a"); |
| EXPECT_EQ(parts[3], "c"); |
| parts.clear(); |
| |
| // test splits that with multi-line delimiter |
| folly::split("ab", "dabcabkdbkab", parts, true); |
| EXPECT_EQ(parts.size(), 3); |
| EXPECT_EQ(parts[0], "d"); |
| EXPECT_EQ(parts[1], "c"); |
| EXPECT_EQ(parts[2], "kdbk"); |
| parts.clear(); |
| |
| // test last part is shorter than the delimiter |
| folly::split("bc", "abcd", parts, true); |
| EXPECT_EQ(parts.size(), 2); |
| EXPECT_EQ(parts[0], "a"); |
| EXPECT_EQ(parts[1], "d"); |
| parts.clear(); |
| |
| string orig = "ab2342asdfv~~!"; |
| folly::split("", orig, parts, true); |
| EXPECT_EQ(parts.size(), 1); |
| EXPECT_EQ(parts[0], orig); |
| parts.clear(); |
| |
| folly::split("452x;o38asfsajsdlfdf.j", "asfds", parts, true); |
| EXPECT_EQ(parts.size(), 1); |
| EXPECT_EQ(parts[0], "asfds"); |
| parts.clear(); |
| |
| folly::split("a", "", parts, true); |
| EXPECT_EQ(parts.size(), 0); |
| parts.clear(); |
| |
| folly::split("a", "", parts); |
| EXPECT_EQ(parts.size(), 1); |
| EXPECT_EQ(parts[0], ""); |
| parts.clear(); |
| |
| folly::split("a", StringPiece(), parts, true); |
| EXPECT_EQ(parts.size(), 0); |
| parts.clear(); |
| |
| folly::split("a", StringPiece(), parts); |
| EXPECT_EQ(parts.size(), 1); |
| EXPECT_EQ(parts[0], ""); |
| parts.clear(); |
| |
| folly::split("a", "abcdefg", parts, true); |
| EXPECT_EQ(parts.size(), 1); |
| EXPECT_EQ(parts[0], "bcdefg"); |
| parts.clear(); |
| |
| orig = "All, , your base, are , , belong to us"; |
| folly::split(", ", orig, parts, true); |
| EXPECT_EQ(parts.size(), 4); |
| EXPECT_EQ(parts[0], "All"); |
| EXPECT_EQ(parts[1], "your base"); |
| EXPECT_EQ(parts[2], "are "); |
| EXPECT_EQ(parts[3], "belong to us"); |
| parts.clear(); |
| folly::split(", ", orig, parts); |
| EXPECT_EQ(parts.size(), 6); |
| EXPECT_EQ(parts[0], "All"); |
| EXPECT_EQ(parts[1], ""); |
| EXPECT_EQ(parts[2], "your base"); |
| EXPECT_EQ(parts[3], "are "); |
| EXPECT_EQ(parts[4], ""); |
| EXPECT_EQ(parts[5], "belong to us"); |
| parts.clear(); |
| |
| orig = ", Facebook, rul,es!, "; |
| folly::split(", ", orig, parts, true); |
| EXPECT_EQ(parts.size(), 2); |
| EXPECT_EQ(parts[0], "Facebook"); |
| EXPECT_EQ(parts[1], "rul,es!"); |
| parts.clear(); |
| folly::split(", ", orig, parts); |
| EXPECT_EQ(parts.size(), 4); |
| EXPECT_EQ(parts[0], ""); |
| EXPECT_EQ(parts[1], "Facebook"); |
| EXPECT_EQ(parts[2], "rul,es!"); |
| EXPECT_EQ(parts[3], ""); |
| } |
| |
| template<template<class,class> class VectorType> |
| void piecesTest() { |
| VectorType<StringPiece,std::allocator<StringPiece> > pieces; |
| VectorType<StringPiece,std::allocator<StringPiece> > pieces2; |
| |
| folly::split(',', "a,b,c", pieces); |
| EXPECT_EQ(pieces.size(), 3); |
| EXPECT_EQ(pieces[0], to_ccs("a")); |
| EXPECT_EQ(pieces[1], to_ccs("b")); |
| EXPECT_EQ(pieces[2], to_ccs("c")); |
| |
| pieces.clear(); |
| |
| folly::split(',', "a,,c", pieces); |
| EXPECT_EQ(pieces.size(), 3); |
| EXPECT_EQ(pieces[0], to_ccs("a")); |
| EXPECT_EQ(pieces[1], to_ccs("")); |
| EXPECT_EQ(pieces[2], to_ccs("c")); |
| pieces.clear(); |
| |
| folly::split(',', "a,,c", pieces, true); |
| EXPECT_EQ(pieces.size(), 2); |
| EXPECT_EQ(pieces[0], to_ccs("a")); |
| EXPECT_EQ(pieces[1], to_ccs("c")); |
| pieces.clear(); |
| |
| folly::split(',', ",,a,,c,,,", pieces, true); |
| EXPECT_EQ(pieces.size(), 2); |
| EXPECT_EQ(pieces[0], to_ccs("a")); |
| EXPECT_EQ(pieces[1], to_ccs("c")); |
| pieces.clear(); |
| |
| // test multiple split w/o clear |
| folly::split(',', ",,a,,c,,,", pieces, true); |
| EXPECT_EQ(pieces.size(), 2); |
| EXPECT_EQ(pieces[0], to_ccs("a")); |
| EXPECT_EQ(pieces[1], to_ccs("c")); |
| folly::split(',', ",,a,,c,,,", pieces, true); |
| EXPECT_EQ(pieces.size(), 4); |
| EXPECT_EQ(pieces[2], to_ccs("a")); |
| EXPECT_EQ(pieces[3], to_ccs("c")); |
| pieces.clear(); |
| |
| // test multiple split rounds |
| folly::split(",", "a_b,c_d", pieces); |
| EXPECT_EQ(pieces.size(), 2); |
| EXPECT_EQ(pieces[0], to_ccs("a_b")); |
| EXPECT_EQ(pieces[1], to_ccs("c_d")); |
| folly::split("_", pieces[0], pieces2); |
| EXPECT_EQ(pieces2.size(), 2); |
| EXPECT_EQ(pieces2[0], to_ccs("a")); |
| EXPECT_EQ(pieces2[1], to_ccs("b")); |
| pieces2.clear(); |
| folly::split("_", pieces[1], pieces2); |
| EXPECT_EQ(pieces2.size(), 2); |
| EXPECT_EQ(pieces2[0], to_ccs("c")); |
| EXPECT_EQ(pieces2[1], to_ccs("d")); |
| pieces.clear(); |
| pieces2.clear(); |
| |
| // test splits that with multi-line delimiter |
| folly::split("ab", "dabcabkdbkab", pieces, true); |
| EXPECT_EQ(pieces.size(), 3); |
| EXPECT_EQ(pieces[0], to_ccs("d")); |
| EXPECT_EQ(pieces[1], to_ccs("c")); |
| EXPECT_EQ(pieces[2], to_ccs("kdbk")); |
| pieces.clear(); |
| |
| string orig = "ab2342asdfv~~!"; |
| folly::split("", orig.c_str(), pieces, true); |
| EXPECT_EQ(pieces.size(), 1); |
| EXPECT_EQ(pieces[0], orig); |
| pieces.clear(); |
| |
| folly::split("452x;o38asfsajsdlfdf.j", "asfds", pieces, true); |
| EXPECT_EQ(pieces.size(), 1); |
| EXPECT_EQ(pieces[0], to_ccs("asfds")); |
| pieces.clear(); |
| |
| folly::split("a", "", pieces, true); |
| EXPECT_EQ(pieces.size(), 0); |
| pieces.clear(); |
| |
| folly::split("a", "", pieces); |
| EXPECT_EQ(pieces.size(), 1); |
| EXPECT_EQ(pieces[0], to_ccs("")); |
| pieces.clear(); |
| |
| folly::split("a", "abcdefg", pieces, true); |
| EXPECT_EQ(pieces.size(), 1); |
| EXPECT_EQ(pieces[0], to_ccs("bcdefg")); |
| pieces.clear(); |
| |
| orig = "All, , your base, are , , belong to us"; |
| folly::split(", ", orig, pieces, true); |
| EXPECT_EQ(pieces.size(), 4); |
| EXPECT_EQ(pieces[0], to_ccs("All")); |
| EXPECT_EQ(pieces[1], to_ccs("your base")); |
| EXPECT_EQ(pieces[2], to_ccs("are ")); |
| EXPECT_EQ(pieces[3], to_ccs("belong to us")); |
| pieces.clear(); |
| folly::split(", ", orig, pieces); |
| EXPECT_EQ(pieces.size(), 6); |
| EXPECT_EQ(pieces[0], to_ccs("All")); |
| EXPECT_EQ(pieces[1], to_ccs("")); |
| EXPECT_EQ(pieces[2], to_ccs("your base")); |
| EXPECT_EQ(pieces[3], to_ccs("are ")); |
| EXPECT_EQ(pieces[4], to_ccs("")); |
| EXPECT_EQ(pieces[5], to_ccs("belong to us")); |
| pieces.clear(); |
| |
| orig = ", Facebook, rul,es!, "; |
| folly::split(", ", orig, pieces, true); |
| EXPECT_EQ(pieces.size(), 2); |
| EXPECT_EQ(pieces[0], to_ccs("Facebook")); |
| EXPECT_EQ(pieces[1], to_ccs("rul,es!")); |
| pieces.clear(); |
| folly::split(", ", orig, pieces); |
| EXPECT_EQ(pieces.size(), 4); |
| EXPECT_EQ(pieces[0], to_ccs("")); |
| EXPECT_EQ(pieces[1], to_ccs("Facebook")); |
| EXPECT_EQ(pieces[2], to_ccs("rul,es!")); |
| EXPECT_EQ(pieces[3], to_ccs("")); |
| pieces.clear(); |
| |
| const char* str = "a,b"; |
| folly::split(',', StringPiece(str), pieces); |
| EXPECT_EQ(pieces.size(), 2); |
| EXPECT_EQ(pieces[0], to_ccs("a")); |
| EXPECT_EQ(pieces[1], to_ccs("b")); |
| EXPECT_EQ(pieces[0].start(), str); |
| EXPECT_EQ(pieces[1].start(), str + 2); |
| |
| std::set<StringPiece> unique; |
| folly::splitTo<StringPiece>(":", "asd:bsd:asd:asd:bsd:csd::asd", |
| std::inserter(unique, unique.begin()), true); |
| EXPECT_EQ(unique.size(), 3); |
| if (unique.size() == 3) { |
| EXPECT_EQ(*unique.begin(), to_ccs("asd")); |
| EXPECT_EQ(*--unique.end(), to_ccs("csd")); |
| } |
| |
| VectorType<fbstring,std::allocator<fbstring> > blah; |
| folly::split('-', "a-b-c-d-f-e", blah); |
| EXPECT_EQ(blah.size(), 6); |
| } |
| |
| } |
| |
| TEST(Split, split_vector) { |
| splitTest<std::vector>(); |
| } |
| TEST(Split, split_fbvector) { |
| splitTest<folly::fbvector>(); |
| } |
| TEST(Split, pieces_vector) { |
| piecesTest<std::vector>(); |
| } |
| TEST(Split, pieces_fbvector) { |
| piecesTest<folly::fbvector>(); |
| } |
| |
| TEST(Split, fixed) { |
| StringPiece a, b, c, d; |
| |
| EXPECT_TRUE(folly::split<false>('.', "a.b.c.d", a, b, c, d)); |
| EXPECT_TRUE(folly::split<false>('.', "a.b.c", a, b, c)); |
| EXPECT_TRUE(folly::split<false>('.', "a.b", a, b)); |
| EXPECT_TRUE(folly::split<false>('.', "a", a)); |
| |
| EXPECT_TRUE(folly::split('.', "a.b.c.d", a, b, c, d)); |
| EXPECT_TRUE(folly::split('.', "a.b.c", a, b, c)); |
| EXPECT_TRUE(folly::split('.', "a.b", a, b)); |
| EXPECT_TRUE(folly::split('.', "a", a)); |
| |
| EXPECT_TRUE(folly::split<false>('.', "a.b.c", a, b, c)); |
| EXPECT_EQ(to_ccs("a"), a); |
| EXPECT_EQ(to_ccs("b"), b); |
| EXPECT_EQ(to_ccs("c"), c); |
| EXPECT_FALSE(folly::split<false>('.', "a.b", a, b, c)); |
| EXPECT_TRUE(folly::split<false>('.', "a.b.c", a, b)); |
| EXPECT_EQ(to_ccs("a"), a); |
| EXPECT_EQ(to_ccs("b.c"), b); |
| |
| EXPECT_TRUE(folly::split('.', "a.b.c", a, b, c)); |
| EXPECT_EQ(to_ccs("a"), a); |
| EXPECT_EQ(to_ccs("b"), b); |
| EXPECT_EQ(to_ccs("c"), c); |
| EXPECT_FALSE(folly::split('.', "a.b.c", a, b)); |
| EXPECT_FALSE(folly::split('.', "a.b", a, b, c)); |
| |
| EXPECT_TRUE(folly::split<false>('.', "a.b", a, b)); |
| EXPECT_EQ(to_ccs("a"), a); |
| EXPECT_EQ(to_ccs("b"), b); |
| EXPECT_FALSE(folly::split<false>('.', "a", a, b)); |
| EXPECT_TRUE(folly::split<false>('.', "a.b", a)); |
| EXPECT_EQ(to_ccs("a.b"), a); |
| |
| EXPECT_TRUE(folly::split('.', "a.b", a, b)); |
| EXPECT_EQ(to_ccs("a"), a); |
| EXPECT_EQ(to_ccs("b"), b); |
| EXPECT_FALSE(folly::split('.', "a", a, b)); |
| EXPECT_FALSE(folly::split('.', "a.b", a)); |
| } |
| |
| TEST(Split, std_string_fixed) { |
| std::string a, b, c, d; |
| |
| EXPECT_TRUE(folly::split<false>('.', "a.b.c.d", a, b, c, d)); |
| EXPECT_TRUE(folly::split<false>('.', "a.b.c", a, b, c)); |
| EXPECT_TRUE(folly::split<false>('.', "a.b", a, b)); |
| EXPECT_TRUE(folly::split<false>('.', "a", a)); |
| |
| EXPECT_TRUE(folly::split('.', "a.b.c.d", a, b, c, d)); |
| EXPECT_TRUE(folly::split('.', "a.b.c", a, b, c)); |
| EXPECT_TRUE(folly::split('.', "a.b", a, b)); |
| EXPECT_TRUE(folly::split('.', "a", a)); |
| |
| EXPECT_TRUE(folly::split<false>('.', "a.b.c", a, b, c)); |
| EXPECT_EQ(to_ccs("a"), a); |
| EXPECT_EQ(to_ccs("b"), b); |
| EXPECT_EQ(to_ccs("c"), c); |
| EXPECT_FALSE(folly::split<false>('.', "a.b", a, b, c)); |
| EXPECT_TRUE(folly::split<false>('.', "a.b.c", a, b)); |
| EXPECT_EQ(to_ccs("a"), a); |
| EXPECT_EQ(to_ccs("b.c"), b); |
| |
| EXPECT_TRUE(folly::split('.', "a.b.c", a, b, c)); |
| EXPECT_EQ(to_ccs("a"), a); |
| EXPECT_EQ(to_ccs("b"), b); |
| EXPECT_EQ(to_ccs("c"), c); |
| EXPECT_FALSE(folly::split('.', "a.b.c", a, b)); |
| EXPECT_FALSE(folly::split('.', "a.b", a, b, c)); |
| |
| EXPECT_TRUE(folly::split<false>('.', "a.b", a, b)); |
| EXPECT_EQ(to_ccs("a"), a); |
| EXPECT_EQ(to_ccs("b"), b); |
| EXPECT_FALSE(folly::split<false>('.', "a", a, b)); |
| EXPECT_TRUE(folly::split<false>('.', "a.b", a)); |
| EXPECT_EQ(to_ccs("a.b"), a); |
| |
| EXPECT_TRUE(folly::split('.', "a.b", a, b)); |
| EXPECT_EQ(to_ccs("a"), a); |
| EXPECT_EQ(to_ccs("b"), b); |
| EXPECT_FALSE(folly::split('.', "a", a, b)); |
| EXPECT_FALSE(folly::split('.', "a.b", a)); |
| } |
| |
| TEST(Split, fixed_convert) { |
| StringPiece a, d; |
| int b; |
| double c; |
| |
| EXPECT_TRUE(folly::split(':', "a:13:14.7:b", a, b, c, d)); |
| EXPECT_EQ(to_ccs("a"), a); |
| EXPECT_EQ(13, b); |
| EXPECT_NEAR(14.7, c, 1e-10); |
| EXPECT_EQ(to_ccs("b"), d); |
| |
| EXPECT_TRUE(folly::split<false>(':', "b:14:15.3:c", a, b, c, d)); |
| EXPECT_EQ(to_ccs("b"), a); |
| EXPECT_EQ(14, b); |
| EXPECT_NEAR(15.3, c, 1e-10); |
| EXPECT_EQ(to_ccs("c"), d); |
| |
| EXPECT_FALSE(folly::split(':', "a:13:14.7:b", a, b, d)); |
| |
| EXPECT_TRUE(folly::split<false>(':', "a:13:14.7:b", a, b, d)); |
| EXPECT_EQ(to_ccs("a"), a); |
| EXPECT_EQ(13, b); |
| EXPECT_EQ(to_ccs("14.7:b"), d); |
| |
| EXPECT_THROW(folly::split<false>(':', "a:13:14.7:b", a, b, c), |
| std::range_error); |
| } |
| |
| TEST(String, join) { |
| string output; |
| |
| std::vector<int> empty = { }; |
| join(":", empty, output); |
| EXPECT_TRUE(output.empty()); |
| |
| std::vector<std::string> input1 = { "1", "23", "456", "" }; |
| join(':', input1, output); |
| EXPECT_EQ(output, "1:23:456:"); |
| output = join(':', input1); |
| EXPECT_EQ(output, "1:23:456:"); |
| |
| auto input2 = { 1, 23, 456 }; |
| join("-*-", input2, output); |
| EXPECT_EQ(output, "1-*-23-*-456"); |
| output = join("-*-", input2); |
| EXPECT_EQ(output, "1-*-23-*-456"); |
| |
| auto input3 = { 'f', 'a', 'c', 'e', 'b', 'o', 'o', 'k' }; |
| join("", input3, output); |
| EXPECT_EQ(output, "facebook"); |
| |
| join("_", { "", "f", "a", "c", "e", "b", "o", "o", "k", "" }, output); |
| EXPECT_EQ(output, "_f_a_c_e_b_o_o_k_"); |
| |
| output = join("", input3.begin(), input3.end()); |
| EXPECT_EQ(output, "facebook"); |
| } |
| |
| TEST(String, hexlify) { |
| string input1 = "0123"; |
| string output1; |
| EXPECT_TRUE(hexlify(input1, output1)); |
| EXPECT_EQ(output1, "30313233"); |
| |
| fbstring input2 = "abcdefg"; |
| input2[1] = 0; |
| input2[3] = 0xff; |
| input2[5] = 0xb6; |
| fbstring output2; |
| EXPECT_TRUE(hexlify(input2, output2)); |
| EXPECT_EQ(output2, "610063ff65b667"); |
| } |
| |
| TEST(String, unhexlify) { |
| string input1 = "30313233"; |
| string output1; |
| EXPECT_TRUE(unhexlify(input1, output1)); |
| EXPECT_EQ(output1, "0123"); |
| |
| fbstring input2 = "610063ff65b667"; |
| fbstring output2; |
| EXPECT_TRUE(unhexlify(input2, output2)); |
| EXPECT_EQ(output2.size(), 7); |
| EXPECT_EQ(output2[0], 'a'); |
| EXPECT_EQ(output2[1], 0); |
| EXPECT_EQ(output2[2], 'c'); |
| EXPECT_EQ(output2[3] & 0xff, 0xff); |
| EXPECT_EQ(output2[4], 'e'); |
| EXPECT_EQ(output2[5] & 0xff, 0xb6); |
| EXPECT_EQ(output2[6], 'g'); |
| |
| string input3 = "x"; |
| string output3; |
| EXPECT_FALSE(unhexlify(input3, output3)); |
| |
| string input4 = "xy"; |
| string output4; |
| EXPECT_FALSE(unhexlify(input4, output4)); |
| } |
| |
| TEST(String, backslashify) { |
| EXPECT_EQ(to_ccs("abc"), string("abc")); |
| EXPECT_EQ(to_ccs("abc"), backslashify(string("abc"))); |
| EXPECT_EQ(to_ccs("abc\\r"), backslashify(string("abc\r"))); |
| EXPECT_EQ(to_ccs("abc\\x0d"), backslashify(string("abc\r"), true)); |
| EXPECT_EQ(to_ccs("\\0\\0"), backslashify(string(2, '\0'))); |
| } |
| |
| TEST(String, humanify) { |
| // Simple cases; output is obvious. |
| EXPECT_EQ("abc", humanify(string("abc"))); |
| EXPECT_EQ("abc\\\\r", humanify(string("abc\\r"))); |
| EXPECT_EQ("0xff", humanify(string("\xff"))); |
| EXPECT_EQ("abc\\xff", humanify(string("abc\xff"))); |
| EXPECT_EQ("abc\\b", humanify(string("abc\b"))); |
| EXPECT_EQ("0x00", humanify(string(1, '\0'))); |
| EXPECT_EQ("0x0000", humanify(string(2, '\0'))); |
| |
| |
| // Mostly printable, so backslash! 80, 60, and 40% printable, respectively |
| EXPECT_EQ("aaaa\\xff", humanify(string("aaaa\xff"))); |
| EXPECT_EQ("aaa\\xff\\xff", humanify(string("aaa\xff\xff"))); |
| EXPECT_EQ("aa\\xff\\xff\\xff", humanify(string("aa\xff\xff\xff"))); |
| |
| // 20% printable, and the printable portion isn't the prefix; hexify! |
| EXPECT_EQ("0xff61ffffff", humanify(string("\xff" "a\xff\xff\xff"))); |
| |
| // Same as previous, except swap first two chars; prefix is |
| // printable and within the threshold, so backslashify. |
| EXPECT_EQ("a\\xff\\xff\\xff\\xff", humanify(string("a\xff\xff\xff\xff"))); |
| |
| // Just too much unprintable; hex, despite prefix. |
| EXPECT_EQ("0x61ffffffffff", humanify(string("a\xff\xff\xff\xff\xff"))); |
| } |
| |
| namespace { |
| |
| /** |
| * Copy bytes from src to somewhere in the buffer referenced by dst. The |
| * actual starting position of the copy will be the first address in the |
| * destination buffer whose address mod 8 is equal to the src address mod 8. |
| * The caller is responsible for ensuring that the destination buffer has |
| * enough extra space to accommodate the shifted copy. |
| */ |
| char* copyWithSameAlignment(char* dst, const char* src, size_t length) { |
| const char* originalDst = dst; |
| size_t dstOffset = size_t(dst) & 0x7; |
| size_t srcOffset = size_t(src) & 0x7; |
| while (dstOffset != srcOffset) { |
| dst++; |
| dstOffset++; |
| dstOffset &= 0x7; |
| } |
| CHECK(dst <= originalDst + 7); |
| CHECK((size_t(dst) & 0x7) == (size_t(src) & 0x7)); |
| memcpy(dst, src, length); |
| return dst; |
| } |
| |
| void testToLowerAscii(Range<const char*> src) { |
| // Allocate extra space so we can make copies that start at the |
| // same alignment (byte, word, quadword, etc) as the source buffer. |
| string controlBuf(src.size() + 7, '\0'); |
| char* control = copyWithSameAlignment(const_cast<char *>(controlBuf.data()), src.begin(), src.size()); |
| |
| string testBuf(src.size() + 7, '\0'); |
| char* test = copyWithSameAlignment(const_cast<char *>(testBuf.data()), src.begin(), src.size()); |
| |
| for (size_t i = 0; i < src.size(); i++) { |
| control[i] = tolower(control[i]); |
| } |
| toLowerAscii(test, src.size()); |
| for (size_t i = 0; i < src.size(); i++) { |
| EXPECT_EQ(control[i], test[i]); |
| } |
| } |
| |
| } // anon namespace |
| |
| TEST(String, toLowerAsciiAligned) { |
| static const size_t kSize = 256; |
| char input[kSize]; |
| for (size_t i = 0; i < kSize; i++) { |
| input[i] = (char)(i & 0xff); |
| } |
| testToLowerAscii(Range<const char*>(input, kSize)); |
| } |
| |
| TEST(String, toLowerAsciiUnaligned) { |
| static const size_t kSize = 256; |
| char input[kSize]; |
| for (size_t i = 0; i < kSize; i++) { |
| input[i] = (char)(i & 0xff); |
| } |
| // Test input buffers of several lengths to exercise all the |
| // cases: buffer at the start/middle/end of an aligned block, plus |
| // buffers that span multiple aligned blocks. The longest test input |
| // is 3 unaligned bytes + 4 32-bit aligned bytes + 8 64-bit aligned |
| // + 4 32-bit aligned + 3 unaligned = 22 bytes. |
| for (size_t length = 1; length < 23; length++) { |
| for (size_t offset = 0; offset + length <= kSize; offset++) { |
| testToLowerAscii(Range<const char*>(input + offset, length)); |
| } |
| } |
| } |
| |
| ////////////////////////////////////////////////////////////////////// |
| |
| BENCHMARK(splitOnSingleChar, iters) { |
| static const std::string line = "one:two:three:four"; |
| for (size_t i = 0; i < iters << 4; ++i) { |
| std::vector<StringPiece> pieces; |
| folly::split(':', line, pieces); |
| } |
| } |
| |
| BENCHMARK(splitOnSingleCharFixed, iters) { |
| static const std::string line = "one:two:three:four"; |
| for (size_t i = 0; i < iters << 4; ++i) { |
| StringPiece a, b, c, d; |
| folly::split(':', line, a, b, c, d); |
| } |
| } |
| |
| BENCHMARK(splitOnSingleCharFixedAllowExtra, iters) { |
| static const std::string line = "one:two:three:four"; |
| for (size_t i = 0; i < iters << 4; ++i) { |
| StringPiece a, b, c, d; |
| folly::split<false>(':', line, a, b, c, d); |
| } |
| } |
| |
| BENCHMARK(splitStr, iters) { |
| static const std::string line = "one-*-two-*-three-*-four"; |
| for (size_t i = 0; i < iters << 4; ++i) { |
| std::vector<StringPiece> pieces; |
| folly::split("-*-", line, pieces); |
| } |
| } |
| |
| BENCHMARK(splitStrFixed, iters) { |
| static const std::string line = "one-*-two-*-three-*-four"; |
| for (size_t i = 0; i < iters << 4; ++i) { |
| StringPiece a, b, c, d; |
| folly::split("-*-", line, a, b, c, d); |
| } |
| } |
| |
| BENCHMARK(boost_splitOnSingleChar, iters) { |
| static const std::string line = "one:two:three:four"; |
| bool(*pred)(char) = [] (char c) -> bool { return c == ':'; }; |
| for (size_t i = 0; i < iters << 4; ++i) { |
| std::vector<boost::iterator_range<std::string::const_iterator> > pieces; |
| boost::split(pieces, line, pred); |
| } |
| } |
| |
| BENCHMARK(joinCharStr, iters) { |
| static const std::vector<std::string> input = { |
| "one", "two", "three", "four", "five", "six", "seven" }; |
| for (size_t i = 0; i < iters << 4; ++i) { |
| std::string output; |
| folly::join(':', input, output); |
| } |
| } |
| |
| BENCHMARK(joinStrStr, iters) { |
| static const std::vector<std::string> input = { |
| "one", "two", "three", "four", "five", "six", "seven" }; |
| for (size_t i = 0; i < iters << 4; ++i) { |
| std::string output; |
| folly::join(":", input, output); |
| } |
| } |
| |
| BENCHMARK(joinInt, iters) { |
| static const auto input = { |
| 123, 456, 78910, 1112, 1314, 151, 61718 }; |
| for (size_t i = 0; i < iters << 4; ++i) { |
| std::string output; |
| folly::join(":", input, output); |
| } |
| } |
| |
| TEST(String, whitespace) { |
| // trimWhitespace: |
| EXPECT_EQ(to_ccs("kavabanga"), |
| trimWhitespace("kavabanga")); |
| EXPECT_EQ(to_ccs("kavabanga"), |
| trimWhitespace("kavabanga \t \n ")); |
| EXPECT_EQ(to_ccs("kavabanga"), |
| trimWhitespace(" \t \r \n \n kavabanga")); |
| EXPECT_EQ(to_ccs("kavabanga"), |
| trimWhitespace("\t \r \n kavabanga \t \n ")); |
| EXPECT_EQ(to_ccs("kavabanga"), |
| trimWhitespace(" \t \r \n \n kavabanga")); |
| EXPECT_EQ(to_ccs("kavabanga"), |
| trimWhitespace("\t \r \n kavabanga \t \n ")); |
| EXPECT_EQ( |
| ltrimWhitespace(rtrimWhitespace("kavabanga")), |
| rtrimWhitespace(ltrimWhitespace("kavabanga"))); |
| EXPECT_EQ( |
| ltrimWhitespace(rtrimWhitespace("kavabanga \r\t\n")), |
| rtrimWhitespace(ltrimWhitespace("kavabanga \r\t\n"))); |
| EXPECT_EQ(to_ccs(""), trimWhitespace("\t \r \n \t \n ")); |
| EXPECT_EQ(to_ccs(""), trimWhitespace("")); |
| EXPECT_EQ(to_ccs(""), trimWhitespace("\t")); |
| EXPECT_EQ(to_ccs(""), trimWhitespace("\r")); |
| EXPECT_EQ(to_ccs(""), trimWhitespace("\n")); |
| EXPECT_EQ(to_ccs(""), trimWhitespace("\t ")); |
| EXPECT_EQ(to_ccs(""), trimWhitespace("\r ")); |
| EXPECT_EQ(to_ccs(""), trimWhitespace("\n ")); |
| EXPECT_EQ(to_ccs(""), trimWhitespace(" \t")); |
| EXPECT_EQ(to_ccs(""), trimWhitespace(" \r")); |
| EXPECT_EQ(to_ccs(""), trimWhitespace(" \n")); |
| |
| // ltrimWhitespace: |
| EXPECT_EQ(to_ccs("kavabanga"), ltrimWhitespace("\t kavabanga")); |
| EXPECT_EQ(to_ccs("kavabanga \r\n"), ltrimWhitespace("\t kavabanga \r\n")); |
| EXPECT_EQ(to_ccs(""), ltrimWhitespace("\r ")); |
| EXPECT_EQ(to_ccs(""), ltrimWhitespace("\n ")); |
| EXPECT_EQ(to_ccs(""), ltrimWhitespace("\r ")); |
| |
| // rtrimWhitespace: |
| EXPECT_EQ(to_ccs("\t kavabanga"), rtrimWhitespace("\t kavabanga")); |
| EXPECT_EQ(to_ccs("\t kavabanga"), rtrimWhitespace("\t kavabanga \r\n")); |
| EXPECT_EQ(to_ccs(""), rtrimWhitespace("\r ")); |
| EXPECT_EQ(to_ccs(""), rtrimWhitespace("\n ")); |
| EXPECT_EQ(to_ccs(""), rtrimWhitespace("\r ")); |
| } |
| |
| int main(int argc, char *argv[]) { |
| testing::InitGoogleTest(&argc, argv); |
| gflags::ParseCommandLineFlags(&argc, &argv, true); |
| auto ret = RUN_ALL_TESTS(); |
| if (!ret) { |
| initBenchmark(); |
| if (FLAGS_benchmark) { |
| folly::runBenchmarks(); |
| } |
| } |
| return ret; |
| } |