mirror of
https://github.com/protocolbuffers/protobuf
synced 2026-08-26 02:23:14 -04:00
980 lines
37 KiB
C++
980 lines
37 KiB
C++
// Protocol Buffers - Google's data interchange format
|
|
// Copyright 2025 Google LLC. All rights reserved.
|
|
//
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file or at
|
|
// https://developers.google.com/open-source/licenses/bsd
|
|
|
|
#include "upb/wire/decode.h"
|
|
|
|
#include <array>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <type_traits>
|
|
#include <vector>
|
|
|
|
#include <gtest/gtest.h>
|
|
#include "absl/strings/ascii.h"
|
|
#include "absl/strings/str_cat.h"
|
|
#include "absl/strings/string_view.h"
|
|
#include "upb/base/descriptor_constants.h"
|
|
#include "upb/base/status.h"
|
|
#include "upb/base/string_view.h"
|
|
#include "upb/base/upcast.h"
|
|
#include "upb/mem/arena.h"
|
|
#include "upb/mem/arena.hpp"
|
|
#include "upb/message/accessors.h"
|
|
#include "upb/message/accessors.hpp"
|
|
#include "upb/message/array.h"
|
|
#include "upb/message/internal/accessors.h"
|
|
#include "upb/message/internal/message.h"
|
|
#include "upb/message/message.h"
|
|
#include "upb/message/unknown_fields.h"
|
|
#include "upb/mini_descriptor/decode.h"
|
|
#include "upb/mini_descriptor/internal/encode.hpp"
|
|
#include "upb/mini_descriptor/link.h"
|
|
#include "upb/mini_table/extension.h"
|
|
#include "upb/mini_table/extension_registry.h"
|
|
#include "upb/mini_table/field.h"
|
|
#include "upb/mini_table/message.h"
|
|
#include "upb/test/test.upb.h"
|
|
#include "upb/test/test.upb_minitable.h"
|
|
#include "upb/wire/decode_fast/combinations.h"
|
|
#include "upb/wire/decode_test.upb_minitable.h"
|
|
#include "upb/wire/encode.h"
|
|
#include "upb/wire/test_util/field_types.h"
|
|
#include "upb/wire/test_util/make_mini_table.h"
|
|
#include "upb/wire/test_util/wire_message.h"
|
|
|
|
// Must be last.
|
|
#include "upb/port/def.inc"
|
|
|
|
namespace upb {
|
|
namespace test {
|
|
|
|
namespace {
|
|
|
|
std::vector<int> GetDecodeOptionsToTest() {
|
|
#if UPB_FASTTABLE
|
|
return {0, kUpb_DecodeOption_DisableFastTable};
|
|
#else
|
|
return {0};
|
|
#endif
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
std::string GetExpectedConsecutiveUnknownsTrace(int options) {
|
|
#if UPB_FASTTABLE
|
|
if (!(options & kUpb_DecodeOption_DisableFastTable)) {
|
|
return "D";
|
|
}
|
|
#endif
|
|
return "M";
|
|
}
|
|
#endif
|
|
|
|
template <typename T>
|
|
std::optional<T> GetOptionalField(upb_Message* msg,
|
|
const upb_MiniTableField* field) {
|
|
if (upb_Message_HasBaseField(msg, field)) {
|
|
return GetMessageBaseField<T>(msg, field, T{});
|
|
} else {
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
class FieldTypeTest : public testing::Test {};
|
|
|
|
TYPED_TEST_SUITE(FieldTypeTest, FieldTypes);
|
|
|
|
std::string ExpectedSingleFieldTrace(const upb_MiniTable* mt,
|
|
const upb_MiniTableField* field) {
|
|
#ifdef NDEBUG
|
|
return "";
|
|
#else
|
|
return MiniTable::HasFastTableEntry(mt, field) ? "DF" : "M";
|
|
#endif
|
|
}
|
|
|
|
std::string ExpectedRepeatedFieldTrace(const upb_MiniTable* mt,
|
|
const upb_MiniTableField* field,
|
|
int count) {
|
|
#ifdef NDEBUG
|
|
return "";
|
|
#else
|
|
if (MiniTable::HasFastTableEntry(mt, field)) {
|
|
// Fasttable repeated fields have a fast path where we bypass dispatch if
|
|
// the same tag is encountered consecutively.
|
|
return absl::StrCat("D", std::string(count, 'F'));
|
|
} else {
|
|
return std::string(count, 'M');
|
|
}
|
|
#endif
|
|
}
|
|
|
|
std::string FilteredTrace(absl::string_view trace) {
|
|
std::string filtered;
|
|
for (char c : trace) {
|
|
if (!absl::ascii_islower(c)) filtered.push_back(c);
|
|
}
|
|
return filtered;
|
|
}
|
|
|
|
TYPED_TEST(FieldTypeTest, DecodeOptionalMaxValue) {
|
|
char trace_buf[64];
|
|
using Value = typename TypeParam::Value;
|
|
upb::Arena arena;
|
|
auto [mt, field] = MiniTable::MakeSingleFieldTable<TypeParam>(
|
|
1, kUpb_DecodeFast_Scalar, arena.ptr());
|
|
upb_Message* msg = upb_Message_New(mt, arena.ptr());
|
|
std::string payload = ToBinaryPayload(wire_types::WireMessage{
|
|
{1, TypeParam::WireValue(Value(TypeParam::kMax))}});
|
|
upb_DecodeStatus result =
|
|
upb_DecodeWithTrace(payload.data(), payload.size(), msg, mt, nullptr, 0,
|
|
arena.ptr(), trace_buf, sizeof(trace_buf));
|
|
ASSERT_EQ(result, kUpb_DecodeStatus_Ok) << upb_DecodeStatus_String(result);
|
|
EXPECT_EQ(GetOptionalField<Value>(msg, field), TypeParam::kMax);
|
|
EXPECT_EQ(absl::string_view(trace_buf), ExpectedSingleFieldTrace(mt, field));
|
|
}
|
|
|
|
TYPED_TEST(FieldTypeTest, DecodeOptionalMinValue) {
|
|
char trace_buf[64];
|
|
using Value = typename TypeParam::Value;
|
|
upb::Arena arena;
|
|
auto [mt, field] = MiniTable::MakeSingleFieldTable<TypeParam>(
|
|
1, kUpb_DecodeFast_Scalar, arena.ptr());
|
|
upb_Message* msg = upb_Message_New(mt, arena.ptr());
|
|
std::string payload = ToBinaryPayload(wire_types::WireMessage{
|
|
{1, TypeParam::WireValue(Value(TypeParam::kMin))}});
|
|
upb_DecodeStatus result =
|
|
upb_DecodeWithTrace(payload.data(), payload.size(), msg, mt, nullptr, 0,
|
|
arena.ptr(), trace_buf, sizeof(trace_buf));
|
|
ASSERT_EQ(result, kUpb_DecodeStatus_Ok) << upb_DecodeStatus_String(result);
|
|
EXPECT_EQ(GetOptionalField<Value>(msg, field), TypeParam::kMin);
|
|
EXPECT_EQ(absl::string_view(trace_buf), ExpectedSingleFieldTrace(mt, field));
|
|
}
|
|
|
|
TYPED_TEST(FieldTypeTest, DecodeOneofMaxValue) {
|
|
char trace_buf[64];
|
|
using Value = typename TypeParam::Value;
|
|
upb::Arena arena;
|
|
auto [mt, field] = MiniTable::MakeSingleFieldTable<TypeParam>(
|
|
1, kUpb_DecodeFast_Oneof, arena.ptr());
|
|
upb_Message* msg = upb_Message_New(mt, arena.ptr());
|
|
std::string payload = ToBinaryPayload(wire_types::WireMessage{
|
|
{1, TypeParam::WireValue(Value(TypeParam::kMax))}});
|
|
upb_DecodeStatus result =
|
|
upb_DecodeWithTrace(payload.data(), payload.size(), msg, mt, nullptr, 0,
|
|
arena.ptr(), trace_buf, sizeof(trace_buf));
|
|
ASSERT_EQ(result, kUpb_DecodeStatus_Ok) << upb_DecodeStatus_String(result);
|
|
EXPECT_EQ(GetOptionalField<Value>(msg, field), TypeParam::kMax);
|
|
EXPECT_EQ(absl::string_view(trace_buf), ExpectedSingleFieldTrace(mt, field));
|
|
}
|
|
|
|
TYPED_TEST(FieldTypeTest, DecodeRepeated) {
|
|
char trace_buf[64];
|
|
using Value = typename TypeParam::Value;
|
|
Value value;
|
|
if constexpr (std::is_same_v<Value, std::string>) {
|
|
for (int i = 0; i < 1000; ++i) {
|
|
value.append("hello world! ");
|
|
}
|
|
} else {
|
|
value = TypeParam::kMax;
|
|
}
|
|
upb::Arena msg_arena;
|
|
upb::Arena mt_arena;
|
|
auto [mt, field] = MiniTable::MakeSingleFieldTable<TypeParam>(
|
|
1, kUpb_DecodeFast_Repeated, mt_arena.ptr());
|
|
upb_Message* msg = upb_Message_New(mt, msg_arena.ptr());
|
|
std::string payload = ToBinaryPayload(wire_types::WireMessage{
|
|
{1, TypeParam::WireValue(Value(TypeParam::kZero))},
|
|
{1, TypeParam::WireValue(Value(TypeParam::kMin))},
|
|
{1, TypeParam::WireValue(Value(TypeParam::kMax))},
|
|
});
|
|
upb_DecodeStatus result =
|
|
upb_DecodeWithTrace(payload.data(), payload.size(), msg, mt, nullptr, 0,
|
|
msg_arena.ptr(), trace_buf, sizeof(trace_buf));
|
|
ASSERT_EQ(result, kUpb_DecodeStatus_Ok) << upb_DecodeStatus_String(result);
|
|
EXPECT_EQ(GetRepeatedField<Value>(msg, field),
|
|
(std::vector<Value>{Value(TypeParam::kZero), Value(TypeParam::kMin),
|
|
Value(TypeParam::kMax)}));
|
|
EXPECT_EQ(FilteredTrace(absl::string_view(trace_buf)),
|
|
ExpectedRepeatedFieldTrace(mt, field, 3));
|
|
}
|
|
|
|
template <typename T>
|
|
class PackedTest : public testing::Test {};
|
|
|
|
TYPED_TEST_SUITE(PackedTest, PackableFieldTypes);
|
|
|
|
TYPED_TEST(PackedTest, DecodePackedDataForPackedField) {
|
|
char trace_buf[64];
|
|
using Value = typename TypeParam::Value;
|
|
upb::Arena msg_arena;
|
|
upb::Arena mt_arena;
|
|
auto [mt, field] = MiniTable::MakeSingleFieldTable<TypeParam>(
|
|
1, kUpb_DecodeFast_Packed, mt_arena.ptr());
|
|
upb_Message* msg = upb_Message_New(mt, msg_arena.ptr());
|
|
std::string packed_value =
|
|
ToBinaryPayload(TypeParam::WireValue(TypeParam::kZero)) +
|
|
ToBinaryPayload(TypeParam::WireValue(TypeParam::kMin)) +
|
|
ToBinaryPayload(TypeParam::WireValue(TypeParam::kMax));
|
|
std::string payload = ToBinaryPayload(
|
|
wire_types::WireMessage{{1, wire_types::Delimited{packed_value}}});
|
|
upb_DecodeStatus result =
|
|
upb_DecodeWithTrace(payload.data(), payload.size(), msg, mt, nullptr, 0,
|
|
msg_arena.ptr(), trace_buf, sizeof(trace_buf));
|
|
ASSERT_EQ(result, kUpb_DecodeStatus_Ok) << upb_DecodeStatus_String(result);
|
|
EXPECT_EQ(GetRepeatedField<Value>(msg, field),
|
|
(std::vector<Value>{Value(TypeParam::kZero), Value(TypeParam::kMin),
|
|
Value(TypeParam::kMax)}));
|
|
EXPECT_EQ(absl::string_view(trace_buf), ExpectedSingleFieldTrace(mt, field));
|
|
}
|
|
|
|
TYPED_TEST(PackedTest, DecodeTruncatedPackedField) {
|
|
char trace_buf[64];
|
|
upb::Arena msg_arena;
|
|
upb::Arena mt_arena;
|
|
auto [mt, field] = MiniTable::MakeSingleFieldTable<TypeParam>(
|
|
1, kUpb_DecodeFast_Packed, mt_arena.ptr());
|
|
upb_Message* msg = upb_Message_New(mt, msg_arena.ptr());
|
|
std::string packed_value =
|
|
ToBinaryPayload(TypeParam::WireValue(TypeParam::kZero)) +
|
|
ToBinaryPayload(TypeParam::WireValue(TypeParam::kMin)) +
|
|
// For varint fields, this will be a multi-byte varint, such that
|
|
// truncating the last byte will result in an invalid varint.
|
|
ToBinaryPayloadWithLongVarints(TypeParam::WireValue(TypeParam::kMax), 2,
|
|
2);
|
|
packed_value.resize(packed_value.size() - 1); // Truncate the last byte.
|
|
std::string payload = ToBinaryPayload(
|
|
wire_types::WireMessage{{1, wire_types::Delimited{packed_value}}});
|
|
upb_DecodeStatus result =
|
|
upb_DecodeWithTrace(payload.data(), payload.size(), msg, mt, nullptr, 0,
|
|
msg_arena.ptr(), trace_buf, sizeof(trace_buf));
|
|
ASSERT_EQ(result, kUpb_DecodeStatus_Malformed)
|
|
<< upb_DecodeStatus_String(result);
|
|
}
|
|
|
|
TYPED_TEST(PackedTest, DecodeEmptyPackedField) {
|
|
char trace_buf[64];
|
|
using Value = typename TypeParam::Value;
|
|
upb::Arena msg_arena;
|
|
upb::Arena mt_arena;
|
|
auto [mt, field] = MiniTable::MakeSingleFieldTable<TypeParam>(
|
|
1, kUpb_DecodeFast_Packed, mt_arena.ptr());
|
|
upb_Message* msg = upb_Message_New(mt, msg_arena.ptr());
|
|
std::string payload =
|
|
ToBinaryPayload(wire_types::WireMessage{{1, wire_types::Delimited{""}}});
|
|
upb_DecodeStatus result =
|
|
upb_DecodeWithTrace(payload.data(), payload.size(), msg, mt, nullptr, 0,
|
|
msg_arena.ptr(), trace_buf, sizeof(trace_buf));
|
|
ASSERT_EQ(result, kUpb_DecodeStatus_Ok) << upb_DecodeStatus_String(result);
|
|
EXPECT_EQ(GetRepeatedField<Value>(msg, field), (std::vector<Value>{}));
|
|
EXPECT_EQ(absl::string_view(trace_buf), ExpectedSingleFieldTrace(mt, field));
|
|
}
|
|
|
|
TYPED_TEST(PackedTest, DecodePackedDataForUnpackedField) {
|
|
// Schema says this is not a packed field, but we supply packed wire format.
|
|
char trace_buf[64];
|
|
using Value = typename TypeParam::Value;
|
|
upb::Arena msg_arena;
|
|
upb::Arena mt_arena;
|
|
auto [mt, field] = MiniTable::MakeSingleFieldTable<TypeParam>(
|
|
1, kUpb_DecodeFast_Repeated, mt_arena.ptr());
|
|
upb_Message* msg = upb_Message_New(mt, msg_arena.ptr());
|
|
std::string packed_value = ToBinaryPayload(TypeParam::WireValue(0)) +
|
|
ToBinaryPayload(TypeParam::WireValue(1 << 10)) +
|
|
ToBinaryPayload(TypeParam::WireValue(1 << 20));
|
|
std::string payload = ToBinaryPayload(
|
|
wire_types::WireMessage{{1, wire_types::Delimited{packed_value}}});
|
|
upb_DecodeStatus result =
|
|
upb_DecodeWithTrace(payload.data(), payload.size(), msg, mt, nullptr, 0,
|
|
msg_arena.ptr(), trace_buf, sizeof(trace_buf));
|
|
ASSERT_EQ(result, kUpb_DecodeStatus_Ok) << upb_DecodeStatus_String(result);
|
|
EXPECT_EQ(GetRepeatedField<Value>(msg, field),
|
|
(std::vector<Value>{0, static_cast<Value>(1 << 10),
|
|
static_cast<Value>(1 << 20)}));
|
|
// Even though there is a mismatch, we can still parse this fast.
|
|
EXPECT_EQ(absl::string_view(trace_buf), ExpectedSingleFieldTrace(mt, field));
|
|
}
|
|
|
|
TYPED_TEST(PackedTest, DecodeUnpackedDataForPackedField) {
|
|
// Schema says this is a packed field, but we supply unpacked wire format.
|
|
char trace_buf[64];
|
|
using Value = typename TypeParam::Value;
|
|
upb::Arena msg_arena;
|
|
upb::Arena mt_arena;
|
|
auto [mt, field] = MiniTable::MakeSingleFieldTable<TypeParam>(
|
|
1, kUpb_DecodeFast_Packed, mt_arena.ptr());
|
|
upb_Message* msg = upb_Message_New(mt, msg_arena.ptr());
|
|
std::string payload = ToBinaryPayload(wire_types::WireMessage{
|
|
{1, TypeParam::WireValue(0)},
|
|
{1, TypeParam::WireValue(1 << 10)},
|
|
{1, TypeParam::WireValue(1 << 20)},
|
|
});
|
|
upb_DecodeStatus result =
|
|
upb_DecodeWithTrace(payload.data(), payload.size(), msg, mt, nullptr, 0,
|
|
msg_arena.ptr(), trace_buf, sizeof(trace_buf));
|
|
ASSERT_EQ(result, kUpb_DecodeStatus_Ok) << upb_DecodeStatus_String(result);
|
|
EXPECT_EQ(GetRepeatedField<Value>(msg, field),
|
|
(std::vector<Value>{0, static_cast<Value>(1 << 10),
|
|
static_cast<Value>(1 << 20)}));
|
|
// Even though there is a mismatch, we can still parse this fast.
|
|
EXPECT_EQ(FilteredTrace(absl::string_view(trace_buf)),
|
|
ExpectedRepeatedFieldTrace(mt, field, 3));
|
|
}
|
|
|
|
TEST(RepeatedFieldTest, RepeatedMessageFallback) {
|
|
Arena mt_arena;
|
|
Arena msg_arena;
|
|
|
|
auto [sub_mt, sub_field] =
|
|
test::MiniTable::MakeSingleFieldTable<test::field_types::Int32>(
|
|
1, kUpb_DecodeFast_Scalar, mt_arena.ptr());
|
|
|
|
auto [mt, field] =
|
|
test::MiniTable::MakeSingleFieldTable<test::field_types::Message>(
|
|
1, kUpb_DecodeFast_Repeated, mt_arena.ptr());
|
|
|
|
const upb_MiniTable* subs[1] = {sub_mt};
|
|
bool linked =
|
|
upb_MiniTable_Link(const_cast<upb_MiniTable*>(mt), subs, 1, nullptr, 0);
|
|
ASSERT_TRUE(linked);
|
|
|
|
upb_Message* msg = upb_Message_New(mt, msg_arena.ptr());
|
|
|
|
// Payload:
|
|
// Element 1: tag 1, len 2, int32 value 5
|
|
// Element 2: tag 1, len 2 (parsed as overlong 3-byte varint to trigger
|
|
// fasttable fallback), int32 value 6
|
|
std::string payload("\x0a\x02\x08\x05\x0a\x82\x80\x00\x08\x06", 10);
|
|
upb_DecodeStatus result = upb_Decode(payload.data(), payload.size(), msg, mt,
|
|
nullptr, 0, msg_arena.ptr());
|
|
|
|
// Fasttable fallback used to drop the first element for repeated messages
|
|
// because array size wasn't updated.
|
|
ASSERT_EQ(result, kUpb_DecodeStatus_Ok) << upb_DecodeStatus_String(result);
|
|
|
|
const upb_Array* arr = upb_Message_GetArray(msg, field);
|
|
ASSERT_NE(arr, nullptr);
|
|
EXPECT_EQ(upb_Array_Size(arr), 2u);
|
|
}
|
|
|
|
TEST(RepeatedFieldTest, RepeatedMessageLongVarintSizeFastPath) {
|
|
char trace_buf[64];
|
|
Arena mt_arena;
|
|
Arena msg_arena;
|
|
|
|
auto [sub_mt, sub_field] =
|
|
test::MiniTable::MakeSingleFieldTable<test::field_types::Int32>(
|
|
1, kUpb_DecodeFast_Scalar, mt_arena.ptr());
|
|
|
|
auto [mt, field] =
|
|
test::MiniTable::MakeSingleFieldTable<test::field_types::Message>(
|
|
1, kUpb_DecodeFast_Repeated, mt_arena.ptr());
|
|
|
|
const upb_MiniTable* subs[1] = {sub_mt};
|
|
bool linked =
|
|
upb_MiniTable_Link(const_cast<upb_MiniTable*>(mt), subs, 1, nullptr, 0);
|
|
ASSERT_TRUE(linked);
|
|
|
|
upb_Message* msg = upb_Message_New(mt, msg_arena.ptr());
|
|
|
|
// Payload:
|
|
// Element 1: tag 1, len 2, int32 value 5
|
|
// Element 2: tag 1, len 2 (parsed as overlong 3-byte varint), int32 value 6
|
|
std::string payload("\x0a\x02\x08\x05\x0a\x82\x80\x00\x08\x06", 10);
|
|
upb_DecodeStatus result =
|
|
upb_DecodeWithTrace(payload.data(), payload.size(), msg, mt, nullptr, 0,
|
|
msg_arena.ptr(), trace_buf, sizeof(trace_buf));
|
|
|
|
ASSERT_EQ(result, kUpb_DecodeStatus_Ok) << upb_DecodeStatus_String(result);
|
|
#if !defined(NDEBUG)
|
|
#if UPB_FASTTABLE
|
|
EXPECT_EQ(FilteredTrace(absl::string_view(trace_buf)), "DDFFDFF");
|
|
#else
|
|
EXPECT_EQ(FilteredTrace(absl::string_view(trace_buf)), "MMMM");
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
TEST(RepeatedFieldTest, LongRepeatedField) {
|
|
auto trace_buf = std::make_unique<std::array<char, 1024>>();
|
|
using TypeParam = field_types::Fixed64;
|
|
using Value = typename TypeParam::Value;
|
|
upb::Arena msg_arena;
|
|
upb::Arena mt_arena;
|
|
auto [mt, field] = MiniTable::MakeSingleFieldTable<TypeParam>(
|
|
1, kUpb_DecodeFast_Packed, mt_arena.ptr());
|
|
upb_Message* msg = upb_Message_New(mt, msg_arena.ptr());
|
|
wire_types::WireMessage wire_msg;
|
|
std::vector<Value> expected;
|
|
for (int i = 0; i < 256; ++i) {
|
|
wire_msg.push_back({1, TypeParam::WireValue(i)});
|
|
expected.push_back(i);
|
|
}
|
|
std::string payload = ToBinaryPayload(wire_msg);
|
|
upb_DecodeStatus result = upb_DecodeWithTrace(
|
|
payload.data(), payload.size(), msg, mt, nullptr, 0, msg_arena.ptr(),
|
|
trace_buf->data(), trace_buf->size());
|
|
ASSERT_EQ(result, kUpb_DecodeStatus_Ok) << upb_DecodeStatus_String(result);
|
|
EXPECT_EQ(GetRepeatedField<Value>(msg, field), expected);
|
|
|
|
// We can't easily check the trace here because the large array size will
|
|
// force reallocations that cause fallbacks to the MiniTable decoder.
|
|
}
|
|
|
|
TYPED_TEST(PackedTest, DecodeTruncatedPackedFieldMaxLen) {
|
|
char trace_buf[64];
|
|
upb::Arena msg_arena;
|
|
upb::Arena mt_arena;
|
|
auto [mt, field] = MiniTable::MakeSingleFieldTable<TypeParam>(
|
|
1, kUpb_DecodeFast_Packed, mt_arena.ptr());
|
|
upb_Message* msg = upb_Message_New(mt, msg_arena.ptr());
|
|
// Malformed payload with the maximum allowed varint length but only one byte
|
|
// of data.
|
|
std::string payload = "\012\xff\xff\xff\xff\x07\000\000\000\000";
|
|
upb_DecodeStatus result =
|
|
upb_DecodeWithTrace(payload.data(), payload.size(), msg, mt, nullptr, 0,
|
|
msg_arena.ptr(), trace_buf, sizeof(trace_buf));
|
|
ASSERT_EQ(result, kUpb_DecodeStatus_Malformed)
|
|
<< upb_DecodeStatus_String(result);
|
|
}
|
|
|
|
TYPED_TEST(PackedTest, DecodeTruncatedPackedFieldShortLength) {
|
|
char trace_buf[64];
|
|
upb::Arena msg_arena;
|
|
upb::Arena mt_arena;
|
|
auto [mt, field] = MiniTable::MakeSingleFieldTable<TypeParam>(
|
|
1, kUpb_DecodeFast_Packed, mt_arena.ptr());
|
|
upb_Message* msg = upb_Message_New(mt, msg_arena.ptr());
|
|
// Malformed payload with the maximum allowed varint length but only one byte
|
|
// of data.
|
|
std::string payload = "\012\001";
|
|
upb_DecodeStatus result =
|
|
upb_DecodeWithTrace(payload.data(), payload.size(), msg, mt, nullptr, 0,
|
|
msg_arena.ptr(), trace_buf, sizeof(trace_buf));
|
|
ASSERT_EQ(result, kUpb_DecodeStatus_Malformed)
|
|
<< upb_DecodeStatus_String(result);
|
|
}
|
|
TEST(DecodeTest, EmptyMiniTableDecodedAsUnknown) {
|
|
Arena mt_arena;
|
|
Arena msg_arena;
|
|
|
|
upb_MiniTable* empty_mt =
|
|
(upb_MiniTable*)upb_Arena_Malloc(mt_arena.ptr(), sizeof(upb_MiniTable));
|
|
memset(empty_mt, 0, sizeof(upb_MiniTable));
|
|
empty_mt->UPB_PRIVATE(size) = sizeof(upb_Message);
|
|
empty_mt->UPB_ONLYBITS(field_count) = 0;
|
|
|
|
upb_Message* msg = upb_Message_New(empty_mt, msg_arena.ptr());
|
|
|
|
// An arbitrary payload that should be parsed as unknown:
|
|
// field 1, length-delimited, length 2, data="\x08\x05"
|
|
std::string payload("\x0a\x02\x08\x05");
|
|
|
|
upb_DecodeStatus result = upb_Decode(payload.data(), payload.size(), msg,
|
|
empty_mt, nullptr, 0, msg_arena.ptr());
|
|
|
|
ASSERT_EQ(result, kUpb_DecodeStatus_Ok) << upb_DecodeStatus_String(result);
|
|
|
|
EXPECT_TRUE(upb_Message_HasUnknown(msg));
|
|
|
|
uintptr_t iter = kUpb_Message_UnknownBegin;
|
|
upb_MessageUnknown data;
|
|
ASSERT_TRUE(upb_Message_NextUnknown2(msg, &data, &iter));
|
|
ASSERT_EQ(data.type, kUpb_MessageUnknownType_StringView);
|
|
EXPECT_EQ(absl::string_view(data.value.bytes.data, data.value.bytes.size),
|
|
payload);
|
|
EXPECT_FALSE(upb_Message_NextUnknown2(msg, &data, &iter));
|
|
}
|
|
|
|
TEST(DecodeTest, ConsecutiveUnknownFieldsWithoutAlias) {
|
|
char trace_buf[64];
|
|
Arena mt_arena;
|
|
|
|
auto [mt, field] = MiniTable::MakeSingleFieldTable<field_types::Int32>(
|
|
1, kUpb_DecodeFast_Scalar, mt_arena.ptr());
|
|
|
|
// Field 2: tag 2, varint, value 2 -> \x10\x02
|
|
// Field 3: tag 3, varint, value 3 -> \x18\x03
|
|
std::string payload("\x10\x02\x18\x03", 4);
|
|
|
|
for (int options : GetDecodeOptionsToTest()) {
|
|
Arena msg_arena;
|
|
upb_Message* msg = upb_Message_New(mt, msg_arena.ptr());
|
|
memset(trace_buf, 0, sizeof(trace_buf));
|
|
|
|
upb_DecodeStatus result = upb_DecodeWithTrace(
|
|
payload.data(), payload.size(), msg, mt, nullptr, options,
|
|
msg_arena.ptr(), trace_buf, sizeof(trace_buf));
|
|
|
|
ASSERT_EQ(result, kUpb_DecodeStatus_Ok) << upb_DecodeStatus_String(result);
|
|
|
|
EXPECT_TRUE(upb_Message_HasUnknown(msg));
|
|
|
|
uintptr_t iter = kUpb_Message_UnknownBegin;
|
|
upb_MessageUnknown data;
|
|
|
|
// We expect them to be merged.
|
|
ASSERT_TRUE(upb_Message_NextUnknown2(msg, &data, &iter));
|
|
ASSERT_EQ(data.type, kUpb_MessageUnknownType_StringView);
|
|
EXPECT_EQ(absl::string_view(data.value.bytes.data, data.value.bytes.size),
|
|
payload);
|
|
EXPECT_FALSE(upb_Message_NextUnknown2(msg, &data, &iter));
|
|
|
|
#ifndef NDEBUG
|
|
// Assert that consecutive unknown fields optimization took effect, decoding
|
|
// both unknown fields in a single step (trace "M" instead of "MM").
|
|
EXPECT_EQ(absl::string_view(trace_buf),
|
|
GetExpectedConsecutiveUnknownsTrace(options));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
TEST(DecodeTest, ConsecutiveUnknownFieldsWithAlias) {
|
|
char trace_buf[64];
|
|
Arena mt_arena;
|
|
|
|
auto [mt, field] = MiniTable::MakeSingleFieldTable<field_types::Int32>(
|
|
1, kUpb_DecodeFast_Scalar, mt_arena.ptr());
|
|
|
|
// Field 2: tag 2, varint, value 2 -> \x10\x02
|
|
// Field 3: tag 3, varint, value 3 -> \x18\x03
|
|
std::string payload("\x10\x02\x18\x03", 4);
|
|
|
|
for (int extra_options : GetDecodeOptionsToTest()) {
|
|
Arena msg_arena;
|
|
upb_Message* msg = upb_Message_New(mt, msg_arena.ptr());
|
|
memset(trace_buf, 0, sizeof(trace_buf));
|
|
|
|
int options = extra_options | kUpb_DecodeOption_AliasString;
|
|
upb_DecodeStatus result = upb_DecodeWithTrace(
|
|
payload.data(), payload.size(), msg, mt, nullptr, options,
|
|
msg_arena.ptr(), trace_buf, sizeof(trace_buf));
|
|
|
|
ASSERT_EQ(result, kUpb_DecodeStatus_Ok) << upb_DecodeStatus_String(result);
|
|
|
|
EXPECT_TRUE(upb_Message_HasUnknown(msg));
|
|
|
|
uintptr_t iter = kUpb_Message_UnknownBegin;
|
|
upb_MessageUnknown data;
|
|
|
|
ASSERT_TRUE(upb_Message_NextUnknown2(msg, &data, &iter));
|
|
ASSERT_EQ(data.type, kUpb_MessageUnknownType_StringView);
|
|
EXPECT_EQ(absl::string_view(data.value.bytes.data, data.value.bytes.size),
|
|
payload);
|
|
EXPECT_FALSE(upb_Message_NextUnknown2(msg, &data, &iter));
|
|
|
|
#ifndef NDEBUG
|
|
EXPECT_EQ(absl::string_view(trace_buf),
|
|
GetExpectedConsecutiveUnknownsTrace(options));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
TEST(DecodeTest, MaxDepthPayloadParsesSuccessfully) {
|
|
upb::Arena mt_arena;
|
|
upb::Arena msg_arena;
|
|
|
|
// Construct recursive message to allow testing arbitrary depths.
|
|
auto [mt, field] =
|
|
test::MiniTable::MakeSingleFieldTable<test::field_types::Message>(
|
|
1, kUpb_DecodeFast_Scalar, mt_arena.ptr());
|
|
const upb_MiniTable* subs[1] = {mt}; // Submessage is of own type.
|
|
bool linked =
|
|
upb_MiniTable_Link(const_cast<upb_MiniTable*>(mt), subs, 1, nullptr, 0);
|
|
ASSERT_TRUE(linked);
|
|
|
|
// We'll set a small depth limit to make it easy to test.
|
|
const int kMaxDepth = 10;
|
|
int options = upb_Decode_LimitDepth(0, kMaxDepth);
|
|
|
|
auto make_payload = [](int depth) {
|
|
std::string payload;
|
|
for (int i = 0; i < depth; ++i) {
|
|
// field 1, delimited
|
|
payload += '\n';
|
|
// length (remaining payload)
|
|
// Each level adds 2 bytes (tag + length byte).
|
|
payload.push_back(static_cast<char>((depth - i - 1) * 2));
|
|
}
|
|
return payload;
|
|
};
|
|
|
|
// Test depth kMaxDepth - should succeed.
|
|
{
|
|
std::string payload = make_payload(kMaxDepth);
|
|
upb_Message* msg = upb_Message_New(mt, msg_arena.ptr());
|
|
upb_DecodeStatus result = upb_Decode(payload.data(), payload.size(), msg,
|
|
mt, nullptr, options, msg_arena.ptr());
|
|
EXPECT_EQ(result, kUpb_DecodeStatus_Ok) << upb_DecodeStatus_String(result);
|
|
}
|
|
|
|
// Test depth kMaxDepth + 1 - should fail.
|
|
{
|
|
std::string payload = make_payload(kMaxDepth + 1);
|
|
upb_Message* msg = upb_Message_New(mt, msg_arena.ptr());
|
|
upb_DecodeStatus result = upb_Decode(payload.data(), payload.size(), msg,
|
|
mt, nullptr, options, msg_arena.ptr());
|
|
EXPECT_EQ(result, kUpb_DecodeStatus_MaxDepthExceeded)
|
|
<< upb_DecodeStatus_String(result);
|
|
}
|
|
}
|
|
|
|
TEST(DecodeTest, DecodeNonCanonicalExtensionAsUnknown) {
|
|
upb::Arena arena;
|
|
|
|
// 1. Create base msg which starts empty
|
|
upb_test_ModelWithExtensions* msg =
|
|
upb_test_ModelWithExtensions_new(arena.ptr());
|
|
|
|
// 2. Create parsed submessage ("World")
|
|
upb_Message* extension1 =
|
|
UPB_UPCAST(upb_test_ModelExtension1_new(arena.ptr()));
|
|
upb_test_ModelExtension1_set_str((upb_test_ModelExtension1*)extension1,
|
|
upb_StringView_FromString("World"));
|
|
|
|
// 3. msg has a non-canonical extension A
|
|
EXPECT_TRUE(UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
|
|
UPB_UPCAST(msg), upb_test_ModelExtension1_model_ext_ext, &extension1,
|
|
arena.ptr()));
|
|
|
|
// Verify extension count is 0 before encoding/decoding.
|
|
EXPECT_EQ((int)upb_Message_ExtensionCount(UPB_UPCAST(msg)), 0);
|
|
|
|
// 5. Obtain encoded non-canonical extension A by serializing msg
|
|
char* buf;
|
|
size_t size;
|
|
upb_EncodeStatus enc_status =
|
|
upb_Encode(UPB_UPCAST(msg), &upb_0test__ModelWithExtensions_msg_init, 0,
|
|
arena.ptr(), &buf, &size);
|
|
ASSERT_EQ(enc_status, kUpb_EncodeStatus_Ok);
|
|
ASSERT_GT(size, 0u);
|
|
|
|
// 6. Decode with extreg = nullptr (so the encoded extension A is decoded as
|
|
// unknown bytes)
|
|
upb_DecodeStatus dec_status = upb_Decode(
|
|
buf, size, UPB_UPCAST(msg), &upb_0test__ModelWithExtensions_msg_init,
|
|
/*extreg=*/nullptr, 0, arena.ptr());
|
|
ASSERT_EQ(dec_status, kUpb_DecodeStatus_Ok);
|
|
|
|
// 7. Verify that we end up with exactly one non-canonical extension A + one
|
|
// unknown bytes block representing A
|
|
int non_canonical_count = 0;
|
|
int unknown_bytes_count = 0;
|
|
uintptr_t iter = kUpb_Message_UnknownBegin;
|
|
upb_MessageUnknown data;
|
|
while (upb_Message_NextUnknown2(UPB_UPCAST(msg), &data, &iter)) {
|
|
if (data.type == kUpb_MessageUnknownType_NonCanonicalExtension) {
|
|
non_canonical_count++;
|
|
} else if (data.type == kUpb_MessageUnknownType_StringView) {
|
|
unknown_bytes_count++;
|
|
}
|
|
}
|
|
EXPECT_EQ(non_canonical_count, 1);
|
|
EXPECT_EQ(unknown_bytes_count, 1);
|
|
|
|
// Verify extension APIs: there are zero canonical extensions.
|
|
EXPECT_EQ((int)upb_Message_ExtensionCount(UPB_UPCAST(msg)), 0);
|
|
uintptr_t ext_iter = kUpb_Message_ExtensionBegin;
|
|
const upb_MiniTableExtension* ext_out = nullptr;
|
|
upb_MessageValue val_out;
|
|
EXPECT_FALSE(upb_Message_NextExtension(UPB_UPCAST(msg), &ext_out, &val_out,
|
|
&ext_iter));
|
|
}
|
|
|
|
TEST(DecodeTest, DecodeExtensionAsUnknownWithPreexistingUnknown) {
|
|
upb::Arena arena;
|
|
|
|
// 1. Create a temporary message to serialize the extension
|
|
upb_test_ModelWithExtensions* tmp_msg =
|
|
upb_test_ModelWithExtensions_new(arena.ptr());
|
|
|
|
// 2. Create parsed submessage ("World")
|
|
upb_Message* extension1 =
|
|
UPB_UPCAST(upb_test_ModelExtension1_new(arena.ptr()));
|
|
upb_test_ModelExtension1_set_str((upb_test_ModelExtension1*)extension1,
|
|
upb_StringView_FromString("World"));
|
|
|
|
// 3. Attach to tmp_msg as a non-canonical extension so we can serialize it to
|
|
// get the bytes
|
|
EXPECT_TRUE(UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
|
|
UPB_UPCAST(tmp_msg), upb_test_ModelExtension1_model_ext_ext, &extension1,
|
|
arena.ptr()));
|
|
|
|
// 5. Obtain encoded extension A by serializing tmp_msg
|
|
char* buf;
|
|
size_t size;
|
|
upb_EncodeStatus enc_status =
|
|
upb_Encode(UPB_UPCAST(tmp_msg), &upb_0test__ModelWithExtensions_msg_init,
|
|
0, arena.ptr(), &buf, &size);
|
|
ASSERT_EQ(enc_status, kUpb_EncodeStatus_Ok);
|
|
ASSERT_GT(size, 0u);
|
|
|
|
// 6. Create destination message and put the serialized bytes as an unknown
|
|
// field on msg
|
|
upb_test_ModelWithExtensions* msg =
|
|
upb_test_ModelWithExtensions_new(arena.ptr());
|
|
bool add_ok = UPB_PRIVATE(_upb_Message_AddUnknown)(
|
|
UPB_UPCAST(msg), buf, size, arena.ptr(), kUpb_AddUnknown_Alias);
|
|
ASSERT_TRUE(add_ok);
|
|
|
|
// Verify extension count is 0 before decoding.
|
|
EXPECT_EQ((int)upb_Message_ExtensionCount(UPB_UPCAST(msg)), 0);
|
|
|
|
// 7. Decode with extreg = nullptr (so the encoded extension A is decoded as
|
|
// unknown bytes)
|
|
upb_DecodeStatus dec_status = upb_Decode(
|
|
buf, size, UPB_UPCAST(msg), &upb_0test__ModelWithExtensions_msg_init,
|
|
/*extreg=*/nullptr, 0, arena.ptr());
|
|
ASSERT_EQ(dec_status, kUpb_DecodeStatus_Ok);
|
|
|
|
// 8. Verify that we end up with exactly two unknown bytes blocks representing
|
|
// A
|
|
int non_canonical_count = 0;
|
|
int unknown_bytes_count = 0;
|
|
uintptr_t iter = kUpb_Message_UnknownBegin;
|
|
upb_MessageUnknown data;
|
|
while (upb_Message_NextUnknown2(UPB_UPCAST(msg), &data, &iter)) {
|
|
if (data.type == kUpb_MessageUnknownType_NonCanonicalExtension) {
|
|
non_canonical_count++;
|
|
} else if (data.type == kUpb_MessageUnknownType_StringView) {
|
|
unknown_bytes_count++;
|
|
}
|
|
}
|
|
EXPECT_EQ(non_canonical_count, 0);
|
|
EXPECT_EQ(unknown_bytes_count, 2);
|
|
|
|
// Verify extension APIs: there are zero canonical extensions.
|
|
EXPECT_EQ((int)upb_Message_ExtensionCount(UPB_UPCAST(msg)), 0);
|
|
uintptr_t ext_iter = kUpb_Message_ExtensionBegin;
|
|
const upb_MiniTableExtension* ext_out = nullptr;
|
|
upb_MessageValue val_out;
|
|
EXPECT_FALSE(upb_Message_NextExtension(UPB_UPCAST(msg), &ext_out, &val_out,
|
|
&ext_iter));
|
|
}
|
|
|
|
TEST(DecodeTest, DecodeGroupFieldFromDelimitedWireFormatAsUnknown) {
|
|
upb::Arena mt_arena;
|
|
upb::Arena msg_arena;
|
|
|
|
// 1. Create Parent MiniTable containing a repeated Group field directly.
|
|
auto [parent_mt, parent_field] =
|
|
test::MiniTable::MakeSingleFieldTable<test::field_types::Group>(
|
|
5, kUpb_DecodeFast_Repeated, mt_arena.ptr());
|
|
|
|
// 2. Build length-delimited wire payload for Group field 5:
|
|
// Tag 5 Delimited = 42 (0x2a), length = 2, child field 1 = 123 ("\x08\x7b").
|
|
std::string payload("\x2a\x02\x08\x7b", 4);
|
|
|
|
// 3. Parse the payload into Parent Message.
|
|
upb_Message* parent_msg = upb_Message_New(parent_mt, msg_arena.ptr());
|
|
upb_DecodeStatus result =
|
|
upb_Decode(payload.data(), payload.size(), parent_msg, parent_mt, nullptr,
|
|
0, msg_arena.ptr());
|
|
|
|
// 4. Verify parsing succeeded cleanly.
|
|
ASSERT_EQ(result, kUpb_DecodeStatus_Ok) << upb_DecodeStatus_String(result);
|
|
|
|
// 5. Verify repeated Group field 5 was NOT populated as a known field.
|
|
const upb_Array* arr = upb_Message_GetArray(parent_msg, parent_field);
|
|
EXPECT_EQ(arr, nullptr);
|
|
|
|
// 6. Verify the wire payload was instead preserved inside the Unknown field
|
|
// set.
|
|
EXPECT_TRUE(upb_Message_HasUnknown(parent_msg));
|
|
|
|
uintptr_t iter = kUpb_Message_UnknownBegin;
|
|
upb_MessageUnknown data;
|
|
ASSERT_TRUE(upb_Message_NextUnknown2(parent_msg, &data, &iter));
|
|
ASSERT_EQ(data.type, kUpb_MessageUnknownType_StringView);
|
|
EXPECT_EQ(absl::string_view(data.value.bytes.data, data.value.bytes.size),
|
|
payload);
|
|
EXPECT_FALSE(upb_Message_NextUnknown2(parent_msg, &data, &iter));
|
|
}
|
|
|
|
TEST(DecodeTest, ConsecutiveUnknownFieldsWithGroup) {
|
|
char trace_buf[64];
|
|
Arena mt_arena;
|
|
|
|
auto [mt, field] = MiniTable::MakeSingleFieldTable<field_types::Int32>(
|
|
1, kUpb_DecodeFast_Scalar, mt_arena.ptr());
|
|
|
|
// Field 2: StartGroup -> \x13
|
|
// Field 3: Varint, value 123 -> \x18\x7b
|
|
// Field 2: EndGroup -> \x14
|
|
// Field 4: Varint, value 456 -> \x20\xc8\x03
|
|
std::string payload("\x13\x18\x7b\x14\x20\xc8\x03", 7);
|
|
|
|
for (int options : GetDecodeOptionsToTest()) {
|
|
Arena msg_arena;
|
|
upb_Message* msg = upb_Message_New(mt, msg_arena.ptr());
|
|
memset(trace_buf, 0, sizeof(trace_buf));
|
|
|
|
upb_DecodeStatus result = upb_DecodeWithTrace(
|
|
payload.data(), payload.size(), msg, mt, nullptr, options,
|
|
msg_arena.ptr(), trace_buf, sizeof(trace_buf));
|
|
|
|
ASSERT_EQ(result, kUpb_DecodeStatus_Ok) << upb_DecodeStatus_String(result);
|
|
|
|
EXPECT_TRUE(upb_Message_HasUnknown(msg));
|
|
|
|
uintptr_t iter = kUpb_Message_UnknownBegin;
|
|
upb_MessageUnknown data;
|
|
|
|
// We expect them to be merged.
|
|
ASSERT_TRUE(upb_Message_NextUnknown2(msg, &data, &iter));
|
|
ASSERT_EQ(data.type, kUpb_MessageUnknownType_StringView);
|
|
EXPECT_EQ(absl::string_view(data.value.bytes.data, data.value.bytes.size),
|
|
payload);
|
|
EXPECT_FALSE(upb_Message_NextUnknown2(msg, &data, &iter));
|
|
|
|
#ifndef NDEBUG
|
|
const char* expected = "M";
|
|
#if UPB_FASTTABLE
|
|
if (!(options & kUpb_DecodeOption_DisableFastTable)) {
|
|
expected = "D<M";
|
|
}
|
|
#endif
|
|
EXPECT_EQ(absl::string_view(trace_buf), expected);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
TEST(DecodeTest, MessageSetConsecutiveUnknowns) {
|
|
Arena mt_arena;
|
|
|
|
const upb_MiniTable* mset_mt = &upb_0decode_0test__TestMessageSet_msg_init;
|
|
const upb_MiniTableExtension* ext = upb_decode_test_ext_message_set_ext;
|
|
|
|
upb_ExtensionRegistry* reg = upb_ExtensionRegistry_New(mt_arena.ptr());
|
|
ASSERT_TRUE(reg != nullptr);
|
|
EXPECT_EQ(upb_ExtensionRegistry_Add(reg, ext),
|
|
kUpb_ExtensionRegistryStatus_Ok);
|
|
|
|
// 5. Construct the payload.
|
|
// Field 10 (Varint, value 1) is unknown: \x50\x01
|
|
// Field 1 (StartGroup, representing the MessageSet Item)
|
|
// Inside the group:
|
|
// Field 2 (type_id = 2000): \x10\xd0\x0f
|
|
// Field 3 (message: empty message, length 0): \x1a\x00
|
|
// Field 1 (EndGroup): \x0c
|
|
std::string payload("\x50\x01\x0b\x10\xd0\x0f\x1a\x00\x0c", 9);
|
|
|
|
for (int options : GetDecodeOptionsToTest()) {
|
|
Arena msg_arena;
|
|
upb_Message* msg = upb_Message_New(mset_mt, msg_arena.ptr());
|
|
ASSERT_TRUE(msg != nullptr);
|
|
|
|
// Parse the payload.
|
|
upb_DecodeStatus result =
|
|
upb_Decode(payload.data(), payload.size(), msg, mset_mt, reg, options,
|
|
msg_arena.ptr());
|
|
|
|
ASSERT_EQ(result, kUpb_DecodeStatus_Ok) << upb_DecodeStatus_String(result);
|
|
|
|
// Check if the extension was successfully parsed.
|
|
EXPECT_TRUE(upb_Message_HasExtension(msg, ext));
|
|
}
|
|
}
|
|
|
|
TEST(DecodeTest, FieldZeroRejected) {
|
|
Arena mt_arena;
|
|
|
|
// 1. Empty message with field 0 varint payload.
|
|
{
|
|
upb_MiniTable* empty_mt =
|
|
(upb_MiniTable*)upb_Arena_Malloc(mt_arena.ptr(), sizeof(upb_MiniTable));
|
|
memset(empty_mt, 0, sizeof(upb_MiniTable));
|
|
empty_mt->UPB_PRIVATE(size) = sizeof(upb_Message);
|
|
empty_mt->UPB_ONLYBITS(field_count) = 0;
|
|
|
|
std::string payload("\x00\x00", 2);
|
|
for (int options : GetDecodeOptionsToTest()) {
|
|
Arena msg_arena;
|
|
upb_Message* msg = upb_Message_New(empty_mt, msg_arena.ptr());
|
|
upb_DecodeStatus result =
|
|
upb_Decode(payload.data(), payload.size(), msg, empty_mt, nullptr,
|
|
options, msg_arena.ptr());
|
|
EXPECT_EQ(result, kUpb_DecodeStatus_Malformed);
|
|
}
|
|
}
|
|
|
|
// 2. Field 0 varint inside unknown group.
|
|
{
|
|
auto [mt, field] = MiniTable::MakeSingleFieldTable<field_types::Int32>(
|
|
1, kUpb_DecodeFast_Scalar, mt_arena.ptr());
|
|
|
|
// Field 2 (StartGroup) containing Field 0 varint.
|
|
std::string payload("\x13\x00\x00\x14", 4);
|
|
for (int options : GetDecodeOptionsToTest()) {
|
|
Arena msg_arena;
|
|
upb_Message* msg = upb_Message_New(mt, msg_arena.ptr());
|
|
upb_DecodeStatus result =
|
|
upb_Decode(payload.data(), payload.size(), msg, mt, nullptr, options,
|
|
msg_arena.ptr());
|
|
EXPECT_EQ(result, kUpb_DecodeStatus_Malformed);
|
|
}
|
|
}
|
|
|
|
// 3. Field 0 varint inside MessageSet item.
|
|
{
|
|
const upb_MiniTable* mset_mt = &upb_0decode_0test__TestMessageSet_msg_init;
|
|
// Field 1 (StartGroup for MessageSet Item) containing Field 0 varint.
|
|
std::string payload("\x0b\x00\x00\x0c", 4);
|
|
for (int options : GetDecodeOptionsToTest()) {
|
|
Arena msg_arena;
|
|
upb_Message* msg = upb_Message_New(mset_mt, msg_arena.ptr());
|
|
upb_DecodeStatus result =
|
|
upb_Decode(payload.data(), payload.size(), msg, mset_mt, nullptr,
|
|
options, msg_arena.ptr());
|
|
EXPECT_EQ(result, kUpb_DecodeStatus_Malformed);
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(DecodeTest, UnlinkedSubMessageFastTableSlotCollision) {
|
|
Arena mt_arena;
|
|
|
|
// Build a message where:
|
|
// - Field 16 is an unlinked submessage (slot 16)
|
|
// - Field 32 is a bool field that collides on the same fasttable slot (slot
|
|
// 16)
|
|
upb::MtDataEncoder e;
|
|
e.StartMessage(0);
|
|
e.PutField(kUpb_FieldType_Message, 16, 0);
|
|
e.PutField(kUpb_FieldType_Bool, 32, 0);
|
|
|
|
upb_Status status;
|
|
upb_Status_Clear(&status);
|
|
const upb_MiniTable* mt = upb_MiniTable_Build(
|
|
e.data().data(), e.data().size(), mt_arena.ptr(), &status);
|
|
ASSERT_TRUE(upb_Status_IsOk(&status)) << upb_Status_ErrorMessage(&status);
|
|
|
|
const upb_MiniTableField* bool_field =
|
|
upb_MiniTable_FindFieldByNumber(mt, 32);
|
|
ASSERT_NE(bool_field, nullptr);
|
|
|
|
// Field 32 (tag 256, varint: 0x80, 0x02), value = 1 (true)
|
|
std::string payload("\x80\x02\x01");
|
|
for (int options : GetDecodeOptionsToTest()) {
|
|
Arena msg_arena;
|
|
upb_Message* msg = upb_Message_New(mt, msg_arena.ptr());
|
|
upb_DecodeStatus result = upb_Decode(payload.data(), payload.size(), msg,
|
|
mt, nullptr, options, msg_arena.ptr());
|
|
EXPECT_EQ(result, kUpb_DecodeStatus_Ok) << upb_DecodeStatus_String(result);
|
|
EXPECT_TRUE(upb_Message_GetBool(msg, bool_field, false));
|
|
EXPECT_FALSE(upb_Message_HasUnknown(msg));
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
} // namespace test
|
|
} // namespace upb
|