From ec94a399f0e9d112e1ba074bcdfecdcc35bd0e73 Mon Sep 17 00:00:00 2001 From: Joel Rosdahl Date: Tue, 13 Jul 2021 10:50:12 +0200 Subject: [PATCH] Move starts_with and ends_with to util --- src/Args.cpp | 4 +- src/CacheFile.cpp | 9 ++-- src/Config.cpp | 4 +- src/Util.cpp | 6 +-- src/Util.hpp | 23 ---------- src/argprocessing.cpp | 63 ++++++++++++++------------- src/ccache.cpp | 43 +++++++++--------- src/cleanup.cpp | 4 +- src/compress.cpp | 3 +- src/hashutil.cpp | 4 +- src/storage/secondary/FileStorage.cpp | 2 +- src/storage/secondary/HttpStorage.cpp | 2 +- src/util/string_utils.hpp | 24 ++++++++++ unittest/test_Util.cpp | 53 ---------------------- unittest/test_util_string_utils.cpp | 53 ++++++++++++++++++++++ 15 files changed, 153 insertions(+), 144 deletions(-) diff --git a/src/Args.cpp b/src/Args.cpp index a1913299f..8f5bc5fc4 100644 --- a/src/Args.cpp +++ b/src/Args.cpp @@ -20,6 +20,8 @@ #include "Util.hpp" +#include + using nonstd::nullopt; using nonstd::optional; using nonstd::string_view; @@ -170,7 +172,7 @@ Args::erase_with_prefix(string_view prefix) 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()); } diff --git a/src/CacheFile.cpp b/src/CacheFile.cpp index 68072df70..cee49b394 100644 --- a/src/CacheFile.cpp +++ b/src/CacheFile.cpp @@ -1,4 +1,4 @@ -// 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. // @@ -20,7 +20,8 @@ #include "Manifest.hpp" #include "Result.hpp" -#include "Util.hpp" + +#include const Stat& CacheFile::lstat() const @@ -35,9 +36,9 @@ 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; diff --git a/src/Config.cpp b/src/Config.cpp index 121f9ab9d..110c04995 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -586,7 +586,7 @@ Config::update_from_environment() 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('='); @@ -596,7 +596,7 @@ Config::update_from_environment() 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); } diff --git a/src/Util.cpp b/src/Util.cpp index 0d546710c..36a6bf4e3 100644 --- a/src/Util.cpp +++ b/src/Util.cpp @@ -179,7 +179,7 @@ rewrite_stderr_to_absolute_paths(string_view text) // In file included from XX:1: // XX: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()); } @@ -870,7 +870,7 @@ make_relative_path(const std::string& base_dir, 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); } @@ -1112,7 +1112,7 @@ parse_unsigned(const std::string& value, 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 { diff --git a/src/Util.hpp b/src/Util.hpp index a682f1daa..853891d8d 100644 --- a/src/Util.hpp +++ b/src/Util.hpp @@ -129,13 +129,6 @@ bool create_dir(nonstd::string_view dir); // 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); @@ -421,22 +414,6 @@ std::vector split_into_strings( 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); diff --git a/src/argprocessing.cpp b/src/argprocessing.cpp index 62c4716db..2d5c6915d 100644 --- a/src/argprocessing.cpp +++ b/src/argprocessing.cpp @@ -27,6 +27,7 @@ #include "language.hpp" #include +#include #ifdef HAVE_UNISTD_H # include @@ -164,7 +165,7 @@ process_profiling_option(Context& ctx, const std::string& arg) 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; @@ -174,8 +175,8 @@ process_profiling_option(Context& ctx, const std::string& arg) // 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" @@ -185,10 +186,10 @@ process_profiling_option(Context& ctx, const std::string& arg) 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 { @@ -255,7 +256,7 @@ process_arg(Context& ctx, } // 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] == '-') { @@ -297,8 +298,8 @@ process_arg(Context& ctx, } // 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; } @@ -310,7 +311,7 @@ process_arg(Context& ctx, } // -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; @@ -415,7 +416,7 @@ process_arg(Context& ctx, 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 @@ -458,15 +459,15 @@ process_arg(Context& ctx, } // 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]); @@ -475,17 +476,17 @@ process_arg(Context& ctx, // 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-) 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; } @@ -516,7 +517,7 @@ process_arg(Context& ctx, 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; @@ -544,7 +545,7 @@ process_arg(Context& ctx, 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) { @@ -598,8 +599,8 @@ process_arg(Context& ctx, 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. @@ -609,13 +610,13 @@ process_arg(Context& ctx, 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); @@ -656,12 +657,12 @@ process_arg(Context& ctx, 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; @@ -669,7 +670,7 @@ process_arg(Context& ctx, 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; @@ -677,13 +678,13 @@ process_arg(Context& ctx, 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') @@ -709,7 +710,7 @@ process_arg(Context& ctx, } // 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; } diff --git a/src/ccache.cpp b/src/ccache.cpp index 26d17fc77..c9f9253b8 100644 --- a/src/ccache.cpp +++ b/src/ccache.cpp @@ -58,6 +58,7 @@ #include #include #include +#include #include "third_party/fmt/core.h" #include "third_party/nonstd/optional.hpp" @@ -368,7 +369,7 @@ do_remember_include_file(Context& ctx, } // Canonicalize path for comparison; Clang uses ./header.h. - if (Util::starts_with(path, "./")) { + if (util::starts_with(path, "./")) { path.erase(0, 2); } @@ -566,14 +567,14 @@ process_preprocessed_file(Context& ctx, // 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); @@ -583,7 +584,7 @@ process_preprocessed_file(Context& ctx, 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); @@ -631,8 +632,8 @@ process_preprocessed_file(Context& ctx, 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: // @@ -1180,7 +1181,7 @@ hash_compiler(const Context& ctx, 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) { @@ -1328,7 +1329,7 @@ hash_common_info(const Context& ctx, 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()); } } @@ -1448,7 +1449,7 @@ option_should_be_ignored(const std::string& arg, 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))); }); } @@ -1494,12 +1495,12 @@ calculate_result_and_manifest_key(Context& ctx, 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; } @@ -1507,17 +1508,17 @@ calculate_result_and_manifest_key(Context& ctx, // 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; @@ -1543,17 +1544,17 @@ calculate_result_and_manifest_key(Context& ctx, // 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); @@ -1569,8 +1570,8 @@ calculate_result_and_manifest_key(Context& ctx, } } - 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; @@ -1595,7 +1596,7 @@ calculate_result_and_manifest_key(Context& ctx, } } - 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"); diff --git a/src/cleanup.cpp b/src/cleanup.cpp index 20e6060b5..286a08d46 100644 --- a/src/cleanup.cpp +++ b/src/cleanup.cpp @@ -26,6 +26,8 @@ #include "Statistics.hpp" #include "Util.hpp" +#include + #ifdef INODE_CACHE_SUPPORTED # include "InodeCache.hpp" #endif @@ -143,7 +145,7 @@ clean_up_dir(const std::string& subdir, 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 diff --git a/src/compress.cpp b/src/compress.cpp index 4a26de784..1aa27ad06 100644 --- a/src/compress.cpp +++ b/src/compress.cpp @@ -33,6 +33,7 @@ #include "fmtmacros.hpp" #include +#include #include "third_party/fmt/core.h" @@ -316,7 +317,7 @@ compress_recompress(Context& ctx, 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(); diff --git a/src/hashutil.cpp b/src/hashutil.cpp index d25b1c240..c21677c4d 100644 --- a/src/hashutil.cpp +++ b/src/hashutil.cpp @@ -380,10 +380,10 @@ hash_command_output(Hash& hash, // 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)); diff --git a/src/storage/secondary/FileStorage.cpp b/src/storage/secondary/FileStorage.cpp index ab6b2fd73..9eb168fa4 100644 --- a/src/storage/secondary/FileStorage.cpp +++ b/src/storage/secondary/FileStorage.cpp @@ -38,7 +38,7 @@ parse_url(const Url& url) { 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); } diff --git a/src/storage/secondary/HttpStorage.cpp b/src/storage/secondary/HttpStorage.cpp index 1928d5916..a9cb39bc3 100644 --- a/src/storage/secondary/HttpStorage.cpp +++ b/src/storage/secondary/HttpStorage.cpp @@ -99,7 +99,7 @@ get_host_header_value(const Url& url) // 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); } diff --git a/src/util/string_utils.hpp b/src/util/string_utils.hpp index c55fc1239..e6aa19b20 100644 --- a/src/util/string_utils.hpp +++ b/src/util/string_utils.hpp @@ -24,11 +24,19 @@ #include // for mode_t +#include #include #include 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 parse_umask(const std::string& value); @@ -43,6 +51,22 @@ percent_decode(nonstd::string_view string); std::pair> 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); diff --git a/unittest/test_Util.cpp b/unittest/test_Util.cpp index cada79249..f34bcbb29 100644 --- a/unittest/test_Util.cpp +++ b/unittest/test_Util.cpp @@ -177,24 +177,6 @@ TEST_CASE("Util::strip_ansi_csi_seqs") 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; @@ -858,41 +840,6 @@ TEST_CASE("Util::same_program_name") // 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("") == ""); diff --git a/unittest/test_util_string_utils.cpp b/unittest/test_util_string_utils.cpp index 17b8cd06b..f4bef5b49 100644 --- a/unittest/test_util_string_utils.cpp +++ b/unittest/test_util_string_utils.cpp @@ -30,6 +30,24 @@ operator==( 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); @@ -83,6 +101,41 @@ TEST_CASE("util::split_once") 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("") == ""); -- 2.47.3