diff --git a/BUILD.gn b/BUILD.gn index cf86da92de..f942f77a85 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -643,7 +643,6 @@ if (skia_compile_sksl_tests) { "src/core/SkStream.cpp", "src/core/SkString.cpp", "src/core/SkStringUtils.cpp", - "src/core/SkStringView.cpp", "src/core/SkThreadID.cpp", "src/core/SkUtils.cpp", "src/core/SkVM.cpp", diff --git a/gn/core.gni b/gn/core.gni index 6d65f7651b..e553f77a6b 100644 --- a/gn/core.gni +++ b/gn/core.gni @@ -376,7 +376,6 @@ skia_core_sources = [ "$_src/core/SkString.cpp", "$_src/core/SkStringUtils.cpp", "$_src/core/SkStringUtils.h", - "$_src/core/SkStringView.cpp", "$_src/core/SkStroke.cpp", "$_src/core/SkStroke.h", "$_src/core/SkStrokeRec.cpp", diff --git a/include/core/SkString.h b/include/core/SkString.h index 5576e74221..1582159c04 100644 --- a/include/core/SkString.h +++ b/include/core/SkString.h @@ -10,6 +10,7 @@ #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" +#include "include/core/SkStringView.h" #include "include/core/SkTypes.h" #include "include/private/SkMalloc.h" #include "include/private/SkTArray.h" @@ -20,10 +21,6 @@ #include #include -namespace skstd { - class string_view; -} - /* Some helper functions for C strings */ static inline bool SkStrStartsWith(const char string[], const char prefixStr[]) { SkASSERT(string); diff --git a/include/core/SkStringView.h b/include/core/SkStringView.h index 184e681d26..a964dbab73 100644 --- a/include/core/SkStringView.h +++ b/include/core/SkStringView.h @@ -8,178 +8,42 @@ #ifndef SkStringView_DEFINED #define SkStringView_DEFINED -#include -#include -#include +#include +#include namespace skstd { -class string_view { -public: - using value_type = char; - using traits_type = std::char_traits; - using const_pointer = const value_type*; - using const_reference = const value_type&; - using iterator = const_pointer; - using const_iterator = iterator; - using size_type = size_t; - static constexpr size_type npos = size_type(-1); +using string_view = std::string_view; - constexpr string_view() - : fData(nullptr) - , fLength(0) {} - - constexpr string_view(const string_view&) = default; - - constexpr string_view(const_pointer data, size_type length) - : fData(data) - , fLength(length) {} - - string_view(const_pointer data) - : string_view(data, strlen(data)) {} - - string_view(const std::string& str) - : string_view(str.data(), str.length()) {} - - constexpr string_view& operator=(const string_view&) = default; - - constexpr iterator begin() const { - return fData; +// C++20 additions +inline constexpr bool starts_with(string_view str, string_view prefix) { + if (prefix.length() > str.length()) { + return false; } + return prefix.length() == 0 || !memcmp(str.data(), prefix.data(), prefix.length()); +} - constexpr iterator end() const { - return fData + fLength; +inline constexpr bool starts_with(string_view str, string_view::value_type c) { + return !str.empty() && str.front() == c; +} + +inline constexpr bool ends_with(string_view str, string_view suffix) { + if (suffix.length() > str.length()) { + return false; } + return suffix.length() == 0 || !memcmp(str.data() + str.length() - suffix.length(), + suffix.data(), suffix.length()); +} - constexpr const_reference operator[](size_type idx) const { - return fData[idx]; - } +inline constexpr bool ends_with(string_view str, string_view::value_type c) { + return !str.empty() && str.back() == c; +} - constexpr const_reference front() const { - return fData[0]; - } +// C++23 additions +inline constexpr bool contains(string_view str, string_view needle) { + return str.find(needle) != string_view::npos; +} - constexpr const_reference back() const { - return fData[fLength - 1]; - } - - constexpr const_pointer data() const { - return fData; - } - - constexpr size_type size() const { - return fLength; - } - - constexpr size_type length() const { - return fLength; - } - - constexpr bool empty() const { - return fLength == 0; - } - - constexpr bool starts_with(string_view s) const { - if (s.length() > fLength) { - return false; - } - return s.length() == 0 || !memcmp(fData, s.fData, s.length()); - } - - constexpr bool starts_with(value_type c) const { - return !this->empty() && this->front() == c; - } - - constexpr bool ends_with(string_view s) const { - if (s.length() > fLength) { - return false; - } - return s.length() == 0 || !memcmp(this->end() - s.length(), s.fData, s.length()); - } - - constexpr bool ends_with(value_type c) const { - return !this->empty() && this->back() == c; - } - - size_type find(string_view needle, size_type pos = 0) const { - if (needle.length() == 0) { - return 0; - } - if (this->length() < needle.length()) { - return npos; - } - const char* match = nullptr; - const char* start = this->data() + pos; - const char* end = start + this->length() - needle.length() + 1; - while ((match = (const char*)(memchr(start, needle[0], (size_t)(end - start))))) { - if (!memcmp(match, needle.data(), needle.length())) { - return (size_type)(match - this->data()); - } else { - start = match + 1; - } - } - return npos; - } - - bool contains(string_view needle) const { - return this->find(needle) != npos; - } - - constexpr string_view substr(size_type pos = 0, size_type count = npos) const { - if (pos > fLength) { - return {}; - } - return string_view{fData + pos, std::min(count, fLength - pos)}; - } - - constexpr void swap(string_view& other) { - const_pointer tempData = fData; - fData = other.fData; - other.fData = tempData; - - size_type tempLength = fLength; - fLength = other.fLength; - other.fLength = tempLength; - } - - constexpr void remove_prefix(size_type n) { - fData += n; - fLength -= n; - } - - constexpr void remove_suffix(size_type n) { - fLength -= n; - } - -private: - const_pointer fData; - size_type fLength; -}; - -bool operator==(string_view left, string_view right); - -bool operator!=(string_view left, string_view right); - -bool operator<(string_view left, string_view right); - -bool operator<=(string_view left, string_view right); - -bool operator>(string_view left, string_view right); - -bool operator>=(string_view left, string_view right); - -} // namespace skstd - -namespace std { - template<> struct hash { - size_t operator()(const skstd::string_view& s) const { - size_t result = 0; - for (auto iter = s.begin(); iter != s.end(); ++iter) { - result = result * 101 + (size_t) *iter; - } - return result; - } - }; -} // namespace std +} // namespace skstd #endif diff --git a/include/private/SkSLString.h b/include/private/SkSLString.h index 7d828760da..4fbc9241cd 100644 --- a/include/private/SkSLString.h +++ b/include/private/SkSLString.h @@ -35,10 +35,10 @@ public: void vappendf(const char* fmt, va_list va); bool starts_with(const char prefix[]) const { - return skstd::string_view(data(), size()).starts_with(prefix); + return skstd::starts_with(skstd::string_view(data(), size()), prefix); } bool ends_with(const char suffix[]) const { - return skstd::string_view(data(), size()).ends_with(suffix); + return skstd::ends_with(skstd::string_view(data(), size()), suffix); } bool consumeSuffix(const char suffix[]); diff --git a/src/core/SkStringView.cpp b/src/core/SkStringView.cpp deleted file mode 100644 index c279994dd0..0000000000 --- a/src/core/SkStringView.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2021 Google LLC. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#include "include/core/SkStringView.h" - -#include - -namespace skstd { - -bool operator==(string_view left, string_view right) { - if (left.length() != right.length()) { - return false; - } - return !string_view::traits_type::compare(left.data(), right.data(), left.length()); -} - -bool operator!=(string_view left, string_view right) { - return !(left == right); -} - -bool operator<(string_view left, string_view right) { - int result = string_view::traits_type::compare(left.data(), right.data(), - std::min(left.length(), right.length())); - if (!result) { - result = left.length() - right.length(); - } - return result < 0; -} - -bool operator<=(string_view left, string_view right) { - return !(left > right); -} - -bool operator>(string_view left, string_view right) { - return right < left; -} - -bool operator>=(string_view left, string_view right) { - return !(left < right); -} - -} // namespace skstd diff --git a/src/sksl/SkSLErrorReporter.cpp b/src/sksl/SkSLErrorReporter.cpp index 6be9dd05c4..005c9bdb3b 100644 --- a/src/sksl/SkSLErrorReporter.cpp +++ b/src/sksl/SkSLErrorReporter.cpp @@ -13,7 +13,7 @@ namespace SkSL { void ErrorReporter::error(skstd::string_view msg, PositionInfo position) { - if (msg.contains(Compiler::POISON_TAG)) { + if (skstd::contains(msg, Compiler::POISON_TAG)) { // don't report errors on poison values return; } @@ -22,7 +22,7 @@ void ErrorReporter::error(skstd::string_view msg, PositionInfo position) { } void ErrorReporter::error(int line, skstd::string_view msg) { - if (msg.contains(Compiler::POISON_TAG)) { + if (skstd::contains(msg, Compiler::POISON_TAG)) { // don't report errors on poison values return; } diff --git a/src/sksl/SkSLMangler.cpp b/src/sksl/SkSLMangler.cpp index aeb69ee457..9f582ccd09 100644 --- a/src/sksl/SkSLMangler.cpp +++ b/src/sksl/SkSLMangler.cpp @@ -14,7 +14,7 @@ String Mangler::uniqueName(skstd::string_view baseName, SymbolTable* symbolTable SkASSERT(symbolTable); // The inliner runs more than once, so the base name might already have been mangled and have a // prefix like "_123_x". Let's strip that prefix off to make the generated code easier to read. - if (baseName.starts_with("_")) { + if (skstd::starts_with(baseName, '_')) { // Determine if we have a string of digits. int offset = 1; while (isdigit(baseName[offset])) { diff --git a/src/sksl/SkSLOperators.cpp b/src/sksl/SkSLOperators.cpp index e35a8e57fb..f3eac6bb41 100644 --- a/src/sksl/SkSLOperators.cpp +++ b/src/sksl/SkSLOperators.cpp @@ -137,10 +137,10 @@ const char* Operator::operatorName() const { skstd::string_view Operator::tightOperatorName() const { skstd::string_view name = this->operatorName(); - if (name.starts_with(' ')) { + if (skstd::starts_with(name, ' ')) { name.remove_prefix(1); } - if (name.ends_with(' ')) { + if (skstd::ends_with(name, ' ')) { name.remove_suffix(1); } return name; diff --git a/src/sksl/SkSLString.cpp b/src/sksl/SkSLString.cpp index 65b620dfdc..6d13ef1d70 100644 --- a/src/sksl/SkSLString.cpp +++ b/src/sksl/SkSLString.cpp @@ -161,11 +161,13 @@ bool stoi(skstd::string_view s, SKSL_INT* value) { if (suffix == 'u' || suffix == 'U') { s.remove_suffix(1); } + String str(s); // s is not null-terminated + const char* strEnd = str.data() + str.length(); char* p; errno = 0; - unsigned long long result = strtoull(s.begin(), &p, /*base=*/0); + unsigned long long result = strtoull(str.data(), &p, /*base=*/0); *value = static_cast(result); - return p == s.end() && errno == 0 && result <= 0xFFFFFFFF; + return p == strEnd && errno == 0 && result <= 0xFFFFFFFF; } } // namespace SkSL diff --git a/src/sksl/ir/SkSLFunctionDeclaration.cpp b/src/sksl/ir/SkSLFunctionDeclaration.cpp index 191c9ce882..a9fd090f0a 100644 --- a/src/sksl/ir/SkSLFunctionDeclaration.cpp +++ b/src/sksl/ir/SkSLFunctionDeclaration.cpp @@ -19,7 +19,7 @@ static IntrinsicKind identify_intrinsic(skstd::string_view functionName) { }; #undef SKSL_INTRINSIC - if (functionName.starts_with('$')) { + if (skstd::starts_with(functionName, '$')) { functionName.remove_prefix(1); } @@ -416,12 +416,12 @@ String FunctionDeclaration::mangledName() const { // $ and add a unique mangling specifier, so user code can't conflict with the name. skstd::string_view name = this->name(); const char* builtinMarker = ""; - if (name.starts_with('$')) { + if (skstd::starts_with(name, '$')) { name.remove_prefix(1); builtinMarker = "Q"; // a unique, otherwise-unused mangle character } // GLSL forbids two underscores in a row; add an extra character if necessary to avoid this. - const char* splitter = name.ends_with('_') ? "x_" : "_"; + const char* splitter = skstd::ends_with(name, '_') ? "x_" : "_"; // Rename function to `funcname_returntypeparamtypes`. String result = name + splitter + builtinMarker + this->returnType().abbreviatedName(); for (const Variable* p : this->parameters()) { diff --git a/src/sksl/ir/SkSLType.h b/src/sksl/ir/SkSLType.h index 15cdd678f9..8773f3bba6 100644 --- a/src/sksl/ir/SkSLType.h +++ b/src/sksl/ir/SkSLType.h @@ -208,7 +208,7 @@ public: /** Returns true if this type is either private, or contains a private field (recursively). */ virtual bool isPrivate() const { - return this->name().starts_with("$"); + return skstd::starts_with(this->name(), '$'); } /** If this is an alias, returns the underlying type, otherwise returns this. */ diff --git a/src/sksl/ir/SkSLVariable.cpp b/src/sksl/ir/SkSLVariable.cpp index e364c74784..b747ad1382 100644 --- a/src/sksl/ir/SkSLVariable.cpp +++ b/src/sksl/ir/SkSLVariable.cpp @@ -36,7 +36,7 @@ std::unique_ptr Variable::Convert(const Context& context, int line, context.fConfig->fKind == ProgramKind::kFragment && name != Compiler::FRAGCOLOR_NAME) { context.fErrors->error(line, "out location=0, index=0 is reserved for sk_FragColor"); } - if (!context.fConfig->fIsBuiltinCode && name.starts_with('$')) { + if (!context.fConfig->fIsBuiltinCode && skstd::starts_with(name, '$')) { context.fErrors->error(line, String("name '") + name + "' is reserved"); } diff --git a/tests/SkStringViewTest.cpp b/tests/SkStringViewTest.cpp index c96918548f..ddbcc121cf 100644 --- a/tests/SkStringViewTest.cpp +++ b/tests/SkStringViewTest.cpp @@ -35,12 +35,12 @@ DEF_TEST(SkStringViewConstructors, r) { DEF_TEST(SkStringViewBasics, r) { skstd::string_view empty(""); REPORTER_ASSERT(r, empty.empty()); - REPORTER_ASSERT(r, !empty.starts_with('x')); - REPORTER_ASSERT(r, !empty.ends_with('x')); - REPORTER_ASSERT(r, !empty.starts_with("x")); - REPORTER_ASSERT(r, !empty.ends_with("x")); - REPORTER_ASSERT(r, empty.starts_with("")); - REPORTER_ASSERT(r, empty.ends_with("")); + REPORTER_ASSERT(r, !skstd::starts_with(empty, 'x')); + REPORTER_ASSERT(r, !skstd::ends_with(empty, 'x')); + REPORTER_ASSERT(r, !skstd::starts_with(empty, "x")); + REPORTER_ASSERT(r, !skstd::ends_with(empty, "x")); + REPORTER_ASSERT(r, skstd::starts_with(empty, "")); + REPORTER_ASSERT(r, skstd::ends_with(empty, "")); skstd::string_view xyz("xyz"); REPORTER_ASSERT(r, !xyz.empty()); @@ -48,23 +48,23 @@ DEF_TEST(SkStringViewBasics, r) { REPORTER_ASSERT(r, xyz.back() == 'z'); REPORTER_ASSERT(r, xyz.length() == 3); - REPORTER_ASSERT(r, xyz.starts_with('x')); - REPORTER_ASSERT(r, !xyz.starts_with('y')); - REPORTER_ASSERT(r, xyz.ends_with('z')); - REPORTER_ASSERT(r, !xyz.ends_with('y')); + REPORTER_ASSERT(r, skstd::starts_with(xyz, 'x')); + REPORTER_ASSERT(r, !skstd::starts_with(xyz, 'y')); + REPORTER_ASSERT(r, skstd::ends_with(xyz, 'z')); + REPORTER_ASSERT(r, !skstd::ends_with(xyz, 'y')); - REPORTER_ASSERT(r, xyz.starts_with("")); - REPORTER_ASSERT(r, xyz.ends_with("")); - REPORTER_ASSERT(r, xyz.starts_with("x")); - REPORTER_ASSERT(r, xyz.ends_with("z")); - REPORTER_ASSERT(r, !xyz.starts_with("xa")); - REPORTER_ASSERT(r, !xyz.ends_with("az")); - REPORTER_ASSERT(r, xyz.starts_with("xy")); - REPORTER_ASSERT(r, xyz.ends_with("yz")); - REPORTER_ASSERT(r, xyz.starts_with("xyz")); - REPORTER_ASSERT(r, xyz.ends_with("xyz")); - REPORTER_ASSERT(r, !xyz.starts_with("wxyz")); - REPORTER_ASSERT(r, !xyz.ends_with("wxyz")); + REPORTER_ASSERT(r, skstd::starts_with(xyz, "")); + REPORTER_ASSERT(r, skstd::ends_with(xyz, "")); + REPORTER_ASSERT(r, skstd::starts_with(xyz, "x")); + REPORTER_ASSERT(r, skstd::ends_with(xyz, "z")); + REPORTER_ASSERT(r, !skstd::starts_with(xyz, "xa")); + REPORTER_ASSERT(r, !skstd::ends_with(xyz, "az")); + REPORTER_ASSERT(r, skstd::starts_with(xyz, "xy")); + REPORTER_ASSERT(r, skstd::ends_with(xyz, "yz")); + REPORTER_ASSERT(r, skstd::starts_with(xyz, "xyz")); + REPORTER_ASSERT(r, skstd::ends_with(xyz, "xyz")); + REPORTER_ASSERT(r, !skstd::starts_with(xyz, "wxyz")); + REPORTER_ASSERT(r, !skstd::ends_with(xyz, "wxyz")); xyz.swap(empty); REPORTER_ASSERT(r, xyz == ""); @@ -154,7 +154,6 @@ DEF_TEST(SkStringViewSubstr, r) { REPORTER_ASSERT(r, xyz.substr(3, 0).empty()); REPORTER_ASSERT(r, xyz.substr(3).empty()); - REPORTER_ASSERT(r, xyz.substr(4).empty()); } DEF_TEST(SkStringViewFind, r) { @@ -175,19 +174,22 @@ DEF_TEST(SkStringViewFind, r) { REPORTER_ASSERT(r, skstd::string_view("ttttest1tttest2tttest3").find("test", 4) == 10); REPORTER_ASSERT(r, skstd::string_view("ttttest1tttest2tttest3").find("test2") == 10); REPORTER_ASSERT(r, skstd::string_view("ttttest1tttest2tttest3").find("test3") == 17); - REPORTER_ASSERT(r, skstd::string_view("ttttest1tttest2tttest3").contains("test")); - REPORTER_ASSERT(r, skstd::string_view("ttttest1tttest2tttest3").contains("test3")); - REPORTER_ASSERT(r, !skstd::string_view("ttttest1tttest2tttest3").contains("test4")); - REPORTER_ASSERT(r, skstd::string_view("").contains("")); - REPORTER_ASSERT(r, !skstd::string_view("").contains("a")); - REPORTER_ASSERT(r, skstd::string_view("abcabcd").contains("abcd")); - REPORTER_ASSERT(r, skstd::string_view("abc").contains("")); - REPORTER_ASSERT(r, skstd::string_view("abc").contains("a")); - REPORTER_ASSERT(r, skstd::string_view("abc").contains("b")); - REPORTER_ASSERT(r, skstd::string_view("abc").contains("c")); - REPORTER_ASSERT(r, skstd::string_view("abc").contains("ab")); - REPORTER_ASSERT(r, skstd::string_view("abc").contains("bc")); - REPORTER_ASSERT(r, !skstd::string_view("abc").contains("ac")); - REPORTER_ASSERT(r, !skstd::string_view("abc").contains("cb")); - REPORTER_ASSERT(r, !skstd::string_view("abc").contains("abcd")); +} + +DEF_TEST(SkStringViewContains, r) { + REPORTER_ASSERT(r, skstd::contains("ttttest1tttest2tttest3", "test")); + REPORTER_ASSERT(r, skstd::contains("ttttest1tttest2tttest3", "test3")); + REPORTER_ASSERT(r, !skstd::contains("ttttest1tttest2tttest3", "test4")); + REPORTER_ASSERT(r, skstd::contains("", "")); + REPORTER_ASSERT(r, !skstd::contains("", "a")); + REPORTER_ASSERT(r, skstd::contains("abcabcd", "abcd")); + REPORTER_ASSERT(r, skstd::contains("abc", "")); + REPORTER_ASSERT(r, skstd::contains("abc", "a")); + REPORTER_ASSERT(r, skstd::contains("abc", "b")); + REPORTER_ASSERT(r, skstd::contains("abc", "c")); + REPORTER_ASSERT(r, skstd::contains("abc", "ab")); + REPORTER_ASSERT(r, skstd::contains("abc", "bc")); + REPORTER_ASSERT(r, !skstd::contains("abc", "ac")); + REPORTER_ASSERT(r, !skstd::contains("abc", "cb")); + REPORTER_ASSERT(r, !skstd::contains("abc", "abcd")); }