#include "Util.hpp"
+#include <util/string_utils.hpp>
+
using nonstd::nullopt;
using nonstd::optional;
using nonstd::string_view;
m_args.erase(std::remove_if(m_args.begin(),
m_args.end(),
[&prefix](const auto& s) {
- return Util::starts_with(s, prefix);
+ return util::starts_with(s, prefix);
}),
m_args.end());
}
-// Copyright (C) 2019-2020 Joel Rosdahl and other contributors
+// Copyright (C) 2019-2021 Joel Rosdahl and other contributors
//
// See doc/AUTHORS.adoc for a complete list of contributors.
//
#include "Manifest.hpp"
#include "Result.hpp"
-#include "Util.hpp"
+
+#include <util/string_utils.hpp>
const Stat&
CacheFile::lstat() const
CacheFile::Type
CacheFile::type() const
{
- if (Util::ends_with(m_path, Manifest::k_file_suffix)) {
+ if (util::ends_with(m_path, Manifest::k_file_suffix)) {
return Type::manifest;
- } else if (Util::ends_with(m_path, Result::k_file_suffix)) {
+ } else if (util::ends_with(m_path, Result::k_file_suffix)) {
return Type::result;
} else {
return Type::unknown;
for (char** env = environ; *env; ++env) {
std::string setting = *env;
const std::string prefix = "CCACHE_";
- if (!Util::starts_with(setting, prefix)) {
+ if (!util::starts_with(setting, prefix)) {
continue;
}
size_t equal_pos = setting.find('=');
std::string key = setting.substr(prefix.size(), equal_pos - prefix.size());
std::string value = setting.substr(equal_pos + 1);
- bool negate = Util::starts_with(key, "NO");
+ bool negate = util::starts_with(key, "NO");
if (negate) {
key = key.substr(2);
}
// In file included from X<path>X:1:
// X<path>X:1:2: ...
- if (Util::starts_with(line, in_file_included_from)) {
+ if (util::starts_with(line, in_file_included_from)) {
result += in_file_included_from;
line = line.substr(in_file_included_from.length());
}
const std::string& apparent_cwd,
nonstd::string_view path)
{
- if (base_dir.empty() || !Util::starts_with(path, base_dir)) {
+ if (base_dir.empty() || !util::starts_with(path, base_dir)) {
return std::string(path);
}
size_t end = 0;
unsigned long long result = 0;
bool failed = false;
- if (Util::starts_with(stripped_value, "-")) {
+ if (util::starts_with(stripped_value, "-")) {
failed = true;
} else {
try {
// Get directory name of path.
nonstd::string_view dir_name(nonstd::string_view path);
-// Return true if `suffix` is a suffix of `string`.
-inline bool
-ends_with(nonstd::string_view string, nonstd::string_view suffix)
-{
- return string.ends_with(suffix);
-}
-
// Like create_dir but throws Fatal on error.
void ensure_dir_exists(nonstd::string_view dir);
const char* separators,
util::Tokenizer::Mode mode = util::Tokenizer::Mode::skip_empty);
-// Return true if `prefix` is a prefix of `string`.
-inline bool
-starts_with(const char* string, nonstd::string_view prefix)
-{
- // Optimized version of starts_with(string_view, string_view): avoid computing
- // the length of the string argument.
- return strncmp(string, prefix.data(), prefix.length()) == 0;
-}
-
-// Return true if `prefix` is a prefix of `string`.
-inline bool
-starts_with(nonstd::string_view string, nonstd::string_view prefix)
-{
- return string.starts_with(prefix);
-}
-
// Returns a copy of string with the specified ANSI CSI sequences removed.
[[nodiscard]] std::string strip_ansi_csi_seqs(nonstd::string_view string);
#include "language.hpp"
#include <core/wincompat.hpp>
+#include <util/string_utils.hpp>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
std::string new_profile_path;
bool new_profile_use = false;
- if (Util::starts_with(arg, "-fprofile-dir=")) {
+ if (util::starts_with(arg, "-fprofile-dir=")) {
new_profile_path = arg.substr(arg.find('=') + 1);
} else if (arg == "-fprofile-generate" || arg == "-fprofile-instr-generate") {
ctx.args_info.profile_generate = true;
// GCC uses $PWD/$(basename $obj).
new_profile_path = ctx.apparent_cwd;
}
- } else if (Util::starts_with(arg, "-fprofile-generate=")
- || Util::starts_with(arg, "-fprofile-instr-generate=")) {
+ } else if (util::starts_with(arg, "-fprofile-generate=")
+ || util::starts_with(arg, "-fprofile-instr-generate=")) {
ctx.args_info.profile_generate = true;
new_profile_path = arg.substr(arg.find('=') + 1);
} else if (arg == "-fprofile-use" || arg == "-fprofile-instr-use"
if (ctx.args_info.profile_path.empty()) {
new_profile_path = ".";
}
- } else if (Util::starts_with(arg, "-fprofile-use=")
- || Util::starts_with(arg, "-fprofile-instr-use=")
- || Util::starts_with(arg, "-fprofile-sample-use=")
- || Util::starts_with(arg, "-fauto-profile=")) {
+ } else if (util::starts_with(arg, "-fprofile-use=")
+ || util::starts_with(arg, "-fprofile-instr-use=")
+ || util::starts_with(arg, "-fprofile-sample-use=")
+ || util::starts_with(arg, "-fauto-profile=")) {
new_profile_use = true;
new_profile_path = arg.substr(arg.find('=') + 1);
} else {
}
// Handle "@file" argument.
- if (Util::starts_with(args[i], "@") || Util::starts_with(args[i], "-@")) {
+ if (util::starts_with(args[i], "@") || util::starts_with(args[i], "-@")) {
const char* argpath = args[i].c_str() + 1;
if (argpath[-1] == '-') {
}
// These are always too hard.
- if (compopt_too_hard(args[i]) || Util::starts_with(args[i], "-fdump-")
- || Util::starts_with(args[i], "-MJ")) {
+ if (compopt_too_hard(args[i]) || util::starts_with(args[i], "-fdump-")
+ || util::starts_with(args[i], "-MJ")) {
LOG("Compiler option {} is unsupported", args[i]);
return Statistic::unsupported_compiler_option;
}
}
// -Xarch_* options are too hard.
- if (Util::starts_with(args[i], "-Xarch_")) {
+ if (util::starts_with(args[i], "-Xarch_")) {
if (i == args.size() - 1) {
LOG("Missing argument to {}", args[i]);
return Statistic::bad_compiler_arguments;
return nullopt;
}
- if (Util::starts_with(args[i], "-x")) {
+ if (util::starts_with(args[i], "-x")) {
if (args[i].length() >= 3 && !islower(args[i][2])) {
// -xCODE (where CODE can be e.g. Host or CORE-AVX2, always starting with
// an uppercase letter) is an ordinary Intel compiler option, not a
}
// Alternate form of -o with no space. Nvcc does not support this.
- if (Util::starts_with(args[i], "-o")
+ if (util::starts_with(args[i], "-o")
&& config.compiler_type() != CompilerType::nvcc) {
args_info.output_obj =
Util::make_relative_path(ctx, string_view(args[i]).substr(2));
return nullopt;
}
- if (Util::starts_with(args[i], "-fdebug-prefix-map=")
- || Util::starts_with(args[i], "-ffile-prefix-map=")) {
+ if (util::starts_with(args[i], "-fdebug-prefix-map=")
+ || util::starts_with(args[i], "-ffile-prefix-map=")) {
std::string map = args[i].substr(args[i].find('=') + 1);
args_info.debug_prefix_maps.push_back(map);
state.common_args.push_back(args[i]);
// Debugging is handled specially, so that we know if we can strip line
// number info.
- if (Util::starts_with(args[i], "-g")) {
+ if (util::starts_with(args[i], "-g")) {
state.common_args.push_back(args[i]);
- if (Util::starts_with(args[i], "-gdwarf")) {
+ if (util::starts_with(args[i], "-gdwarf")) {
// Selection of DWARF format (-gdwarf or -gdwarf-<version>) enables
// debug info on level 2.
args_info.generating_debuginfo = true;
return nullopt;
}
- if (Util::starts_with(args[i], "-gz")) {
+ if (util::starts_with(args[i], "-gz")) {
// -gz[=type] neither disables nor enables debug info.
return nullopt;
}
return nullopt;
}
- if (Util::starts_with(args[i], "-MF")) {
+ if (util::starts_with(args[i], "-MF")) {
state.dependency_filename_specified = true;
std::string dep_file;
return nullopt;
}
- if (Util::starts_with(args[i], "-MQ") || Util::starts_with(args[i], "-MT")) {
+ if (util::starts_with(args[i], "-MQ") || util::starts_with(args[i], "-MT")) {
ctx.args_info.dependency_target_specified = true;
if (args[i].size() == 3) {
return nullopt;
}
- if (Util::starts_with(args[i], "-fprofile-")
- || Util::starts_with(args[i], "-fauto-profile")
+ if (util::starts_with(args[i], "-fprofile-")
+ || util::starts_with(args[i], "-fauto-profile")
|| args[i] == "-fbranch-probabilities") {
if (!process_profiling_option(ctx, args[i])) {
// The failure is logged by process_profiling_option.
return nullopt;
}
- if (Util::starts_with(args[i], "-fsanitize-blacklist=")) {
+ if (util::starts_with(args[i], "-fsanitize-blacklist=")) {
args_info.sanitize_blacklists.emplace_back(args[i].substr(21));
state.common_args.push_back(args[i]);
return nullopt;
}
- if (Util::starts_with(args[i], "--sysroot=")) {
+ if (util::starts_with(args[i], "--sysroot=")) {
auto path = string_view(args[i]).substr(10);
auto relpath = Util::make_relative_path(ctx, path);
state.common_args.push_back("--sysroot=" + relpath);
return nullopt;
}
- if (Util::starts_with(args[i], "-Wp,")) {
+ if (util::starts_with(args[i], "-Wp,")) {
if (args[i].find(",-P,") != std::string::npos
- || Util::ends_with(args[i], ",-P")) {
+ || util::ends_with(args[i], ",-P")) {
// -P together with other preprocessor options is just too hard.
return Statistic::unsupported_compiler_option;
- } else if (Util::starts_with(args[i], "-Wp,-MD,")
+ } else if (util::starts_with(args[i], "-Wp,-MD,")
&& args[i].find(',', 8) == std::string::npos) {
args_info.generating_dependencies = true;
state.dependency_filename_specified = true;
Util::make_relative_path(ctx, string_view(args[i]).substr(8));
state.dep_args.push_back(args[i]);
return nullopt;
- } else if (Util::starts_with(args[i], "-Wp,-MMD,")
+ } else if (util::starts_with(args[i], "-Wp,-MMD,")
&& args[i].find(',', 9) == std::string::npos) {
args_info.generating_dependencies = true;
state.dependency_filename_specified = true;
Util::make_relative_path(ctx, string_view(args[i]).substr(9));
state.dep_args.push_back(args[i]);
return nullopt;
- } else if (Util::starts_with(args[i], "-Wp,-D")
+ } else if (util::starts_with(args[i], "-Wp,-D")
&& args[i].find(',', 6) == std::string::npos) {
// Treat it like -D.
state.cpp_args.push_back(args[i].substr(4));
return nullopt;
} else if (args[i] == "-Wp,-MP"
- || (args[i].size() > 8 && Util::starts_with(args[i], "-Wp,-M")
+ || (args[i].size() > 8 && util::starts_with(args[i], "-Wp,-M")
&& args[i][7] == ','
&& (args[i][6] == 'F' || args[i][6] == 'Q'
|| args[i][6] == 'T')
}
// Input charset needs to be handled specially.
- if (Util::starts_with(args[i], "-finput-charset=")) {
+ if (util::starts_with(args[i], "-finput-charset=")) {
state.input_charset_option = args[i];
return nullopt;
}
#include <core/types.hpp>
#include <core/wincompat.hpp>
#include <util/path_utils.hpp>
+#include <util/string_utils.hpp>
#include "third_party/fmt/core.h"
#include "third_party/nonstd/optional.hpp"
}
// Canonicalize path for comparison; Clang uses ./header.h.
- if (Util::starts_with(path, "./")) {
+ if (util::starts_with(path, "./")) {
path.erase(0, 2);
}
// GCC:
&& ((q[1] == ' ' && q[2] >= '0' && q[2] <= '9')
// GCC precompiled header:
- || Util::starts_with(&q[1], pragma_gcc_pch_preprocess)
+ || util::starts_with(&q[1], pragma_gcc_pch_preprocess)
// HP/AIX:
|| (q[1] == 'l' && q[2] == 'i' && q[3] == 'n' && q[4] == 'e'
&& q[5] == ' '))
&& (q == data.data() || q[-1] == '\n')) {
// Workarounds for preprocessor linemarker bugs in GCC version 6.
if (q[2] == '3') {
- if (Util::starts_with(q, hash_31_command_line_newline)) {
+ if (util::starts_with(q, hash_31_command_line_newline)) {
// Bogus extra line with #31, after the regular #1: Ignore the whole
// line, and continue parsing.
hash.hash(p, q - p);
q++;
p = q;
continue;
- } else if (Util::starts_with(q, hash_32_command_line_2_newline)) {
+ } else if (util::starts_with(q, hash_32_command_line_2_newline)) {
// Bogus wrong line with #32, instead of regular #1: Replace the line
// number with the usual one.
hash.hash(p, q - p);
bool should_hash_inc_path = true;
if (!ctx.config.hash_dir()) {
- if (Util::starts_with(inc_path, ctx.apparent_cwd)
- && Util::ends_with(inc_path, "//")) {
+ if (util::starts_with(inc_path, ctx.apparent_cwd)
+ && util::ends_with(inc_path, "//")) {
// When compiling with -g or similar, GCC adds the absolute path to
// CWD like this:
//
hash.hash_delimiter("cc_mtime");
hash.hash(st.size());
hash.hash(st.mtime());
- } else if (Util::starts_with(ctx.config.compiler_check(), "string:")) {
+ } else if (util::starts_with(ctx.config.compiler_check(), "string:")) {
hash.hash_delimiter("cc_hash");
hash.hash(&ctx.config.compiler_check()[7]);
} else if (ctx.config.compiler_check() == "content" || !allow_command) {
old_path,
new_path,
ctx.apparent_cwd);
- if (Util::starts_with(ctx.apparent_cwd, old_path)) {
+ if (util::starts_with(ctx.apparent_cwd, old_path)) {
dir_to_hash = new_path + ctx.apparent_cwd.substr(old_path.size());
}
}
const auto& prefix = string_view(pattern).substr(0, pattern.length() - 1);
return (
pattern == arg
- || (Util::ends_with(pattern, "*") && Util::starts_with(arg, prefix)));
+ || (util::ends_with(pattern, "*") && util::starts_with(arg, prefix)));
});
}
i++;
continue;
}
- if (Util::starts_with(args[i], "-L") && !is_clang) {
+ if (util::starts_with(args[i], "-L") && !is_clang) {
continue;
}
// -Wl,... doesn't affect compilation (except for clang).
- if (Util::starts_with(args[i], "-Wl,") && !is_clang) {
+ if (util::starts_with(args[i], "-Wl,") && !is_clang) {
continue;
}
// CCACHE_BASEDIR to reuse results across different directories. Skip using
// the value of the option from hashing but still hash the existence of the
// option.
- if (Util::starts_with(args[i], "-fdebug-prefix-map=")) {
+ if (util::starts_with(args[i], "-fdebug-prefix-map=")) {
hash.hash_delimiter("arg");
hash.hash("-fdebug-prefix-map=");
continue;
}
- if (Util::starts_with(args[i], "-ffile-prefix-map=")) {
+ if (util::starts_with(args[i], "-ffile-prefix-map=")) {
hash.hash_delimiter("arg");
hash.hash("-ffile-prefix-map=");
continue;
}
- if (Util::starts_with(args[i], "-fmacro-prefix-map=")) {
+ if (util::starts_with(args[i], "-fmacro-prefix-map=")) {
hash.hash_delimiter("arg");
hash.hash("-fmacro-prefix-map=");
continue;
// If we're generating dependencies, we make sure to skip the filename of
// the dependency file, since it doesn't impact the output.
if (ctx.args_info.generating_dependencies) {
- if (Util::starts_with(args[i], "-Wp,")) {
- if (Util::starts_with(args[i], "-Wp,-MD,")
+ if (util::starts_with(args[i], "-Wp,")) {
+ if (util::starts_with(args[i], "-Wp,-MD,")
&& args[i].find(',', 8) == std::string::npos) {
hash.hash(args[i].data(), 8);
continue;
- } else if (Util::starts_with(args[i], "-Wp,-MMD,")
+ } else if (util::starts_with(args[i], "-Wp,-MMD,")
&& args[i].find(',', 9) == std::string::npos) {
hash.hash(args[i].data(), 9);
continue;
}
- } else if (Util::starts_with(args[i], "-MF")) {
+ } else if (util::starts_with(args[i], "-MF")) {
// In either case, hash the "-MF" part.
hash.hash_delimiter("arg");
hash.hash(args[i].data(), 3);
}
}
- if (Util::starts_with(args[i], "-specs=")
- || Util::starts_with(args[i], "--specs=")
+ if (util::starts_with(args[i], "-specs=")
+ || util::starts_with(args[i], "--specs=")
|| (args[i] == "-specs" || args[i] == "--specs")
|| args[i] == "--config") {
std::string path;
}
}
- if (Util::starts_with(args[i], "-fplugin=")) {
+ if (util::starts_with(args[i], "-fplugin=")) {
auto st = Stat::stat(&args[i][9], Stat::OnError::log);
if (st) {
hash.hash_delimiter("plugin");
#include "Statistics.hpp"
#include "Util.hpp"
+#include <util/string_utils.hpp>
+
#ifdef INODE_CACHE_SUPPORTED
# include "InodeCache.hpp"
#endif
break;
}
- if (Util::ends_with(file.path(), ".stderr")) {
+ if (util::ends_with(file.path(), ".stderr")) {
// In order to be nice to legacy ccache versions, make sure that the .o
// file is deleted before .stderr, because if the ccache process gets
// killed after deleting the .stderr but before deleting the .o, the
#include "fmtmacros.hpp"
#include <core/wincompat.hpp>
+#include <util/string_utils.hpp>
#include "third_party/fmt/core.h"
sub_progress_receiver(0.1 + 0.9 * i / files.size());
}
- if (Util::ends_with(subdir, "f")) {
+ if (util::ends_with(subdir, "f")) {
// Wait here instead of after Util::for_each_level_1_subdir to avoid
// updating the progress bar to 100% before all work is done.
thread_pool.shut_down();
// Add "echo" command.
bool using_cmd_exe;
- if (Util::starts_with(adjusted_command, "echo")) {
+ if (util::starts_with(adjusted_command, "echo")) {
adjusted_command = FMT("cmd.exe /c \"{}\"", adjusted_command);
using_cmd_exe = true;
- } else if (Util::starts_with(adjusted_command, "%compiler%")
+ } else if (util::starts_with(adjusted_command, "%compiler%")
&& compiler == "echo") {
adjusted_command =
FMT("cmd.exe /c \"{}{}\"", compiler, adjusted_command.substr(10));
{
ASSERT(url.scheme() == "file");
const auto& dir = url.path();
- if (!Util::starts_with(dir, "/")) {
+ if (!util::starts_with(dir, "/")) {
throw Error("invalid file path \"{}\" - directory must start with a slash",
dir);
}
// slashes must be stripped.
const auto rendered_value = host_and_port_only.str();
const auto prefix = nonstd::string_view{"//"};
- if (!Util::starts_with(rendered_value, prefix)) {
+ if (!util::starts_with(rendered_value, prefix)) {
throw Error(
"Expected partial URL to start with '{}': '{}'", prefix, rendered_value);
}
#include <sys/stat.h> // for mode_t
+#include <cstring>
#include <string>
#include <utility>
namespace util {
+// Return true if `suffix` is a suffix of `string`.
+inline bool
+ends_with(const nonstd::string_view string, const nonstd::string_view suffix)
+{
+ return string.ends_with(suffix);
+}
+
// Parse `value` (an octal integer).
nonstd::expected<mode_t, std::string> parse_umask(const std::string& value);
std::pair<nonstd::string_view, nonstd::optional<nonstd::string_view>>
split_once(nonstd::string_view string, char split_char);
+// Return true if `prefix` is a prefix of `string`.
+inline bool
+starts_with(const char* string, const nonstd::string_view prefix)
+{
+ // Optimized version of starts_with(string_view, string_view): avoid computing
+ // the length of the string argument.
+ return std::strncmp(string, prefix.data(), prefix.length()) == 0;
+}
+
+// Return true if `prefix` is a prefix of `string`.
+inline bool
+starts_with(const nonstd::string_view string, const nonstd::string_view prefix)
+{
+ return string.starts_with(prefix);
+}
+
// Strip whitespace from left and right side of a string.
[[nodiscard]] std::string strip_whitespace(nonstd::string_view string);
CHECK(Util::strip_ansi_csi_seqs(input) == "Normal, bold, red, bold green.\n");
}
-TEST_CASE("Util::ends_with")
-{
- CHECK(Util::ends_with("", ""));
- CHECK(Util::ends_with("x", ""));
- CHECK(Util::ends_with("x", "x"));
- CHECK(Util::ends_with("xy", ""));
- CHECK(Util::ends_with("xy", "y"));
- CHECK(Util::ends_with("xy", "xy"));
- CHECK(Util::ends_with("xyz", ""));
- CHECK(Util::ends_with("xyz", "z"));
- CHECK(Util::ends_with("xyz", "yz"));
- CHECK(Util::ends_with("xyz", "xyz"));
-
- CHECK_FALSE(Util::ends_with("", "x"));
- CHECK_FALSE(Util::ends_with("x", "y"));
- CHECK_FALSE(Util::ends_with("x", "xy"));
-}
-
TEST_CASE("Util::ensure_dir_exists")
{
TestContext test_context;
// Util::split_into_strings and Util::split_into_views are tested implicitly in
// test_util_Tokenizer.cpp.
-TEST_CASE("Util::starts_with")
-{
- // starts_with(const char*, string_view)
- CHECK(Util::starts_with("", ""));
- CHECK(Util::starts_with("x", ""));
- CHECK(Util::starts_with("x", "x"));
- CHECK(Util::starts_with("xy", ""));
- CHECK(Util::starts_with("xy", "x"));
- CHECK(Util::starts_with("xy", "xy"));
- CHECK(Util::starts_with("xyz", ""));
- CHECK(Util::starts_with("xyz", "x"));
- CHECK(Util::starts_with("xyz", "xy"));
- CHECK(Util::starts_with("xyz", "xyz"));
-
- CHECK_FALSE(Util::starts_with("", "x"));
- CHECK_FALSE(Util::starts_with("x", "y"));
- CHECK_FALSE(Util::starts_with("x", "xy"));
-
- // starts_with(string_view, string_view)
- CHECK(Util::starts_with(std::string(""), ""));
- CHECK(Util::starts_with(std::string("x"), ""));
- CHECK(Util::starts_with(std::string("x"), "x"));
- CHECK(Util::starts_with(std::string("xy"), ""));
- CHECK(Util::starts_with(std::string("xy"), "x"));
- CHECK(Util::starts_with(std::string("xy"), "xy"));
- CHECK(Util::starts_with(std::string("xyz"), ""));
- CHECK(Util::starts_with(std::string("xyz"), "x"));
- CHECK(Util::starts_with(std::string("xyz"), "xy"));
- CHECK(Util::starts_with(std::string("xyz"), "xyz"));
-
- CHECK_FALSE(Util::starts_with(std::string(""), "x"));
- CHECK_FALSE(Util::starts_with(std::string("x"), "y"));
- CHECK_FALSE(Util::starts_with(std::string("x"), "xy"));
-}
-
TEST_CASE("Util::to_lowercase")
{
CHECK(Util::to_lowercase("") == "");
TEST_SUITE_BEGIN("util");
+TEST_CASE("util::ends_with")
+{
+ CHECK(util::ends_with("", ""));
+ CHECK(util::ends_with("x", ""));
+ CHECK(util::ends_with("x", "x"));
+ CHECK(util::ends_with("xy", ""));
+ CHECK(util::ends_with("xy", "y"));
+ CHECK(util::ends_with("xy", "xy"));
+ CHECK(util::ends_with("xyz", ""));
+ CHECK(util::ends_with("xyz", "z"));
+ CHECK(util::ends_with("xyz", "yz"));
+ CHECK(util::ends_with("xyz", "xyz"));
+
+ CHECK_FALSE(util::ends_with("", "x"));
+ CHECK_FALSE(util::ends_with("x", "y"));
+ CHECK_FALSE(util::ends_with("x", "xy"));
+}
+
TEST_CASE("util::parse_umask")
{
CHECK(util::parse_umask("1") == 01u);
CHECK(split_once("x y", ' ') == make_pair("x", "y"));
}
+TEST_CASE("util::starts_with")
+{
+ // starts_with(const char*, string_view)
+ CHECK(util::starts_with("", ""));
+ CHECK(util::starts_with("x", ""));
+ CHECK(util::starts_with("x", "x"));
+ CHECK(util::starts_with("xy", ""));
+ CHECK(util::starts_with("xy", "x"));
+ CHECK(util::starts_with("xy", "xy"));
+ CHECK(util::starts_with("xyz", ""));
+ CHECK(util::starts_with("xyz", "x"));
+ CHECK(util::starts_with("xyz", "xy"));
+ CHECK(util::starts_with("xyz", "xyz"));
+
+ CHECK_FALSE(util::starts_with("", "x"));
+ CHECK_FALSE(util::starts_with("x", "y"));
+ CHECK_FALSE(util::starts_with("x", "xy"));
+
+ // starts_with(string_view, string_view)
+ CHECK(util::starts_with(std::string(""), ""));
+ CHECK(util::starts_with(std::string("x"), ""));
+ CHECK(util::starts_with(std::string("x"), "x"));
+ CHECK(util::starts_with(std::string("xy"), ""));
+ CHECK(util::starts_with(std::string("xy"), "x"));
+ CHECK(util::starts_with(std::string("xy"), "xy"));
+ CHECK(util::starts_with(std::string("xyz"), ""));
+ CHECK(util::starts_with(std::string("xyz"), "x"));
+ CHECK(util::starts_with(std::string("xyz"), "xy"));
+ CHECK(util::starts_with(std::string("xyz"), "xyz"));
+
+ CHECK_FALSE(util::starts_with(std::string(""), "x"));
+ CHECK_FALSE(util::starts_with(std::string("x"), "y"));
+ CHECK_FALSE(util::starts_with(std::string("x"), "xy"));
+}
+
TEST_CASE("util::strip_whitespace")
{
CHECK(util::strip_whitespace("") == "");