Reland "Use native std::string_view."

This is a reland of 64c971350e

Original change's description:
> Use native std::string_view.
>
> We also used some string_view functionality from C++20/23. These have
> been replaced with free functions with the same name.
>
> Change-Id: I3bf40f99aeb500495f344fd8c6872619267d42be
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/500897
> Reviewed-by: Herb Derby <herb@google.com>
> Auto-Submit: John Stiles <johnstiles@google.com>
> Reviewed-by: Brian Osman <brianosman@google.com>
> Commit-Queue: John Stiles <johnstiles@google.com>

Change-Id: I4ff237381c16179f716ecde1929154fdd4ad3442
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/501480
Reviewed-by: Brian Osman <brianosman@google.com>
Reviewed-by: Herb Derby <herb@google.com>
Commit-Queue: John Stiles <johnstiles@google.com>
This commit is contained in:
John Stiles 2022-01-31 21:45:35 -05:00 committed by SkCQ
parent 17d0fc087c
commit 2c764e1eac
14 changed files with 84 additions and 266 deletions

View File

@ -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",

View File

@ -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",

View File

@ -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 <atomic>
#include <string>
namespace skstd {
class string_view;
}
/* Some helper functions for C strings */
static inline bool SkStrStartsWith(const char string[], const char prefixStr[]) {
SkASSERT(string);

View File

@ -8,178 +8,42 @@
#ifndef SkStringView_DEFINED
#define SkStringView_DEFINED
#include <algorithm>
#include <cstring>
#include <string>
#include <string.h>
#include <string_view>
namespace skstd {
class string_view {
public:
using value_type = char;
using traits_type = std::char_traits<value_type>;
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<skstd::string_view> {
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

View File

@ -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[]);

View File

@ -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 <algorithm>
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

View File

@ -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;
}

View File

@ -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])) {

View File

@ -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;

View File

@ -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<SKSL_INT>(result);
return p == s.end() && errno == 0 && result <= 0xFFFFFFFF;
return p == strEnd && errno == 0 && result <= 0xFFFFFFFF;
}
} // namespace SkSL

View File

@ -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()) {

View File

@ -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. */

View File

@ -36,7 +36,7 @@ std::unique_ptr<Variable> 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");
}

View File

@ -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"));
}