This is an automated email from the ASF dual-hosted git repository.
twice pushed a commit to branch unstable
in repository https://gitbox.apache.org/repos/asf/kvrocks.git
The following commit(s) were added to refs/heads/unstable by this push:
new 009a1bd80 chore(util): add prefix/suffix string util functions (#2911)
009a1bd80 is described below
commit 009a1bd80bdde88c091a69aca1539be0f58d2658
Author: Twice <[email protected]>
AuthorDate: Wed Apr 30 14:50:52 2025 +0800
chore(util): add prefix/suffix string util functions (#2911)
Signed-off-by: PragmaTwice <[email protected]>
---
src/commands/cmd_server.cc | 20 ++++++++++----------
src/common/string_util.cc | 15 ++++++++++++---
src/common/string_util.h | 6 +++++-
src/config/config.cc | 14 +++++++-------
tests/cppunit/string_util_test.cc | 18 +++++++++++++-----
5 files changed, 47 insertions(+), 26 deletions(-)
diff --git a/src/commands/cmd_server.cc b/src/commands/cmd_server.cc
index bc248e0f7..d1359087c 100644
--- a/src/commands/cmd_server.cc
+++ b/src/commands/cmd_server.cc
@@ -428,31 +428,31 @@ class CommandClient : public Commander {
while (i < args.size()) {
bool more_args = i < args.size();
- if (!strcasecmp(args[i].c_str(), "addr") && more_args) {
+ if (util::EqualICase(args[i], "addr") && more_args) {
addr_ = args[i + 1];
- } else if (!strcasecmp(args[i].c_str(), "id") && more_args) {
+ } else if (util::EqualICase(args[i], "id") && more_args) {
auto parse_result = ParseInt<uint64_t>(args[i + 1], 10);
if (!parse_result) {
return {Status::RedisParseErr, errValueNotInteger};
}
id_ = *parse_result;
- } else if (!strcasecmp(args[i].c_str(), "skipme") && more_args) {
- if (!strcasecmp(args[i + 1].c_str(), "yes")) {
+ } else if (util::EqualICase(args[i], "skipme") && more_args) {
+ if (util::EqualICase(args[i + 1], "yes")) {
skipme_ = true;
- } else if (!strcasecmp(args[i + 1].c_str(), "no")) {
+ } else if (util::EqualICase(args[i + 1], "no")) {
skipme_ = false;
} else {
return {Status::RedisParseErr, errInvalidSyntax};
}
- } else if (!strcasecmp(args[i].c_str(), "type") && more_args) {
- if (!strcasecmp(args[i + 1].c_str(), "normal")) {
+ } else if (util::EqualICase(args[i], "type") && more_args) {
+ if (util::EqualICase(args[i + 1], "normal")) {
kill_type_ |= kTypeNormal;
- } else if (!strcasecmp(args[i + 1].c_str(), "pubsub")) {
+ } else if (util::EqualICase(args[i + 1], "pubsub")) {
kill_type_ |= kTypePubsub;
- } else if (!strcasecmp(args[i + 1].c_str(), "master")) {
+ } else if (util::EqualICase(args[i + 1], "master")) {
kill_type_ |= kTypeMaster;
- } else if (!strcasecmp(args[i + 1].c_str(), "replica") ||
!strcasecmp(args[i + 1].c_str(), "slave")) {
+ } else if (util::EqualICase(args[i + 1], "replica") ||
util::EqualICase(args[i + 1], "slave")) {
kill_type_ |= kTypeSlave;
} else {
return {Status::RedisParseErr, errInvalidSyntax};
diff --git a/src/common/string_util.cc b/src/common/string_util.cc
index f05e286fc..2342db323 100644
--- a/src/common/string_util.cc
+++ b/src/common/string_util.cc
@@ -101,9 +101,18 @@ std::vector<std::string> Split2KV(const std::string &in,
const std::string &deli
return out;
}
-bool HasPrefix(const std::string &str, const std::string &prefix) {
- if (str.empty() || prefix.empty()) return false;
- return !strncasecmp(str.data(), prefix.data(), prefix.size());
+bool StartsWith(std::string_view str, std::string_view prefix) {
+ return str.size() >= prefix.size() && str.compare(0, prefix.size(), prefix)
== 0;
+}
+bool StartsWithICase(std::string_view str, std::string_view prefix) {
+ return str.size() >= prefix.size() && EqualICase(str.substr(0,
prefix.size()), prefix);
+}
+
+bool EndsWith(std::string_view str, std::string_view suffix) {
+ return str.size() >= suffix.size() && str.compare(str.size() -
suffix.size(), suffix.size(), suffix) == 0;
+}
+bool EndsWithICase(std::string_view str, std::string_view suffix) {
+ return str.size() >= suffix.size() && EqualICase(str.substr(str.size() -
suffix.size()), suffix);
}
Status ValidateGlob(std::string_view glob) {
diff --git a/src/common/string_util.h b/src/common/string_util.h
index 32c0f4b8e..20eca54f3 100644
--- a/src/common/string_util.h
+++ b/src/common/string_util.h
@@ -40,7 +40,11 @@ std::string BytesToHuman(uint64_t n);
std::string Trim(std::string in, std::string_view chars);
std::vector<std::string> Split(std::string_view in, std::string_view delim);
std::vector<std::string> Split2KV(const std::string &in, const std::string
&delim);
-bool HasPrefix(const std::string &str, const std::string &prefix);
+
+bool StartsWith(std::string_view str, std::string_view prefix);
+bool EndsWith(std::string_view str, std::string_view suffix);
+bool StartsWithICase(std::string_view str, std::string_view prefix);
+bool EndsWithICase(std::string_view str, std::string_view suffix);
template <typename Iter>
Iter FindICase(Iter begin, Iter end, std::string_view expected) {
diff --git a/src/config/config.cc b/src/config/config.cc
index 1ae6e0128..d942eef9c 100644
--- a/src/config/config.cc
+++ b/src/config/config.cc
@@ -94,8 +94,9 @@ const std::vector<ConfigEnum<MigrationType>>
migration_types{{"redis-command", M
{"raw-key-value",
MigrationType::kRawKeyValue}};
std::string TrimRocksDbPrefix(std::string s) {
- if (strncasecmp(s.data(), "rocksdb.", 8) != 0) return s;
- return s.substr(8, s.size() - 8);
+ constexpr std::string_view prefix = "rocksdb.";
+ if (!util::StartsWithICase(s, prefix)) return s;
+ return s.substr(prefix.size());
}
Status SetRocksdbCompression(Server *srv, const rocksdb::CompressionType
compression,
@@ -793,12 +794,11 @@ void Config::ClearMaster() {
Status Config::parseConfigFromPair(const std::pair<std::string, std::string>
&input, int line_number) {
std::string field_key = util::ToLower(input.first);
- constexpr const char ns_str[] = "namespace.";
- size_t ns_str_size = sizeof(ns_str) - 1;
- if (strncasecmp(input.first.data(), ns_str, ns_str_size) == 0) {
+ constexpr std::string_view ns_str = "namespace.";
+ if (util::StartsWithICase(input.first, ns_str)) {
// namespace should keep key case-sensitive
field_key = input.first;
- load_tokens[input.second] = input.first.substr(ns_str_size);
+ load_tokens[input.second] = input.first.substr(ns_str.size());
return Status::OK();
}
@@ -997,7 +997,7 @@ Status Config::Rewrite(const std::map<std::string,
std::string> &tokens) {
continue;
}
auto kv = std::move(*parsed);
- if (util::HasPrefix(kv.first, namespace_prefix)) {
+ if (util::StartsWith(kv.first, namespace_prefix)) {
// Ignore namespace fields here since we would always rewrite them
continue;
}
diff --git a/tests/cppunit/string_util_test.cc
b/tests/cppunit/string_util_test.cc
index d31a99615..02c131393 100644
--- a/tests/cppunit/string_util_test.cc
+++ b/tests/cppunit/string_util_test.cc
@@ -78,11 +78,19 @@ TEST(StringUtil, TokenizeRedisProtocol) {
ASSERT_EQ(expected, array);
}
-TEST(StringUtil, HasPrefix) {
- ASSERT_TRUE(util::HasPrefix("has_prefix_is_true", "has_prefix"));
- ASSERT_FALSE(util::HasPrefix("has_prefix_is_false", "_has_prefix"));
- ASSERT_TRUE(util::HasPrefix("has_prefix", "has_prefix"));
- ASSERT_FALSE(util::HasPrefix("has", "has_prefix"));
+TEST(StringUtil, StartsEndsWith) {
+ ASSERT_TRUE(util::StartsWith("has_prefix_is_true", "has_prefix"));
+ ASSERT_FALSE(util::StartsWith("has_prefix_is_false", "_has_prefix"));
+ ASSERT_TRUE(util::StartsWith("has_prefix", "has_prefix"));
+ ASSERT_FALSE(util::StartsWith("has", "has_prefix"));
+ ASSERT_TRUE(util::EndsWith("has_suffix_is_true", "_is_true"));
+ ASSERT_FALSE(util::EndsWith("has_suffix_is_false", "has_suffix"));
+ ASSERT_TRUE(util::EndsWith("has_suffix", "has_suffix"));
+ ASSERT_FALSE(util::EndsWith("has", "has_suffix"));
+ ASSERT_TRUE(util::StartsWithICase("has_prefix_is_true", "has_PREfix"));
+ ASSERT_FALSE(util::StartsWithICase("has_prefix_is_false", "_has_prefix"));
+ ASSERT_TRUE(util::EndsWithICase("has_suffix_IS_true", "_is_true"));
+ ASSERT_FALSE(util::EndsWithICase("has_suffix_is_false", "has_suffix"));
}
TEST(StringUtil, ValidateGlob) {