Compensate for -m32 platforms.
[folly.git] / folly / test / StringTest.cpp
index b283161a43cd1ec32fc886ddb4c30e1a5ce61d7e..72e87521582cd3777f85599d6ad755a45315233f 100644 (file)
@@ -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.
 
 #include <folly/String.h>
 
-#include <random>
-#include <boost/algorithm/string.hpp>
+#include <boost/regex.hpp>
 #include <gtest/gtest.h>
 
-#include <folly/Benchmark.h>
-
 using namespace folly;
 using namespace std;
 
@@ -36,9 +33,12 @@ TEST(StringPrintf, BasicTest) {
 
 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("2000000000", stringPrintf("%ld", 2000000000ULL));
+  EXPECT_EQ("2000000000", stringPrintf("%ld", 2000000000L));
+  EXPECT_EQ("-2000000000", stringPrintf("%ld", -2000000000L));
+  EXPECT_EQ("5000000000", stringPrintf("%lld", 5000000000ULL));
+  EXPECT_EQ("5000000000", stringPrintf("%lld", 5000000000LL));
+  EXPECT_EQ("-5000000000", stringPrintf("%lld", -5000000000LL));
   EXPECT_EQ("-1", stringPrintf("%d", 0xffffffff));
   EXPECT_EQ("-1", stringPrintf("%ld", 0xffffffffffffffff));
   EXPECT_EQ("-1", stringPrintf("%ld", 0xffffffffffffffffUL));
@@ -60,11 +60,70 @@ TEST(StringPrintf, Appending) {
   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
-  for (int i = 0; i < 100; ++i) {
+  // 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');
-    EXPECT_EQ("X" + expected + "X", stringPrintf("X%sX", expected.c_str()));
+    expected = "X" + expected + "X";
+    string result = stringPrintf("%s", expected.c_str());
+    EXPECT_EQ(expected.size(), result.size());
+    EXPECT_EQ(expected, result);
   }
 
   EXPECT_EQ("abc12345678910111213141516171819202122232425xyz",
@@ -95,16 +154,6 @@ TEST(StringPrintf, oldStringAppendf) {
   EXPECT_EQ(string("helloa/b/c/d"), s);
 }
 
-BENCHMARK(new_stringPrintfSmall, iters) {
-  for (int64_t i = 0; i < iters; ++i) {
-    int32_t x = int32_t(i);
-    int32_t y = int32_t(i + 1);
-    string s =
-      stringPrintf("msg msg msg msg msg msg msg msg:  %d, %d, %s",
-                   x, y, "hello");
-  }
-}
-
 TEST(Escape, cEscape) {
   EXPECT_EQ("hello world", cEscape<std::string>("hello world"));
   EXPECT_EQ("hello \\\\world\\\" goodbye",
@@ -137,6 +186,11 @@ TEST(Escape, uriEscape) {
                                                         UriEscapeMode::PATH));
   EXPECT_EQ("hello%2c+%2fworld", uriEscape<std::string>("hello, /world",
                                                         UriEscapeMode::QUERY));
+  EXPECT_EQ(
+    "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.~",
+    uriEscape<std::string>(
+      "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.~")
+  );
 }
 
 TEST(Escape, uriUnescape) {
@@ -215,96 +269,6 @@ TEST(Escape, uriUnescapePercentDecoding) {
   }
 }
 
-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) {
@@ -444,7 +408,7 @@ TEST(PrettyToDouble, Basic) {
     PrettyType formatType = testCase.prettyType;
     double x = testCase.realValue;
     std::string testString = testCase.prettyString;
-    double recoveredX;
+    double recoveredX = 0;
     try{
       recoveredX = prettyToDouble(testString, formatType);
     } catch (std::range_error &ex){
@@ -461,7 +425,7 @@ TEST(PrettyToDouble, Basic) {
     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;
+        double recoveredX = 0;
         try{
           recoveredX = prettyToDouble(prettyPrint(x, formatType, addSpace),
                                              formatType);
@@ -519,33 +483,6 @@ TEST(System, errnoStr) {
   EXPECT_EQ(EACCES, errno);
 }
 
-namespace folly_test {
-struct ThisIsAVeryLongStructureName {
-};
-}  // namespace folly_test
-
-#if FOLLY_HAVE_CPLUS_DEMANGLE_V3_CALLBACK
-TEST(System, demangle) {
-  char expected[] = "folly_test::ThisIsAVeryLongStructureName";
-  EXPECT_STREQ(
-      expected,
-      demangle(typeid(folly_test::ThisIsAVeryLongStructureName)).c_str());
-
-  {
-    char buf[sizeof(expected)];
-    EXPECT_EQ(sizeof(expected) - 1,
-              demangle(typeid(folly_test::ThisIsAVeryLongStructureName),
-                       buf, sizeof(buf)));
-    EXPECT_STREQ(expected, buf);
-
-    EXPECT_EQ(sizeof(expected) - 1,
-              demangle(typeid(folly_test::ThisIsAVeryLongStructureName),
-                       buf, 11));
-    EXPECT_STREQ("folly_test", buf);
-  }
-}
-#endif
-
 namespace {
 
 template<template<class,class> class VectorType>
@@ -948,7 +885,7 @@ TEST(Split, std_string_fixed) {
 TEST(Split, fixed_convert) {
   StringPiece a, d;
   int b;
-  double c;
+  double c = 0;
 
   EXPECT_TRUE(folly::split(':', "a:13:14.7:b", a, b, c, d));
   EXPECT_EQ("a", a);
@@ -998,6 +935,9 @@ TEST(String, join) {
 
   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) {
@@ -1079,11 +1019,37 @@ TEST(String, humanify) {
 
 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) {
-  char control[src.size()];
-  memcpy(control, src.begin(), src.size());
-  char test[src.size()];
-  memcpy(test, src.begin(), src.size());
+  // Allocate extra space so we can make copies that start at the
+  // same alignment (byte, word, quadword, etc) as the source buffer.
+  char controlBuf[src.size() + 7];
+  char* control = copyWithSameAlignment(controlBuf, src.begin(), src.size());
+
+  char testBuf[src.size() + 7];
+  char* test = copyWithSameAlignment(testBuf, src.begin(), src.size());
+
   for (size_t i = 0; i < src.size(); i++) {
     control[i] = tolower(control[i]);
   }
@@ -1112,101 +1078,188 @@ TEST(String, toLowerAsciiUnaligned) {
   }
   // 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.
-  for (size_t length = 1; length < 11; length++) {
+  // 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));
     }
   }
 }
 
-//////////////////////////////////////////////////////////////////////
+TEST(String, whitespace) {
+  // trimWhitespace:
+  EXPECT_EQ("kavabanga",
+        trimWhitespace("kavabanga"));
+  EXPECT_EQ("kavabanga",
+        trimWhitespace("kavabanga \t \n  "));
+  EXPECT_EQ("kavabanga",
+        trimWhitespace("   \t \r \n \n kavabanga"));
+  EXPECT_EQ("kavabanga",
+        trimWhitespace("\t \r \n   kavabanga \t \n  "));
+  EXPECT_EQ("kavabanga",
+        trimWhitespace("   \t \r \n \n kavabanga"));
+  EXPECT_EQ("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("", trimWhitespace("\t \r \n   \t \n  "));
+  EXPECT_EQ("", trimWhitespace(""));
+  EXPECT_EQ("", trimWhitespace("\t"));
+  EXPECT_EQ("", trimWhitespace("\r"));
+  EXPECT_EQ("", trimWhitespace("\n"));
+  EXPECT_EQ("", trimWhitespace("\t "));
+  EXPECT_EQ("", trimWhitespace("\r  "));
+  EXPECT_EQ("", trimWhitespace("\n   "));
+  EXPECT_EQ("", trimWhitespace("    \t"));
+  EXPECT_EQ("", trimWhitespace("    \r"));
+  EXPECT_EQ("", trimWhitespace("    \n"));
+
+  // ltrimWhitespace:
+  EXPECT_EQ("kavabanga", ltrimWhitespace("\t kavabanga"));
+  EXPECT_EQ("kavabanga \r\n", ltrimWhitespace("\t kavabanga \r\n"));
+  EXPECT_EQ("", ltrimWhitespace("\r "));
+  EXPECT_EQ("", ltrimWhitespace("\n   "));
+  EXPECT_EQ("", ltrimWhitespace("\r   "));
+
+  // rtrimWhitespace:
+  EXPECT_EQ("\t kavabanga", rtrimWhitespace("\t kavabanga"));
+  EXPECT_EQ("\t kavabanga", rtrimWhitespace("\t kavabanga \r\n"));
+  EXPECT_EQ("", rtrimWhitespace("\r "));
+  EXPECT_EQ("", rtrimWhitespace("\n   "));
+  EXPECT_EQ("", rtrimWhitespace("\r   "));
+}
 
-BENCHMARK(splitOnSingleChar, iters) {
-  static const std::string line = "one:two:three:four";
-  for (int i = 0; i < iters << 4; ++i) {
-    std::vector<StringPiece> pieces;
-    folly::split(':', line, pieces);
-  }
+TEST(String, stripLeftMargin_empty) {
+  auto input = R"TEXT(
+  )TEXT";
+  auto expected = "";
+  EXPECT_EQ(expected, stripLeftMargin(input));
 }
 
-BENCHMARK(splitOnSingleCharFixed, iters) {
-  static const std::string line = "one:two:three:four";
-  for (int i = 0; i < iters << 4; ++i) {
-    StringPiece a, b, c, d;
-    folly::split(':', line, a, b, c, d);
-  }
+TEST(String, stripLeftMargin_one_line) {
+  auto input = R"TEXT(
+    hi there bob!
+  )TEXT";
+  auto expected = "hi there bob!\n";
+  EXPECT_EQ(expected, stripLeftMargin(input));
 }
 
-BENCHMARK(splitOnSingleCharFixedAllowExtra, iters) {
-  static const std::string line = "one:two:three:four";
-  for (int i = 0; i < iters << 4; ++i) {
-    StringPiece a, b, c, d;
-    folly::split<false>(':', line, a, b, c, d);
-  }
+TEST(String, stripLeftMargin_two_lines) {
+  auto input = R"TEXT(
+    hi there bob!
+    nice weather today!
+  )TEXT";
+  auto expected = "hi there bob!\nnice weather today!\n";
+  EXPECT_EQ(expected, stripLeftMargin(input));
 }
 
-BENCHMARK(splitStr, iters) {
-  static const std::string line = "one-*-two-*-three-*-four";
-  for (int i = 0; i < iters << 4; ++i) {
-    std::vector<StringPiece> pieces;
-    folly::split("-*-", line, pieces);
-  }
+TEST(String, stripLeftMargin_three_lines_uneven) {
+  auto input = R"TEXT(
+      hi there bob!
+    nice weather today!
+      so long!
+  )TEXT";
+  auto expected = "  hi there bob!\nnice weather today!\n  so long!\n";
+  EXPECT_EQ(expected, stripLeftMargin(input));
 }
 
-BENCHMARK(splitStrFixed, iters) {
-  static const std::string line = "one-*-two-*-three-*-four";
-  for (int i = 0; i < iters << 4; ++i) {
-    StringPiece a, b, c, d;
-    folly::split("-*-", line, a, b, c, d);
-  }
+TEST(String, stripLeftMargin_preceding_blank_lines) {
+  auto input = R"TEXT(
+
+
+    hi there bob!
+  )TEXT";
+  auto expected = "\n\nhi there bob!\n";
+  EXPECT_EQ(expected, stripLeftMargin(input));
 }
 
-BENCHMARK(boost_splitOnSingleChar, iters) {
-  static const std::string line = "one:two:three:four";
-  bool(*pred)(char) = [] (char c) -> bool { return c == ':'; };
-  for (int i = 0; i < iters << 4; ++i) {
-    std::vector<boost::iterator_range<std::string::const_iterator> > pieces;
-    boost::split(pieces, line, pred);
-  }
+TEST(String, stripLeftMargin_succeeding_blank_lines) {
+  auto input = R"TEXT(
+    hi there bob!
+
+
+  )TEXT";
+  auto expected = "hi there bob!\n\n\n";
+  EXPECT_EQ(expected, stripLeftMargin(input));
 }
 
-BENCHMARK(joinCharStr, iters) {
-  static const std::vector<std::string> input = {
-    "one", "two", "three", "four", "five", "six", "seven" };
-  for (int i = 0; i < iters << 4; ++i) {
-    std::string output;
-    folly::join(':', input, output);
-  }
+TEST(String, stripLeftMargin_interstitial_undented_whiteline) {
+  //  using ~ as a marker
+  string input = R"TEXT(
+      hi there bob!
+    ~
+      so long!
+  )TEXT";
+  input = boost::regex_replace(input, boost::regex(" +~"), "");
+  EXPECT_EQ("\n      hi there bob!\n\n      so long!\n  ", input);
+  auto expected = "hi there bob!\n\nso long!\n";
+  EXPECT_EQ(expected, stripLeftMargin(input));
 }
 
-BENCHMARK(joinStrStr, iters) {
-  static const std::vector<std::string> input = {
-    "one", "two", "three", "four", "five", "six", "seven" };
-  for (int i = 0; i < iters << 4; ++i) {
-    std::string output;
-    folly::join(":", input, output);
-  }
+TEST(String, stripLeftMargin_interstitial_dedented_whiteline) {
+  //  using ~ as a marker
+  string input = R"TEXT(
+      hi there bob!
+    ~
+      so long!
+  )TEXT";
+  input = boost::regex_replace(input, boost::regex("~"), "");
+  EXPECT_EQ("\n      hi there bob!\n    \n      so long!\n  ", input);
+  auto expected = "hi there bob!\n\nso long!\n";
+  EXPECT_EQ(expected, stripLeftMargin(input));
 }
 
-BENCHMARK(joinInt, iters) {
-  static const auto input = {
-    123, 456, 78910, 1112, 1314, 151, 61718 };
-  for (int i = 0; i < iters << 4; ++i) {
-    std::string output;
-    folly::join(":", input, output);
-  }
+TEST(String, stripLeftMargin_interstitial_equidented_whiteline) {
+  //  using ~ as a marker
+  string input = R"TEXT(
+      hi there bob!
+      ~
+      so long!
+  )TEXT";
+  input = boost::regex_replace(input, boost::regex("~"), "");
+  EXPECT_EQ("\n      hi there bob!\n      \n      so long!\n  ", input);
+  auto expected = "hi there bob!\n\nso long!\n";
+  EXPECT_EQ(expected, stripLeftMargin(input));
 }
 
-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;
+TEST(String, stripLeftMargin_interstitial_indented_whiteline) {
+  //  using ~ as a marker
+  string input = R"TEXT(
+      hi there bob!
+        ~
+      so long!
+  )TEXT";
+  input = boost::regex_replace(input, boost::regex("~"), "");
+  EXPECT_EQ("\n      hi there bob!\n        \n      so long!\n  ", input);
+  auto expected = "hi there bob!\n  \nso long!\n";
+  EXPECT_EQ(expected, stripLeftMargin(input));
+}
+
+const folly::StringPiece kTestUTF8 = "This is \U0001F602 stuff!";
+
+TEST(UTF8StringPiece, valid_utf8) {
+  folly::StringPiece sp = kTestUTF8;
+  UTF8StringPiece utf8 = sp;
+  // utf8.size() not available since it's not a random-access range
+  EXPECT_EQ(16, utf8.walk_size());
+}
+
+TEST(UTF8StringPiece, valid_suffix) {
+  UTF8StringPiece utf8 = kTestUTF8.subpiece(8);
+  EXPECT_EQ(8, utf8.walk_size());
+}
+
+TEST(UTF8StringPiece, empty_mid_codepoint) {
+  UTF8StringPiece utf8 = kTestUTF8.subpiece(9, 0); // okay since it's empty
+  EXPECT_EQ(0, utf8.walk_size());
+}
+
+TEST(UTF8StringPiece, invalid_mid_codepoint) {
+  EXPECT_THROW(UTF8StringPiece(kTestUTF8.subpiece(9, 1)), std::out_of_range);
 }