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

wwbmmm 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 e6fd4c5a Fix JSON parsing offsets at EOF (#3635)
e6fd4c5a is described below

commit e6fd4c5a803bfe4c803456081f064632cc883539
Author: Bright Chen <[email protected]>
AuthorDate: Thu Oct 22 21:28:56 2026 +0800

    Fix JSON parsing offsets at EOF (#3635)
    
    * Fix JSON parsing offsets at EOF
    
    * Opt
    
    * Fix ut
---
 src/butil/third_party/rapidjson/reader.h |  7 +--
 test/brpc_protobuf_json_unittest.cpp     | 96 ++++++++++++++++++++++++++++++++
 2 files changed, 99 insertions(+), 4 deletions(-)

diff --git a/src/butil/third_party/rapidjson/reader.h 
b/src/butil/third_party/rapidjson/reader.h
index 26e53c2a..938a0827 100644
--- a/src/butil/third_party/rapidjson/reader.h
+++ b/src/butil/third_party/rapidjson/reader.h
@@ -1539,10 +1539,9 @@ private:
             if ((parseFlags & kParseStopWhenDoneFlag) && state == 
IterativeParsingFinishState) {
                 // wwb: Update parseResult_.Offset() when 
kParseStopWhenDoneFlag
                 // is set which means the user needs to know where to resume
-                // parsing in next calls to JsonToProtoMessage()
-                if (is.Peek() != '\0') {
-                    SetParseError(kParseErrorNone, is.Tell());
-                }
+                // parsing in next calls to JsonToProtoMessage(), including
+                // when the root ends exactly at EOF.
+                SetParseError(kParseErrorNone, is.Tell());
                 break;
             }
             SkipWhitespace(is);
diff --git a/test/brpc_protobuf_json_unittest.cpp 
b/test/brpc_protobuf_json_unittest.cpp
index eeadc4f7..e38f3b9c 100644
--- a/test/brpc_protobuf_json_unittest.cpp
+++ b/test/brpc_protobuf_json_unittest.cpp
@@ -19,6 +19,7 @@
 #include <gtest/gtest.h>
 #include <iostream>
 #include <fstream>
+#include <limits>
 #include <string>
 #include <google/protobuf/text_format.h>
 #include "butil/iobuf.h"
@@ -1689,6 +1690,101 @@ TEST_F(ProtobufJsonTest, string_to_int64) {
     ASSERT_EQ(person.data(), 1234567);
 }
 
+TEST_F(ProtobufJsonTest, parse_json_offset_at_eof) {
+    const struct {
+        const char* json;
+        size_t expected_offset;
+        bool array_to_single_repeated;
+    } cases[] = {
+        {"{}", 2, false},
+        {" \t{}", 4, false},
+        {R"({"person":[]})", 13, false},
+        {"[]", 2, true},
+        {"{} \n", 2, false},
+        {"{}{}", 2, false},
+    };
+
+    for (const auto& test_case : cases) {
+        SCOPED_TRACE(test_case.json);
+        json2pb::Json2PbOptions options;
+        options.allow_remaining_bytes_after_parsing = true;
+        options.array_to_single_repeated = test_case.array_to_single_repeated;
+        AddressBook message;
+        std::string error;
+        size_t offset = 0;
+        ASSERT_TRUE(json2pb::JsonToProtoMessage(
+            test_case.json, &message, options, &error, &offset)) << error;
+        EXPECT_EQ(test_case.expected_offset, offset);
+        EXPECT_TRUE(error.empty());
+
+        butil::IOBuf buf;
+        buf.append(test_case.json);
+        butil::IOBufAsZeroCopyInputStream stream(buf);
+        message.Clear();
+        offset = 0;
+        ASSERT_TRUE(json2pb::JsonToProtoMessage(
+            &stream, &message, options, &error, &offset)) << error;
+        EXPECT_EQ(test_case.expected_offset, offset);
+        EXPECT_TRUE(error.empty());
+    }
+}
+
+TEST_F(ProtobufJsonTest, parse_multiple_json_at_eof) {
+    const std::string jsons[] = {
+        R"({"message":"first"})",
+        R"({"message":"second"})",
+    };
+    const char* expected_messages[] = {"first", "second"};
+    std::string remaining = jsons[0] + jsons[1];
+    butil::IOBuf buf;
+    buf.append(remaining);
+    butil::IOBufAsZeroCopyInputStream stream(buf);
+    json2pb::ZeroCopyStreamReader reader(&stream);
+    json2pb::Json2PbOptions options;
+    options.allow_remaining_bytes_after_parsing = true;
+    test::EchoRequest message;
+    std::string error;
+    size_t string_offset = 0;
+    size_t stream_offset = 0;
+    size_t total_offset = 0;
+
+    for (size_t i = 0; i < 2; ++i) {
+        SCOPED_TRACE(i);
+        message.Clear();
+        ASSERT_TRUE(json2pb::JsonToProtoMessage(
+            remaining, &message, options, &error, &string_offset)) << error;
+        EXPECT_EQ(expected_messages[i], message.message());
+        EXPECT_EQ(jsons[i].size(), string_offset);
+        remaining = remaining.substr(string_offset);
+
+        message.Clear();
+        ASSERT_TRUE(json2pb::JsonToProtoMessage(
+            &reader, &message, options, &error, &stream_offset)) << error;
+        EXPECT_EQ(expected_messages[i], message.message());
+        total_offset += jsons[i].size();
+        EXPECT_EQ(total_offset, stream_offset);
+        EXPECT_EQ(total_offset, reader.Tell());
+    }
+    ASSERT_TRUE(remaining.empty());
+    // Seed outputs to verify that EOF parsing overwrites them.
+    message.Clear();
+    error = "previous error";
+    string_offset = std::numeric_limits<size_t>::max();
+    EXPECT_FALSE(json2pb::JsonToProtoMessage(
+        remaining, &message, options, &error, &string_offset));
+    EXPECT_TRUE(error.empty());
+    EXPECT_EQ(0ul, string_offset);
+
+    message.Clear();
+    error = "previous error";
+    stream_offset = std::numeric_limits<size_t>::max();
+    EXPECT_FALSE(json2pb::JsonToProtoMessage(
+        &reader, &message, options, &error, &stream_offset));
+    EXPECT_TRUE(error.empty());
+    EXPECT_EQ(total_offset, stream_offset);
+    EXPECT_EQ(total_offset, reader.Tell());
+}
+
 TEST_F(ProtobufJsonTest, parse_multiple_json) {
     const int COUNT = 4;
     std::vector<std::string> expectedNames = { "tom", "bob", "jerry", "lucy" };


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

Reply via email to