about summary refs log tree commit diff
path: root/absl/strings
diff options
context:
space:
mode:
Diffstat (limited to 'absl/strings')
-rw-r--r--absl/strings/BUILD.bazel1
-rw-r--r--absl/strings/CMakeLists.txt1
-rw-r--r--absl/strings/charconv_test.cc7
-rw-r--r--absl/strings/internal/charconv_parse.cc6
-rw-r--r--absl/strings/internal/str_format/convert_test.cc110
-rw-r--r--absl/strings/numbers_test.cc14
-rw-r--r--absl/strings/string_view.h62
-rw-r--r--absl/strings/string_view_test.cc23
8 files changed, 148 insertions, 76 deletions
diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel
index 729bdd761f0f..4863ead2992e 100644
--- a/absl/strings/BUILD.bazel
+++ b/absl/strings/BUILD.bazel
@@ -630,6 +630,7 @@ cc_test(
     visibility = ["//visibility:private"],
     deps = [
         ":str_format_internal",
+        "//absl/base:raw_logging_internal",
         "//absl/numeric:int128",
         "@com_google_googletest//:gtest_main",
     ],
diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt
index ccff44419341..3f9079575011 100644
--- a/absl/strings/CMakeLists.txt
+++ b/absl/strings/CMakeLists.txt
@@ -461,6 +461,7 @@ absl_cc_test(
     ${ABSL_TEST_COPTS}
   DEPS
     absl::str_format_internal
+    absl::raw_logging_internal
     absl::int128
     gmock_main
 )
diff --git a/absl/strings/charconv_test.cc b/absl/strings/charconv_test.cc
index b58fad269d8b..9090e9c89c50 100644
--- a/absl/strings/charconv_test.cc
+++ b/absl/strings/charconv_test.cc
@@ -511,6 +511,13 @@ TEST(FromChars, Overflow) {
   EXPECT_EQ(f, std::numeric_limits<float>::max());
 }
 
+TEST(FromChars, RegressionTestsFromFuzzer) {
+  absl::string_view src = "0x21900000p00000000099";
+  float f;
+  auto result = absl::from_chars(src.data(), src.data() + src.size(), f);
+  EXPECT_EQ(result.ec, std::errc::result_out_of_range);
+}
+
 TEST(FromChars, ReturnValuePtr) {
   // Check that `ptr` points one past the number scanned, even if that number
   // is not representable.
diff --git a/absl/strings/internal/charconv_parse.cc b/absl/strings/internal/charconv_parse.cc
index f3c7232403a1..fa9a8965fe6b 100644
--- a/absl/strings/internal/charconv_parse.cc
+++ b/absl/strings/internal/charconv_parse.cc
@@ -253,6 +253,12 @@ std::size_t ConsumeDigits(const char* begin, const char* end, int max_digits,
     assert(max_digits * 4 <= std::numeric_limits<T>::digits);
   }
   const char* const original_begin = begin;
+
+  // Skip leading zeros, but only if *out is zero.
+  // They don't cause an overflow so we don't have to count them for
+  // `max_digits`.
+  while (!*out && end != begin && *begin == '0') ++begin;
+
   T accumulator = *out;
   const char* significant_digits_end =
       (end - begin > max_digits) ? begin + max_digits : end;
diff --git a/absl/strings/internal/str_format/convert_test.cc b/absl/strings/internal/str_format/convert_test.cc
index 814ccf4c65e6..ab8d53914119 100644
--- a/absl/strings/internal/str_format/convert_test.cc
+++ b/absl/strings/internal/str_format/convert_test.cc
@@ -5,7 +5,9 @@
 #include <cmath>
 #include <string>
 
+#include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "absl/base/internal/raw_logging.h"
 #include "absl/strings/internal/str_format/bind.h"
 
 namespace absl {
@@ -157,12 +159,20 @@ TEST_F(FormatConvertTest, StringPrecision) {
   EXPECT_EQ("ABC", FormatPack(format2, {FormatArgImpl(p)}));
 }
 
+// Pointer formatting is implementation defined. This checks that the argument
+// can be matched to `ptr`.
+MATCHER_P(MatchesPointerString, ptr, "") {
+  if (ptr == nullptr && arg == "(nil)") {
+    return true;
+  }
+  void* parsed = nullptr;
+  if (sscanf(arg.c_str(), "%p", &parsed) != 1) {
+    ABSL_RAW_LOG(FATAL, "Could not parse %s", arg.c_str());
+  }
+  return ptr == parsed;
+}
+
 TEST_F(FormatConvertTest, Pointer) {
-#ifdef _MSC_VER
-  // MSVC's printf implementation prints pointers differently. We can't easily
-  // compare our implementation to theirs.
-  return;
-#endif
   static int x = 0;
   const int *xp = &x;
   char c = 'h';
@@ -173,48 +183,62 @@ TEST_F(FormatConvertTest, Pointer) {
   using VoidF = void (*)();
   VoidF fp = [] {}, fnil = nullptr;
   volatile char vc;
-  volatile char* vcp = &vc;
-  volatile char* vcnil = nullptr;
-  const FormatArgImpl args[] = {
+  volatile char *vcp = &vc;
+  volatile char *vcnil = nullptr;
+  const FormatArgImpl args_array[] = {
       FormatArgImpl(xp),   FormatArgImpl(cp),  FormatArgImpl(inil),
       FormatArgImpl(cnil), FormatArgImpl(mcp), FormatArgImpl(fp),
       FormatArgImpl(fnil), FormatArgImpl(vcp), FormatArgImpl(vcnil),
   };
-  struct Expectation {
-    std::string out;
-    const char *fmt;
-  };
-  const Expectation kExpect[] = {
-      {StrPrint("%p", &x), "%p"},
-      {StrPrint("%20p", &x), "%20p"},
-      {StrPrint("%.1p", &x), "%.1p"},
-      {StrPrint("%.20p", &x), "%.20p"},
-      {StrPrint("%30.20p", &x), "%30.20p"},
-
-      {StrPrint("%-p", &x), "%-p"},
-      {StrPrint("%-20p", &x), "%-20p"},
-      {StrPrint("%-.1p", &x), "%-.1p"},
-      {StrPrint("%.20p", &x), "%.20p"},
-      {StrPrint("%-30.20p", &x), "%-30.20p"},
-
-      {StrPrint("%p", cp), "%2$p"},   // const char*
-      {"(nil)", "%3$p"},              // null const char *
-      {"(nil)", "%4$p"},              // null const int *
-      {StrPrint("%p", mcp), "%5$p"},  // nonconst char*
-
-      {StrPrint("%p", fp), "%6$p"},   // function pointer
-      {StrPrint("%p", vcp), "%8$p"},  // function pointer
-
-#ifndef __APPLE__
-      // Apple's printf differs here (0x0 vs. nil)
-      {StrPrint("%p", fnil), "%7$p"},   // null function pointer
-      {StrPrint("%p", vcnil), "%9$p"},  // null function pointer
-#endif
-  };
-  for (const Expectation &e : kExpect) {
-    UntypedFormatSpecImpl format(e.fmt);
-    EXPECT_EQ(e.out, FormatPack(format, absl::MakeSpan(args))) << e.fmt;
-  }
+  auto args = absl::MakeConstSpan(args_array);
+
+  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%p"), args),
+              MatchesPointerString(&x));
+  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%20p"), args),
+              MatchesPointerString(&x));
+  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%.1p"), args),
+              MatchesPointerString(&x));
+  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%.20p"), args),
+              MatchesPointerString(&x));
+  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%30.20p"), args),
+              MatchesPointerString(&x));
+
+  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-p"), args),
+              MatchesPointerString(&x));
+  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-20p"), args),
+              MatchesPointerString(&x));
+  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-.1p"), args),
+              MatchesPointerString(&x));
+  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%.20p"), args),
+              MatchesPointerString(&x));
+  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-30.20p"), args),
+              MatchesPointerString(&x));
+
+  // const char*
+  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%2$p"), args),
+              MatchesPointerString(cp));
+  // null const int*
+  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%3$p"), args),
+              MatchesPointerString(nullptr));
+  // null const char*
+  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%4$p"), args),
+              MatchesPointerString(nullptr));
+  // nonconst char*
+  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%5$p"), args),
+              MatchesPointerString(mcp));
+
+  // function pointers
+  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%6$p"), args),
+              MatchesPointerString(reinterpret_cast<const void*>(fp)));
+  EXPECT_THAT(
+      FormatPack(UntypedFormatSpecImpl("%8$p"), args),
+      MatchesPointerString(reinterpret_cast<volatile const void *>(vcp)));
+
+  // null function pointers
+  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%7$p"), args),
+              MatchesPointerString(nullptr));
+  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%9$p"), args),
+              MatchesPointerString(nullptr));
 }
 
 struct Cardinal {
diff --git a/absl/strings/numbers_test.cc b/absl/strings/numbers_test.cc
index 77d7e7835b8b..4edf8891be3a 100644
--- a/absl/strings/numbers_test.cc
+++ b/absl/strings/numbers_test.cc
@@ -713,11 +713,11 @@ TEST(stringtest, safe_strtou64_base_length_delimited) {
   }
 }
 
-// feenableexcept() and fedisableexcept() are missing on macOS, MSVC,
-// and WebAssembly.
-#if defined(_MSC_VER) || defined(__APPLE__) || defined(__EMSCRIPTEN__)
-#define ABSL_MISSING_FEENABLEEXCEPT 1
-#define ABSL_MISSING_FEDISABLEEXCEPT 1
+// feenableexcept() and fedisableexcept() are extensions supported by some libc
+// implementations.
+#if defined(__GLIBC__) || defined(__BIONIC__)
+#define ABSL_HAVE_FEENABLEEXCEPT 1
+#define ABSL_HAVE_FEDISABLEEXCEPT 1
 #endif
 
 class SimpleDtoaTest : public testing::Test {
@@ -725,7 +725,7 @@ class SimpleDtoaTest : public testing::Test {
   void SetUp() override {
     // Store the current floating point env & clear away any pending exceptions.
     feholdexcept(&fp_env_);
-#ifndef ABSL_MISSING_FEENABLEEXCEPT
+#ifdef ABSL_HAVE_FEENABLEEXCEPT
     // Turn on floating point exceptions.
     feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
 #endif
@@ -735,7 +735,7 @@ class SimpleDtoaTest : public testing::Test {
     // Restore the floating point environment to the original state.
     // In theory fedisableexcept is unnecessary; fesetenv will also do it.
     // In practice, our toolchains have subtle bugs.
-#ifndef ABSL_MISSING_FEDISABLEEXCEPT
+#ifdef ABSL_HAVE_FEDISABLEEXCEPT
     fedisableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
 #endif
     fesetenv(&fp_env_);
diff --git a/absl/strings/string_view.h b/absl/strings/string_view.h
index 82344eb246b3..3438ccc12a7d 100644
--- a/absl/strings/string_view.h
+++ b/absl/strings/string_view.h
@@ -40,6 +40,13 @@ using std::string_view;
 
 #else  // ABSL_HAVE_STD_STRING_VIEW
 
+#if ABSL_HAVE_BUILTIN(__builtin_memcmp) || \
+    (defined(__GNUC__) && !defined(__clang__))
+#define ABSL_INTERNAL_STRING_VIEW_MEMCMP __builtin_memcmp
+#else  // ABSL_HAVE_BUILTIN(__builtin_memcmp)
+#define ABSL_INTERNAL_STRING_VIEW_MEMCMP memcmp
+#endif  // ABSL_HAVE_BUILTIN(__builtin_memcmp)
+
 #include <cassert>
 #include <cstddef>
 #include <cstring>
@@ -381,16 +388,13 @@ class string_view {
   // view. Note that in the case of data equality, a further comparison is made
   // on the respective sizes of the two `string_view`s to determine which is
   // smaller, equal, or greater.
-  int compare(string_view x) const noexcept {
-    auto min_length = (std::min)(length_, x.length_);
-    if (min_length > 0) {
-      int r = memcmp(ptr_, x.ptr_, min_length);
-      if (r < 0) return -1;
-      if (r > 0) return 1;
-    }
-    if (length_ < x.length_) return -1;
-    if (length_ > x.length_) return 1;
-    return 0;
+  constexpr int compare(string_view x) const noexcept {
+    return CompareImpl(
+        length_, x.length_,
+        length_ == 0 || x.length_ == 0
+            ? 0
+            : ABSL_INTERNAL_STRING_VIEW_MEMCMP(
+                  ptr_, x.ptr_, length_ < x.length_ ? length_ : x.length_));
   }
 
   // Overload of `string_view::compare()` for comparing a substring of the
@@ -528,6 +532,14 @@ class string_view {
 #endif
   }
 
+  static constexpr int CompareImpl(size_type length_a, size_type length_b,
+                                   int compare_result) {
+    return compare_result == 0 ? static_cast<int>(length_a > length_b) -
+                                     static_cast<int>(length_a < length_b)
+                               : static_cast<int>(compare_result > 0) -
+                                     static_cast<int>(compare_result < 0);
+  }
+
   const char* ptr_;
   size_type length_;
 };
@@ -535,33 +547,29 @@ class string_view {
 // This large function is defined inline so that in a fairly common case where
 // one of the arguments is a literal, the compiler can elide a lot of the
 // following comparisons.
-inline bool operator==(string_view x, string_view y) noexcept {
-  auto len = x.size();
-  if (len != y.size()) {
-    return false;
-  }
-
-  return x.data() == y.data() || len <= 0 ||
-         memcmp(x.data(), y.data(), len) == 0;
+constexpr bool operator==(string_view x, string_view y) noexcept {
+  return x.size() == y.size() &&
+         (x.empty() ||
+          ABSL_INTERNAL_STRING_VIEW_MEMCMP(x.data(), y.data(), x.size()) == 0);
 }
 
-inline bool operator!=(string_view x, string_view y) noexcept {
+constexpr bool operator!=(string_view x, string_view y) noexcept {
   return !(x == y);
 }
 
-inline bool operator<(string_view x, string_view y) noexcept {
-  auto min_size = (std::min)(x.size(), y.size());
-  const int r = min_size == 0 ? 0 : memcmp(x.data(), y.data(), min_size);
-  return (r < 0) || (r == 0 && x.size() < y.size());
+constexpr bool operator<(string_view x, string_view y) noexcept {
+  return x.compare(y) < 0;
 }
 
-inline bool operator>(string_view x, string_view y) noexcept { return y < x; }
+constexpr bool operator>(string_view x, string_view y) noexcept {
+  return y < x;
+}
 
-inline bool operator<=(string_view x, string_view y) noexcept {
+constexpr bool operator<=(string_view x, string_view y) noexcept {
   return !(y < x);
 }
 
-inline bool operator>=(string_view x, string_view y) noexcept {
+constexpr bool operator>=(string_view x, string_view y) noexcept {
   return !(x < y);
 }
 
@@ -570,6 +578,8 @@ std::ostream& operator<<(std::ostream& o, string_view piece);
 
 }  // namespace absl
 
+#undef ABSL_INTERNAL_STRING_VIEW_MEMCMP
+
 #endif  // ABSL_HAVE_STD_STRING_VIEW
 
 namespace absl {
diff --git a/absl/strings/string_view_test.cc b/absl/strings/string_view_test.cc
index eb8b170bbbde..86f2fbcd003a 100644
--- a/absl/strings/string_view_test.cc
+++ b/absl/strings/string_view_test.cc
@@ -974,6 +974,29 @@ TEST(StringViewTest, ConstexprCompiles) {
   EXPECT_EQ(cstr_strlen.length(), 3);
   constexpr absl::string_view cstr_strlen2 = "bar";
   EXPECT_EQ(cstr_strlen2, "bar");
+
+#if ABSL_HAVE_BUILTIN(__builtin_memcmp) || \
+    (defined(__GNUC__) && !defined(__clang__))
+#define ABSL_HAVE_CONSTEXPR_STRING_VIEW_COMPARISON 1
+#endif
+#ifdef ABSL_HAVE_CONSTEXPR_STRING_VIEW_COMPARISON
+  constexpr absl::string_view foo = "foo";
+  constexpr absl::string_view bar = "bar";
+  constexpr bool foo_eq_bar = foo == bar;
+  constexpr bool foo_ne_bar = foo != bar;
+  constexpr bool foo_lt_bar = foo < bar;
+  constexpr bool foo_le_bar = foo <= bar;
+  constexpr bool foo_gt_bar = foo > bar;
+  constexpr bool foo_ge_bar = foo >= bar;
+  constexpr int foo_compare_bar = foo.compare(bar);
+  EXPECT_FALSE(foo_eq_bar);
+  EXPECT_TRUE(foo_ne_bar);
+  EXPECT_FALSE(foo_lt_bar);
+  EXPECT_FALSE(foo_le_bar);
+  EXPECT_TRUE(foo_gt_bar);
+  EXPECT_TRUE(foo_ge_bar);
+  EXPECT_GT(foo_compare_bar, 0);
+#endif
 #endif
 
 #if !defined(__clang__) || 3 < __clang_major__ || \