This is an automated email from the ASF dual-hosted git repository.

chenBright pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brpc.git


The following commit(s) were added to refs/heads/master by this push:
     new d0f3f933 Optimize Redis command formatting buffer handling (#3609)
d0f3f933 is described below

commit d0f3f933cc051af3e760f169a7e95a515d9faf29
Author: nkysg <[email protected]>
AuthorDate: Sun Oct 18 19:09:30 2026 +0800

    Optimize Redis command formatting buffer handling (#3609)
    
    * Optimize Redis command formatting buffer handling
    
    * Handle Redis command formatting append failures
    
    * remove unused function
---
 src/brpc/redis_command.cpp   | 70 +++++++++++++++++++++++++++++++-------------
 test/brpc_redis_unittest.cpp | 25 ++++++++++++++++
 2 files changed, 74 insertions(+), 21 deletions(-)

diff --git a/src/brpc/redis_command.cpp b/src/brpc/redis_command.cpp
index 2513d160..5f30d049 100644
--- a/src/brpc/redis_command.cpp
+++ b/src/brpc/redis_command.cpp
@@ -68,7 +68,7 @@ inline size_t AppendDecimal(char* outbuf, unsigned long d) {
 // short or does not have many %. In a 100K-time call to formating of
 // "GET key1", the time spent on RedisRequest.AddCommand() are ~700ns
 // vs. ~400ns while using snprintf() vs. AppendDecimal() respectively.
-inline void AppendHeader(std::string& buf, char fc, unsigned long value) {
+inline void AppendHeader(butil::IOBuf& buf, char fc, unsigned long value) {
     char header[32];
     header[0] = fc;
     size_t len = AppendDecimal(header + 1, value);
@@ -76,21 +76,31 @@ inline void AppendHeader(std::string& buf, char fc, 
unsigned long value) {
     header[len + 2] = '\n';
     buf.append(header, len + 3);
 }
-inline void AppendHeader(butil::IOBuf& buf, char fc, unsigned long value) {
+
+inline int AppendHeader(butil::IOBufAppender& buf, char fc,
+                        unsigned long value) {
     char header[32];
     header[0] = fc;
     size_t len = AppendDecimal(header + 1, value);
     header[len + 1] = '\r';
     header[len + 2] = '\n';
-    buf.append(header, len + 3);
+    return buf.append(header, len + 3);
 }
 
-static void FlushComponent(std::string* out, std::string* compbuf, int* ncomp) 
{
-    AppendHeader(*out, '$', compbuf->size());
-    out->append(*compbuf);
-    out->append("\r\n", 2);
+static int FlushComponent(butil::IOBufAppender* out, std::string* compbuf,
+                          int* ncomp) {
+    if (AppendHeader(*out, '$', compbuf->size()) != 0 ||
+        out->append(compbuf->data(), compbuf->size()) != 0 ||
+        out->append("\r\n", 2) != 0) {
+        return -1;
+    }
     compbuf->clear();
     ++*ncomp;
+    return 0;
+}
+
+inline butil::Status AppendFailed() {
+    return butil::Status(ENOMEM, "Fail to append Redis command");
 }
 
 // Support hiredis-style format, namely everything is same with printf except
@@ -105,8 +115,7 @@ RedisCommandFormatV(butil::IOBuf* outbuf, const char* fmt, 
va_list ap) {
         return butil::Status(EINVAL, "Param[outbuf] or [fmt] is NULL");
     }
     const size_t fmt_len = strlen(fmt);
-    std::string nocount_buf;
-    nocount_buf.reserve(fmt_len * 3 / 2 + 16);
+    butil::IOBufAppender nocount_buf;
     std::string compbuf;  // A component
     compbuf.reserve(fmt_len + 16);
     const char* c = fmt;
@@ -123,7 +132,9 @@ RedisCommandFormatV(butil::IOBuf* outbuf, const char* fmt, 
va_list ap) {
                 if (quote_char) {
                     compbuf.push_back(*c);
                 } else if (!compbuf.empty() || format_arg_in_comp) {
-                    FlushComponent(&nocount_buf, &compbuf, &ncomponent);
+                    if (FlushComponent(&nocount_buf, &compbuf, &ncomponent) != 
0) {
+                        return AppendFailed();
+                    }
                     format_arg_in_comp = false;
                 }
             } else if (*c == '"' || *c == '\'') {  // Check quotation.
@@ -131,7 +142,9 @@ RedisCommandFormatV(butil::IOBuf* outbuf, const char* fmt, 
va_list ap) {
                     quote_char = *c;
                     quote_pos = c;
                     if (!compbuf.empty() || format_arg_in_comp) {
-                        FlushComponent(&nocount_buf, &compbuf, &ncomponent);
+                        if (FlushComponent(&nocount_buf, &compbuf, 
&ncomponent) != 0) {
+                            return AppendFailed();
+                        }
                         format_arg_in_comp = false;
                     }
                 } else if (quote_char == *c) {
@@ -144,7 +157,9 @@ RedisCommandFormatV(butil::IOBuf* outbuf, const char* fmt, 
va_list ap) {
                         compbuf.push_back(*c);
                     } else { // end quote
                         quote_char = 0;
-                        FlushComponent(&nocount_buf, &compbuf, &ncomponent);
+                        if (FlushComponent(&nocount_buf, &compbuf, 
&ncomponent) != 0) {
+                            return AppendFailed();
+                        }
                         format_arg_in_comp = false;
                     }
                 } else {
@@ -323,7 +338,9 @@ RedisCommandFormatV(butil::IOBuf* outbuf, const char* fmt, 
va_list ap) {
     }
     
     if (!compbuf.empty() || format_arg_in_comp) {
-        FlushComponent(&nocount_buf, &compbuf, &ncomponent);
+        if (FlushComponent(&nocount_buf, &compbuf, &ncomponent) != 0) {
+            return AppendFailed();
+        }
         format_arg_in_comp = false;
     }
 
@@ -332,7 +349,9 @@ RedisCommandFormatV(butil::IOBuf* outbuf, const char* fmt, 
va_list ap) {
         "formatting of conversion specifiers)";
     
     AppendHeader(*outbuf, '*', ncomponent);
-    outbuf->append(nocount_buf);
+    if (ncomponent != 0) {
+        outbuf->append(nocount_buf.buf().movable());
+    }
     return butil::Status::OK();
 }
 
@@ -350,8 +369,7 @@ RedisCommandNoFormat(butil::IOBuf* outbuf, const 
butil::StringPiece& cmd) {
         return butil::Status(EINVAL, "Param[outbuf] or [cmd] is NULL");
     }
     const size_t cmd_len = cmd.size();
-    std::string nocount_buf;
-    nocount_buf.reserve(cmd_len * 3 / 2 + 16);
+    butil::IOBufAppender nocount_buf;
     std::string compbuf;  // A component
     compbuf.reserve(cmd_len + 16);
     int ncomponent = 0;
@@ -362,14 +380,18 @@ RedisCommandNoFormat(butil::IOBuf* outbuf, const 
butil::StringPiece& cmd) {
             if (quote_char) {
                 compbuf.push_back(*c);
             } else if (!compbuf.empty()) {
-                FlushComponent(&nocount_buf, &compbuf, &ncomponent);
+                if (FlushComponent(&nocount_buf, &compbuf, &ncomponent) != 0) {
+                    return AppendFailed();
+                }
             }
         } else if (*c == '"' || *c == '\'') {  // Check quotation.
             if (!quote_char) {  // begin quote
                 quote_char = *c;
                 quote_pos = c;
                 if (!compbuf.empty()) {
-                    FlushComponent(&nocount_buf, &compbuf, &ncomponent);
+                    if (FlushComponent(&nocount_buf, &compbuf, &ncomponent) != 
0) {
+                        return AppendFailed();
+                    }
                 }
             } else if (quote_char == *c) {
                 const char last_char = (compbuf.empty() ? 0 : compbuf.back());
@@ -381,7 +403,9 @@ RedisCommandNoFormat(butil::IOBuf* outbuf, const 
butil::StringPiece& cmd) {
                     compbuf.push_back(*c);
                 } else { // end quote
                     quote_char = 0;
-                    FlushComponent(&nocount_buf, &compbuf, &ncomponent);
+                    if (FlushComponent(&nocount_buf, &compbuf, &ncomponent) != 
0) {
+                        return AppendFailed();
+                    }
                 }
             } else {
                 compbuf.push_back(*c);
@@ -400,11 +424,15 @@ RedisCommandNoFormat(butil::IOBuf* outbuf, const 
butil::StringPiece& cmd) {
     }
     
     if (!compbuf.empty()) {
-        FlushComponent(&nocount_buf, &compbuf, &ncomponent);
+        if (FlushComponent(&nocount_buf, &compbuf, &ncomponent) != 0) {
+            return AppendFailed();
+        }
     }
 
     AppendHeader(*outbuf, '*', ncomponent);
-    outbuf->append(nocount_buf);
+    if (ncomponent != 0) {
+        outbuf->append(nocount_buf.buf().movable());
+    }
     return butil::Status::OK();
 }
 
diff --git a/test/brpc_redis_unittest.cpp b/test/brpc_redis_unittest.cpp
index 13806681..7e8d0be0 100644
--- a/test/brpc_redis_unittest.cpp
+++ b/test/brpc_redis_unittest.cpp
@@ -164,6 +164,31 @@ TEST(RedisCommandFormatTest, 
vector_components_preserve_binary_payload) {
     EXPECT_EQ(expected, buf.to_string());
 }
 
+TEST(RedisCommandFormatTest, format_preserves_binary_payload) {
+    const std::string value("a\0b", 3);
+    butil::IOBuf buf;
+    ASSERT_TRUE(brpc::RedisCommandFormat(
+                    &buf, "SET key %b", value.data(), value.size())
+                    .ok());
+
+    std::string expected = "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$3\r\n";
+    expected.append(value).append("\r\n");
+    EXPECT_EQ(expected, buf.to_string());
+}
+
+TEST(RedisCommandFormatTest, no_format_encodes_components) {
+    butil::IOBuf buf;
+    ASSERT_TRUE(brpc::RedisCommandNoFormat(&buf, "SET key value").ok());
+    EXPECT_EQ("*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n",
+              buf.to_string());
+}
+
+TEST(RedisCommandFormatTest, empty_format_encodes_empty_array) {
+    butil::IOBuf buf;
+    ASSERT_TRUE(brpc::RedisCommandFormat(&buf, "").ok());
+    EXPECT_EQ("*0\r\n", buf.to_string());
+}
+
 TEST(RedisCommandFormatTest, authenticator_encodes_password_and_database) {
     brpc::policy::RedisAuthenticator authenticator("a b", 3);
     std::string credential;


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to