1
0
Fork 0
mirror of https://github.com/transmission/transmission synced 2024-12-22 15:54:57 +00:00

fix: tr_variant_serde::parse_json() bug fixes (#6901)

* perf: avoid unnecessary copying

* fix: set `tr_variant_serde::end_` in `parse_json()`

* test: `tr_variant_serde::end()`

* fix: compensate for innate read cursor offset of `rapidjson::AutoUTFInputStream`

* fix: stop parsing json after parsing a complete json root

This matches the benc parser's behaviour

* fixup! fix: stop parsing json after parsing a complete json root
This commit is contained in:
Yat Ho 2024-07-17 09:34:13 +08:00 committed by GitHub
parent c21ee87eea
commit fa8be1b981
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 40 additions and 5 deletions

View file

@ -89,7 +89,7 @@ struct json_to_variant_handler : public rapidjson::BaseReaderHandler<>
bool String(Ch const* const str, rapidjson::SizeType const len, bool const copy) bool String(Ch const* const str, rapidjson::SizeType const len, bool const copy)
{ {
*get_leaf() = copy ? tr_variant{ std::string{ str, len } } : tr_variant::unmanaged_string({ str, len }); *get_leaf() = copy ? tr_variant{ std::string_view{ str, len } } : tr_variant::unmanaged_string({ str, len });
return true; return true;
} }
@ -216,7 +216,13 @@ std::optional<tr_variant> tr_variant_serde::parse_json(std::string_view input)
auto ms = rapidjson::MemoryStream{ begin, size }; auto ms = rapidjson::MemoryStream{ begin, size };
auto eis = rapidjson::AutoUTFInputStream<unsigned, rapidjson::MemoryStream>{ ms }; auto eis = rapidjson::AutoUTFInputStream<unsigned, rapidjson::MemoryStream>{ ms };
auto reader = rapidjson::GenericReader<rapidjson::AutoUTF<unsigned>, rapidjson::UTF8<char>>{}; auto reader = rapidjson::GenericReader<rapidjson::AutoUTF<unsigned>, rapidjson::UTF8<char>>{};
reader.Parse(eis, handler); reader.Parse<rapidjson::kParseStopWhenDoneFlag>(eis, handler);
// Due to the nature of how AutoUTFInputStream works, when AutoUTFInputStream
// is used with MemoryStream, the read cursor position is always 1 ahead of
// the current character (unless the end of stream is reached).
auto const pos = eis.Peek() == '\0' ? eis.Tell() : eis.Tell() - 1U;
end_ = begin + pos;
if (!reader.HasParseError()) if (!reader.HasParseError())
{ {
@ -229,13 +235,12 @@ std::optional<tr_variant> tr_variant_serde::parse_json(std::string_view input)
} }
else else
{ {
auto const err_offset = reader.GetErrorOffset();
error_.set( error_.set(
EILSEQ, EILSEQ,
fmt::format( fmt::format(
_("Couldn't parse JSON at position {position} '{text}': {error} ({error_code})"), _("Couldn't parse JSON at position {position} '{text}': {error} ({error_code})"),
fmt::arg("position", err_offset), fmt::arg("position", pos),
fmt::arg("text", std::string_view{ begin + err_offset, std::min(size_t{ 16U }, size - err_offset) }), fmt::arg("text", std::string_view{ begin + pos, std::min(size_t{ 16U }, size - pos) }),
fmt::arg("error", rapidjson::GetParseError_En(err_code)), fmt::arg("error", rapidjson::GetParseError_En(err_code)),
fmt::arg("error_code", static_cast<std::underlying_type_t<decltype(err_code)>>(err_code)))); fmt::arg("error_code", static_cast<std::underlying_type_t<decltype(err_code)>>(err_code))));
} }

View file

@ -535,3 +535,33 @@ TEST_F(VariantTest, variantFromBufFuzz)
(void)json_serde.inplace().parse(buf); (void)json_serde.inplace().parse(buf);
} }
} }
TEST_F(VariantTest, serdeEnd)
{
static auto constexpr TestsJson = std::array{
std::tuple{ R"({ "json1": 1 }{ "json2": 2 })"sv, '{', 14U },
std::tuple{ R"({ "json1": 1 })"sv, '\0', 14U },
};
static auto constexpr TestsBenc = std::array{
std::tuple{ "d5:benc1i1eed5:benc2i2ee"sv, 'd', 12U },
std::tuple{ "d5:benc1i1ee"sv, '\0', 12U },
};
for (auto [in, c, pos] : TestsJson)
{
auto json_serde = tr_variant_serde::json().inplace();
auto json_var = json_serde.parse(in).value_or(tr_variant{});
EXPECT_TRUE(json_var.holds_alternative<tr_variant::Map>()) << json_serde.error_;
EXPECT_EQ(*json_serde.end(), c);
EXPECT_EQ(json_serde.end() - std::data(in), pos);
}
for (auto [in, c, pos] : TestsBenc)
{
auto benc_serde = tr_variant_serde::benc().inplace();
auto benc_var = benc_serde.parse(in).value_or(tr_variant{});
EXPECT_TRUE(benc_var.holds_alternative<tr_variant::Map>()) << benc_serde.error_;
EXPECT_EQ(*benc_serde.end(), c);
EXPECT_EQ(benc_serde.end() - std::data(in), pos);
}
}