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>
This commit is contained in:
parent
83859e27c3
commit
64c971350e
1
BUILD.gn
1
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",
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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[]);
|
||||
|
@ -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
|
@ -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;
|
||||
}
|
||||
|
@ -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])) {
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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()) {
|
||||
|
@ -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. */
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
@ -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"));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user