diff --git a/base/BUILD.gn b/base/BUILD.gn index 8d9a08e6bdc144..5f04e581aefeca 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn @@ -729,6 +729,7 @@ jumbo_component("base") { "sha1.h", "single_thread_task_runner.h", "stl_util.h", + "strings/char_traits.h", "strings/latin1_string_conversions.cc", "strings/latin1_string_conversions.h", "strings/nullable_string16.cc", @@ -2306,6 +2307,7 @@ test("base_unittests") { "sequenced_task_runner_unittest.cc", "sha1_unittest.cc", "stl_util_unittest.cc", + "strings/char_traits_unittest.cc", "strings/nullable_string16_unittest.cc", "strings/pattern_unittest.cc", "strings/safe_sprintf_unittest.cc", diff --git a/base/strings/char_traits.h b/base/strings/char_traits.h new file mode 100644 index 00000000000000..6cfbad5af4d018 --- /dev/null +++ b/base/strings/char_traits.h @@ -0,0 +1,90 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_STRINGS_CHAR_TRAITS_H_ +#define BASE_STRINGS_CHAR_TRAITS_H_ + +#include + +namespace base { + +// constexpr version of http://en.cppreference.com/w/cpp/string/char_traits. +// This currently just implements the bits needed to support a (mostly) +// constexpr StringPiece. +// +// TODO(dcheng): Once we switch to C++17, most methods will become constexpr and +// we can switch over to using the one in the standard library. +template +struct CharTraits { + // Performs a lexographical comparison of the first N characters of |s1| and + // |s2|. Returns 0 if equal, -1 if |s1| is less than |s2|, and 1 if |s1| is + // greater than |s2|. + static constexpr int compare(const T* s1, const T* s2, size_t n) noexcept; + + // Returns the length of |s|, assuming null termination (and not including the + // terminating null). + static constexpr size_t length(const T* s) noexcept; +}; + +template +constexpr int CharTraits::compare(const T* s1, + const T* s2, + size_t n) noexcept { + for (; n; --n, ++s1, ++s2) { + if (*s1 < *s2) + return -1; + if (*s1 > *s2) + return 1; + } + return 0; +} + +template +constexpr size_t CharTraits::length(const T* s) noexcept { + size_t i = 0; + for (; *s; ++s) + ++i; + return i; +} + +// char specialization of CharTraits that can use clang's constexpr instrinsics, +// where available. +template <> +struct CharTraits { + static constexpr int compare(const char* s1, + const char* s2, + size_t n) noexcept; + static constexpr size_t length(const char* s) noexcept; +}; + +constexpr int CharTraits::compare(const char* s1, + const char* s2, + size_t n) noexcept { +#if __has_feature(cxx_constexpr_string_builtins) + return __builtin_memcmp(s1, s2, n); +#else + for (; n; --n, ++s1, ++s2) { + if (*s1 < *s2) + return -1; + if (*s1 > *s2) + return 1; + } + return 0; +#endif +} + +constexpr size_t CharTraits::length(const char* s) noexcept { +#if defined(__clang__) + return __builtin_strlen(s); +#else + size_t i = 0; + for (; *s; ++s) + ++i; + return i; +#endif +} + +} // namespace base + +#endif // BASE_STRINGS_CHAR_TRAITS_H_ diff --git a/base/strings/char_traits_unittest.cc b/base/strings/char_traits_unittest.cc new file mode 100644 index 00000000000000..31c421b5b98a65 --- /dev/null +++ b/base/strings/char_traits_unittest.cc @@ -0,0 +1,32 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/strings/char_traits.h" +#include "base/strings/string16.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { + +TEST(CharTraitsTest, CharCompare) { + static_assert(CharTraits::compare("abc", "def", 3) == -1, ""); + static_assert(CharTraits::compare("def", "def", 3) == 0, ""); + static_assert(CharTraits::compare("ghi", "def", 3) == 1, ""); +} + +TEST(CharTraitsTest, CharLength) { + static_assert(CharTraits::length("") == 0, ""); + static_assert(CharTraits::length("abc") == 3, ""); +} + +TEST(CharTraitsTest, Char16TCompare) { + static_assert(CharTraits::compare(u"abc", u"def", 3) == -1, ""); + static_assert(CharTraits::compare(u"def", u"def", 3) == 0, ""); + static_assert(CharTraits::compare(u"ghi", u"def", 3) == 1, ""); +} + +TEST(CharTraitsTest, Char16TLength) { + static_assert(CharTraits::length(u"abc") == 3, ""); +} + +} // namespace base diff --git a/base/strings/string_piece.cc b/base/strings/string_piece.cc index c26bb3652fa725..c82a223d14d4c9 100644 --- a/base/strings/string_piece.cc +++ b/base/strings/string_piece.cc @@ -44,7 +44,8 @@ bool operator==(const StringPiece& x, const StringPiece& y) { if (x.size() != y.size()) return false; - return StringPiece::wordmemcmp(x.data(), y.data(), x.size()) == 0; + return CharTraits::compare(x.data(), y.data(), + x.size()) == 0; } std::ostream& operator<<(std::ostream& o, const StringPiece& piece) { diff --git a/base/strings/string_piece.h b/base/strings/string_piece.h index 8a7d0d8710b452..775ea7cf001227 100644 --- a/base/strings/string_piece.h +++ b/base/strings/string_piece.h @@ -29,6 +29,7 @@ #include "base/base_export.h" #include "base/logging.h" +#include "base/strings/char_traits.h" #include "base/strings/string16.h" #include "base/strings/string_piece_forward.h" @@ -176,9 +177,12 @@ template class BasicStringPiece { // in a "const char*" or a "string" wherever a "StringPiece" is // expected (likewise for char16, string16, StringPiece16). constexpr BasicStringPiece() : ptr_(NULL), length_(0) {} - BasicStringPiece(const value_type* str) - : ptr_(str), - length_((str == NULL) ? 0 : STRING_TYPE::traits_type::length(str)) {} + // TODO(dcheng): Construction from nullptr is not allowed for + // std::basic_string_view, so remove the special handling for it. + // Note: This doesn't just use STRING_TYPE::traits_type::length(), since that + // isn't constexpr until C++17. + constexpr BasicStringPiece(const value_type* str) + : ptr_(str), length_(!str ? 0 : CharTraits::length(str)) {} BasicStringPiece(const STRING_TYPE& str) : ptr_(str.data()), length_(str.size()) {} constexpr BasicStringPiece(const value_type* offset, size_type len) @@ -245,8 +249,8 @@ template class BasicStringPiece { length_ -= n; } - int compare(const BasicStringPiece& x) const { - int r = wordmemcmp( + constexpr int compare(BasicStringPiece x) const noexcept { + int r = CharTraits::compare( ptr_, x.ptr_, (length_ < x.length_ ? length_ : x.length_)); if (r == 0) { if (length_ < x.length_) r = -1; @@ -275,12 +279,6 @@ template class BasicStringPiece { size_type max_size() const { return length_; } size_type capacity() const { return length_; } - static int wordmemcmp(const value_type* p, - const value_type* p2, - size_type N) { - return STRING_TYPE::traits_type::compare(p, p2, N); - } - // Sets the value of the given string target type to be the current string. // This saves a temporary over doing |a = b.as_string()| void CopyToString(STRING_TYPE* target) const { @@ -296,16 +294,18 @@ template class BasicStringPiece { } // Does "this" start with "x" - bool starts_with(const BasicStringPiece& x) const { - return ((this->length_ >= x.length_) && - (wordmemcmp(this->ptr_, x.ptr_, x.length_) == 0)); + constexpr bool starts_with(BasicStringPiece x) const noexcept { + return ( + (this->length_ >= x.length_) && + (CharTraits::compare(this->ptr_, x.ptr_, x.length_) == 0)); } // Does "this" end with "x" - bool ends_with(const BasicStringPiece& x) const { + constexpr bool ends_with(BasicStringPiece x) const noexcept { return ((this->length_ >= x.length_) && - (wordmemcmp(this->ptr_ + (this->length_-x.length_), - x.ptr_, x.length_) == 0)); + (CharTraits::compare( + this->ptr_ + (this->length_ - x.length_), x.ptr_, x.length_) == + 0)); } // find: Search for a character or substring at a given offset. @@ -395,7 +395,7 @@ inline bool operator!=(const StringPiece& x, const StringPiece& y) { } inline bool operator<(const StringPiece& x, const StringPiece& y) { - const int r = StringPiece::wordmemcmp( + const int r = CharTraits::compare( x.data(), y.data(), (x.size() < y.size() ? x.size() : y.size())); return ((r < 0) || ((r == 0) && (x.size() < y.size()))); } @@ -418,7 +418,8 @@ inline bool operator==(const StringPiece16& x, const StringPiece16& y) { if (x.size() != y.size()) return false; - return StringPiece16::wordmemcmp(x.data(), y.data(), x.size()) == 0; + return CharTraits::compare(x.data(), y.data(), + x.size()) == 0; } inline bool operator!=(const StringPiece16& x, const StringPiece16& y) { @@ -426,7 +427,7 @@ inline bool operator!=(const StringPiece16& x, const StringPiece16& y) { } inline bool operator<(const StringPiece16& x, const StringPiece16& y) { - const int r = StringPiece16::wordmemcmp( + const int r = CharTraits::compare( x.data(), y.data(), (x.size() < y.size() ? x.size() : y.size())); return ((r < 0) || ((r == 0) && (x.size() < y.size()))); } diff --git a/base/strings/string_piece_unittest.cc b/base/strings/string_piece_unittest.cc index 40e21e9958838b..17d08973ec6cfb 100644 --- a/base/strings/string_piece_unittest.cc +++ b/base/strings/string_piece_unittest.cc @@ -706,4 +706,100 @@ TYPED_TEST(CommonStringPieceTest, CheckConstructors) { ASSERT_EQ(empty, BasicStringPiece(empty.begin(), empty.end())); } +TEST(StringPieceTest, ConstexprCtor) { + { + constexpr StringPiece piece; + std::ignore = piece; + } + + { + constexpr StringPiece piece("abc"); + std::ignore = piece; + } + + { + constexpr StringPiece piece("abc", 2); + std::ignore = piece; + } +} + +TEST(StringPieceTest, ConstexprData) { + { + constexpr StringPiece piece; + static_assert(piece.data() == nullptr, ""); + } + + { + constexpr StringPiece piece("abc"); + static_assert(piece.data()[0] == 'a', ""); + static_assert(piece.data()[1] == 'b', ""); + static_assert(piece.data()[2] == 'c', ""); + } + + { + constexpr StringPiece piece("def", 2); + static_assert(piece.data()[0] == 'd', ""); + static_assert(piece.data()[1] == 'e', ""); + } +} + +TEST(StringPieceTest, ConstexprSize) { + { + constexpr StringPiece piece; + static_assert(piece.size() == 0, ""); + } + + { + constexpr StringPiece piece("abc"); + static_assert(piece.size() == 3, ""); + } + + { + constexpr StringPiece piece("def", 2); + static_assert(piece.size() == 2, ""); + } +} + +TEST(StringPieceTest, Compare) { + constexpr StringPiece piece = "def"; + + static_assert(piece.compare("ab") == 1, ""); + static_assert(piece.compare("abc") == 1, ""); + static_assert(piece.compare("abcd") == 1, ""); + static_assert(piece.compare("de") == 1, ""); + static_assert(piece.compare("def") == 0, ""); + static_assert(piece.compare("defg") == -1, ""); + static_assert(piece.compare("gh") == -1, ""); + static_assert(piece.compare("ghi") == -1, ""); + static_assert(piece.compare("ghij") == -1, ""); +} + +TEST(StringPieceTest, StartsWith) { + constexpr StringPiece piece("abc"); + + static_assert(piece.starts_with(""), ""); + static_assert(piece.starts_with("a"), ""); + static_assert(piece.starts_with("ab"), ""); + static_assert(piece.starts_with("abc"), ""); + + static_assert(!piece.starts_with("b"), ""); + static_assert(!piece.starts_with("bc"), ""); + + static_assert(!piece.starts_with("abcd"), ""); +} + +TEST(StringPieceTest, EndsWith) { + constexpr StringPiece piece("abc"); + + static_assert(piece.ends_with(""), ""); + static_assert(piece.ends_with("c"), ""); + static_assert(piece.ends_with("bc"), ""); + static_assert(piece.ends_with("abc"), ""); + + static_assert(!piece.ends_with("a"), ""); + static_assert(!piece.ends_with("ab"), ""); + + static_assert(!piece.ends_with("abcd"), ""); +} + } // namespace base diff --git a/components/viz/service/display/shader.cc b/components/viz/service/display/shader.cc index d7cd218fecb7e0..62bab35ad8bb44 100644 --- a/components/viz/service/display/shader.cc +++ b/components/viz/service/display/shader.cc @@ -11,6 +11,7 @@ #include #include "base/logging.h" +#include "base/strings/char_traits.h" #include "base/strings/stringprintf.h" #include "components/viz/service/display/static_geometry_binding.h" #include "gpu/command_buffer/client/gles2_interface.h" @@ -18,29 +19,18 @@ #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/size.h" -constexpr bool ConstexprEqual(const char* a, const char* b, size_t length) { - for (size_t i = 0; i < length; i++) { - if (a[i] != b[i]) - return false; - } - return true; -} - constexpr base::StringPiece StripLambda(base::StringPiece shader) { - // Must contain at least "[]() {}" and trailing null (included in size). - // TODO(jbroman): Simplify this once we're in a post-C++17 world, where - // starts_with and ends_with can easily be made constexpr. - DCHECK(shader.size() >= 7); // NOLINT - DCHECK(ConstexprEqual(shader.data(), "[]() {", 6)); + // Must contain at least "[]() {}". + DCHECK(shader.starts_with("[]() {")); + DCHECK(shader.ends_with("}")); shader.remove_prefix(6); - DCHECK(shader[shader.size() - 1] == '}'); // NOLINT shader.remove_suffix(1); return shader; } // Shaders are passed in with lambda syntax, which tricks clang-format into // handling them correctly. StripLambda removes this. -#define SHADER0(Src) StripLambda(base::StringPiece(#Src, sizeof(#Src) - 1)) +#define SHADER0(Src) StripLambda(#Src) #define HDR(x) \ do { \ diff --git a/content/shell/renderer/layout_test/blink_test_helpers.cc b/content/shell/renderer/layout_test/blink_test_helpers.cc index 47609d7afbcff6..af6f7427f1bc58 100644 --- a/content/shell/renderer/layout_test/blink_test_helpers.cc +++ b/content/shell/renderer/layout_test/blink_test_helpers.cc @@ -8,6 +8,8 @@ #include "base/files/file_util.h" #include "base/path_service.h" #include "base/stl_util.h" +#include "base/strings/string_piece.h" +#include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "content/public/common/content_switches.h" @@ -33,20 +35,19 @@ namespace { // Note that this isn't applied to external/wpt because tests in external/wpt // are accessed via http. WebURL RewriteAbsolutePathInCsswgTest(const std::string& utf8_url) { - const char kFileScheme[] = "file:///"; - const int kFileSchemeLen = base::size(kFileScheme) - 1; - if (utf8_url.compare(0, kFileSchemeLen, kFileScheme, kFileSchemeLen) != 0) + static constexpr base::StringPiece kFileScheme = "file:///"; + if (!base::StartsWith(utf8_url, kFileScheme, base::CompareCase::SENSITIVE)) return WebURL(); if (utf8_url.find("/LayoutTests/") != std::string::npos) return WebURL(); #if defined(OS_WIN) // +3 for a drive letter, :, and /. - const int kFileSchemeAndDriveLen = kFileSchemeLen + 3; + static constexpr size_t kFileSchemeAndDriveLen = kFileScheme.size() + 3; if (utf8_url.size() <= kFileSchemeAndDriveLen) return WebURL(); std::string path = utf8_url.substr(kFileSchemeAndDriveLen); #else - std::string path = utf8_url.substr(kFileSchemeLen); + std::string path = utf8_url.substr(kFileScheme.size()); #endif base::FilePath new_path = content::GetWebKitRootDirFilePath() .Append(FILE_PATH_LITERAL("LayoutTests/")) @@ -179,30 +180,28 @@ WebURL RewriteLayoutTestsURL(const std::string& utf8_url, bool is_wpt_mode) { return WebURL(GURL(utf8_url)); } - const char kGenPrefix[] = "file:///gen/"; - const int kGenPrefixLen = base::size(kGenPrefix) - 1; + static constexpr base::StringPiece kGenPrefix = "file:///gen/"; // Map "file:///gen/" to "file:///gen/". - if (!utf8_url.compare(0, kGenPrefixLen, kGenPrefix, kGenPrefixLen)) { + if (base::StartsWith(utf8_url, kGenPrefix, base::CompareCase::SENSITIVE)) { base::FilePath gen_directory_path = GetBuildDirectory().Append(FILE_PATH_LITERAL("gen/")); std::string new_url = std::string("file://") + gen_directory_path.AsUTF8Unsafe() + - utf8_url.substr(kGenPrefixLen); + utf8_url.substr(kGenPrefix.size()); return WebURL(GURL(new_url)); } - const char kPrefix[] = "file:///tmp/LayoutTests/"; - const int kPrefixLen = base::size(kPrefix) - 1; + static constexpr base::StringPiece kPrefix = "file:///tmp/LayoutTests/"; - if (utf8_url.compare(0, kPrefixLen, kPrefix, kPrefixLen)) + if (!base::StartsWith(utf8_url, kPrefix, base::CompareCase::SENSITIVE)) return WebURL(GURL(utf8_url)); base::FilePath replace_path = GetWebKitRootDirFilePath().Append(FILE_PATH_LITERAL("LayoutTests/")); std::string utf8_path = replace_path.AsUTF8Unsafe(); std::string new_url = - std::string("file://") + utf8_path + utf8_url.substr(kPrefixLen); + std::string("file://") + utf8_path + utf8_url.substr(kPrefix.size()); return WebURL(GURL(new_url)); } diff --git a/services/network/cross_origin_read_blocking_unittest.cc b/services/network/cross_origin_read_blocking_unittest.cc index 7722ca2320f2d9..68a1e393307426 100644 --- a/services/network/cross_origin_read_blocking_unittest.cc +++ b/services/network/cross_origin_read_blocking_unittest.cc @@ -140,7 +140,6 @@ TEST(CrossOriginReadBlockingTest, SniffForJSON) { StringPiece json_data2("{ \"key \\\" \" \t\t\r\n:"); StringPiece non_json_data0("\t\t\r\n { name : \"chrome\", "); StringPiece non_json_data1("\t\t\r\n foo({ \"name\" : \"chrome\", "); - StringPiece empty_data(""); EXPECT_EQ(SniffingResult::kYes, CrossOriginReadBlocking::SniffForJSON(json_data));