summaryrefslogtreecommitdiff
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 729bdd76..4863ead2 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 ccff4441..3f907957 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 b58fad26..9090e9c8 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 f3c72324..fa9a8965 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 814ccf4c..ab8d5391 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 77d7e783..4edf8891 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 82344eb2..3438ccc1 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 eb8b170b..86f2fbcd 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__ || \