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

Reply via email to