Add vector field support to FlatbuffersTableUntypedDomainImpl
PiperOrigin-RevId: 745927987
diff --git a/domain_tests/arbitrary_domains_flatbuffers_test.cc b/domain_tests/arbitrary_domains_flatbuffers_test.cc
index 3d397a2..c6565de 100644
--- a/domain_tests/arbitrary_domains_flatbuffers_test.cc
+++ b/domain_tests/arbitrary_domains_flatbuffers_test.cc
@@ -19,6 +19,7 @@
#include <cstring>
#include <optional>
#include <string>
+#include <type_traits>
#include <vector>
#include "gmock/gmock.h"
@@ -42,11 +43,19 @@
namespace {
using ::fuzztest::internal::BoolTable;
+using ::fuzztest::internal::ByteEnum;
using ::fuzztest::internal::DefaultTable;
+using ::fuzztest::internal::IntEnum;
+using ::fuzztest::internal::LongEnum;
using ::fuzztest::internal::OptionalTable;
using ::fuzztest::internal::RecursiveTable;
using ::fuzztest::internal::RequiredTable;
+using ::fuzztest::internal::ShortEnum;
+using ::fuzztest::internal::UByteEnum;
+using ::fuzztest::internal::UIntEnum;
+using ::fuzztest::internal::ULongEnum;
using ::fuzztest::internal::UnsupportedTypesTable;
+using ::fuzztest::internal::UShortEnum;
using ::testing::_;
using ::testing::AllOf;
using ::testing::Each;
@@ -82,6 +91,24 @@
return lhs.b() == rhs.b();
}
+template <typename T>
+inline bool VectorEq(const flatbuffers::Vector<T>& lhs,
+ const flatbuffers::Vector<T>& rhs) {
+ if (lhs.size() != rhs.size()) return false;
+ for (int i = 0; i < lhs.size(); ++i) {
+ if (!Eq(lhs.Get(i), rhs.Get(i))) return false;
+ }
+ return true;
+}
+
+template <typename T>
+inline bool VectorEq(const flatbuffers::Vector<T>* lhs,
+ const flatbuffers::Vector<T>* rhs) {
+ if (lhs == nullptr && rhs == nullptr) return true;
+ if (lhs == nullptr || rhs == nullptr) return false;
+ return VectorEq(*lhs, *rhs);
+}
+
template <>
inline bool Eq<DefaultTable>(const DefaultTable& lhs, const DefaultTable& rhs) {
const bool eq_b = lhs.b() == rhs.b();
@@ -105,14 +132,71 @@
const bool eq_eu32 = lhs.eu32() == rhs.eu32();
const bool eq_eu64 = lhs.eu64() == rhs.eu64();
const bool eq_t = Eq(lhs.t(), rhs.t());
+ const bool eq_v_b = VectorEq(lhs.v_b(), rhs.v_b());
+ const bool eq_v_i8 = VectorEq(lhs.v_i8(), rhs.v_i8());
+ const bool eq_v_i16 = VectorEq(lhs.v_i16(), rhs.v_i16());
+ const bool eq_v_i32 = VectorEq(lhs.v_i32(), rhs.v_i32());
+ const bool eq_v_i64 = VectorEq(lhs.v_i64(), rhs.v_i64());
+ const bool eq_v_u8 = VectorEq(lhs.v_u8(), rhs.v_u8());
+ const bool eq_v_u16 = VectorEq(lhs.v_u16(), rhs.v_u16());
+ const bool eq_v_u32 = VectorEq(lhs.v_u32(), rhs.v_u32());
+ const bool eq_v_u64 = VectorEq(lhs.v_u64(), rhs.v_u64());
+ const bool eq_v_f = VectorEq(lhs.v_f(), rhs.v_f());
+ const bool eq_v_d = VectorEq(lhs.v_d(), rhs.v_d());
+ const bool eq_v_str = VectorEq(lhs.v_str(), rhs.v_str());
+ const bool eq_v_ei8 = VectorEq(lhs.v_ei8(), rhs.v_ei8());
+ const bool eq_v_ei16 = VectorEq(lhs.v_ei16(), rhs.v_ei16());
+ const bool eq_v_ei32 = VectorEq(lhs.v_ei32(), rhs.v_ei32());
+ const bool eq_v_ei64 = VectorEq(lhs.v_ei64(), rhs.v_ei64());
+ const bool eq_v_eu8 = VectorEq(lhs.v_eu8(), rhs.v_eu8());
+ const bool eq_v_eu16 = VectorEq(lhs.v_eu16(), rhs.v_eu16());
+ const bool eq_v_eu32 = VectorEq(lhs.v_eu32(), rhs.v_eu32());
+ const bool eq_v_eu64 = VectorEq(lhs.v_eu64(), rhs.v_eu64());
+ const bool eq_v_t = VectorEq(lhs.v_t(), rhs.v_t());
return eq_b && eq_i8 && eq_i16 && eq_i32 && eq_i64 && eq_u8 && eq_u16 &&
eq_u32 && eq_u64 && eq_f && eq_d && eq_str && eq_ei8 && eq_ei16 &&
- eq_ei32 && eq_ei64 && eq_eu8 && eq_eu16 && eq_eu32 && eq_eu64 && eq_t;
+ eq_ei32 && eq_ei64 && eq_eu8 && eq_eu16 && eq_eu32 && eq_eu64 &&
+ eq_t && eq_v_b && eq_v_i8 && eq_v_i16 && eq_v_i32 && eq_v_i64 &&
+ eq_v_u8 && eq_v_u16 && eq_v_u32 && eq_v_u64 && eq_v_f && eq_v_d &&
+ eq_v_str && eq_v_ei8 && eq_v_ei16 && eq_v_ei32 && eq_v_ei64 &&
+ eq_v_eu8 && eq_v_eu16 && eq_v_eu32 && eq_v_eu64 && eq_v_t;
}
const internal::DefaultTable* CreateDefaultTable(
flatbuffers::FlatBufferBuilder& fbb) {
auto bool_table_offset = internal::CreateBoolTable(fbb, true);
+ std::vector<uint8_t> v_b{true, false};
+ std::vector<int8_t> v_i8{1, 2, 3};
+ std::vector<int16_t> v_i16{1, 2, 3};
+ std::vector<int32_t> v_i32{1, 2, 3};
+ std::vector<int64_t> v_i64{1, 2, 3};
+ std::vector<uint8_t> v_u8{1, 2, 3};
+ std::vector<uint16_t> v_u16{1, 2, 3};
+ std::vector<uint32_t> v_u32{1, 2, 3};
+ std::vector<uint64_t> v_u64{1, 2, 3};
+ std::vector<float> v_f{1, 2, 3};
+ std::vector<double> v_d{1, 2, 3};
+ std::vector<flatbuffers::Offset<flatbuffers::String>> v_str{
+ fbb.CreateString("foo"), fbb.CreateString("bar"),
+ fbb.CreateString("baz")};
+ std::vector<std::underlying_type_t<ByteEnum>> v_ei8{
+ internal::ByteEnum_First, internal::ByteEnum_Second};
+ std::vector<std::underlying_type_t<ShortEnum>> v_ei16{
+ internal::ShortEnum_First, internal::ShortEnum_Second};
+ std::vector<std::underlying_type_t<IntEnum>> v_ei32{internal::IntEnum_First,
+ internal::IntEnum_Second};
+ std::vector<std::underlying_type_t<LongEnum>> v_ei64{
+ internal::LongEnum_First, internal::LongEnum_Second};
+ std::vector<std::underlying_type_t<UByteEnum>> v_eu8{
+ internal::UByteEnum_First, internal::UByteEnum_Second};
+ std::vector<std::underlying_type_t<UShortEnum>> v_eu16{
+ internal::UShortEnum_First, internal::UShortEnum_Second};
+ std::vector<std::underlying_type_t<UIntEnum>> v_eu32{
+ internal::UIntEnum_First, internal::UIntEnum_Second};
+ std::vector<std::underlying_type_t<ULongEnum>> v_eu64{
+ internal::ULongEnum_First, internal::ULongEnum_Second};
+ std::vector<flatbuffers::Offset<BoolTable>> v_t{bool_table_offset};
+
auto table_offset =
internal::CreateDefaultTableDirect(fbb,
/*b=*/true,
@@ -135,7 +219,28 @@
/*eu16=*/internal::UShortEnum_Second,
/*eu32=*/internal::UIntEnum_Second,
/*eu64=*/internal::ULongEnum_Second,
- /*t=*/bool_table_offset);
+ /*t=*/bool_table_offset,
+ /*v_b=*/&v_b,
+ /*v_i8=*/&v_i8,
+ /*v_i16=*/&v_i16,
+ /*v_i32=*/&v_i32,
+ /*v_i64=*/&v_i64,
+ /*v_u8=*/&v_u8,
+ /*v_u16=*/&v_u16,
+ /*v_u32=*/&v_u32,
+ /*v_u64=*/&v_u64,
+ /*v_f=*/&v_f,
+ /*v_d=*/&v_d,
+ /*v_str=*/&v_str,
+ /*v_ei8=*/&v_ei8,
+ /*v_ei16=*/&v_ei16,
+ /*v_ei32=*/&v_ei32,
+ /*v_ei64=*/&v_ei64,
+ /*v_eu8=*/&v_eu8,
+ /*v_eu16=*/&v_eu16,
+ /*v_eu32=*/&v_eu32,
+ /*v_eu64=*/&v_eu64,
+ /*v_t=*/&v_t);
fbb.Finish(table_offset);
return flatbuffers::GetRoot<DefaultTable>(fbb.GetBufferPointer());
}
@@ -281,6 +386,96 @@
EXPECT_EQ(new_table->eu64(), internal::ULongEnum_Second);
ASSERT_THAT(new_table->t(), NotNull());
EXPECT_EQ(new_table->t()->b(), true);
+ ASSERT_THAT(new_table->v_b(), NotNull());
+ EXPECT_EQ(new_table->v_b()->size(), 2);
+ EXPECT_EQ(new_table->v_b()->Get(0), true);
+ EXPECT_EQ(new_table->v_b()->Get(1), false);
+ ASSERT_THAT(new_table->v_i8(), NotNull());
+ EXPECT_EQ(new_table->v_i8()->size(), 3);
+ EXPECT_EQ(new_table->v_i8()->Get(0), 1);
+ EXPECT_EQ(new_table->v_i8()->Get(1), 2);
+ EXPECT_EQ(new_table->v_i8()->Get(2), 3);
+ ASSERT_THAT(new_table->v_i16(), NotNull());
+ EXPECT_EQ(new_table->v_i16()->size(), 3);
+ EXPECT_EQ(new_table->v_i16()->Get(0), 1);
+ EXPECT_EQ(new_table->v_i16()->Get(1), 2);
+ EXPECT_EQ(new_table->v_i16()->Get(2), 3);
+ ASSERT_THAT(new_table->v_i32(), NotNull());
+ EXPECT_EQ(new_table->v_i32()->size(), 3);
+ EXPECT_EQ(new_table->v_i32()->Get(0), 1);
+ EXPECT_EQ(new_table->v_i32()->Get(1), 2);
+ EXPECT_EQ(new_table->v_i32()->Get(2), 3);
+ ASSERT_THAT(new_table->v_i64(), NotNull());
+ EXPECT_EQ(new_table->v_i64()->size(), 3);
+ EXPECT_EQ(new_table->v_i64()->Get(0), 1);
+ EXPECT_EQ(new_table->v_i64()->Get(1), 2);
+ EXPECT_EQ(new_table->v_i64()->Get(2), 3);
+ ASSERT_THAT(new_table->v_u8(), NotNull());
+ EXPECT_EQ(new_table->v_u8()->size(), 3);
+ EXPECT_EQ(new_table->v_u8()->Get(0), 1);
+ EXPECT_EQ(new_table->v_u8()->Get(1), 2);
+ EXPECT_EQ(new_table->v_u8()->Get(2), 3);
+ ASSERT_THAT(new_table->v_u16(), NotNull());
+ EXPECT_EQ(new_table->v_u16()->size(), 3);
+ EXPECT_EQ(new_table->v_u16()->Get(0), 1);
+ EXPECT_EQ(new_table->v_u16()->Get(1), 2);
+ EXPECT_EQ(new_table->v_u16()->Get(2), 3);
+ ASSERT_THAT(new_table->v_u32(), NotNull());
+ EXPECT_EQ(new_table->v_u32()->size(), 3);
+ EXPECT_EQ(new_table->v_u32()->Get(0), 1);
+ EXPECT_EQ(new_table->v_u32()->Get(1), 2);
+ EXPECT_EQ(new_table->v_u32()->Get(2), 3);
+ ASSERT_THAT(new_table->v_u64(), NotNull());
+ EXPECT_EQ(new_table->v_u64()->size(), 3);
+ EXPECT_EQ(new_table->v_u64()->Get(0), 1);
+ EXPECT_EQ(new_table->v_u64()->Get(1), 2);
+ EXPECT_EQ(new_table->v_u64()->Get(2), 3);
+ ASSERT_THAT(new_table->v_f(), NotNull());
+ EXPECT_EQ(new_table->v_f()->size(), 3);
+ EXPECT_EQ(new_table->v_f()->Get(0), 1);
+ EXPECT_EQ(new_table->v_f()->Get(1), 2);
+ EXPECT_EQ(new_table->v_f()->Get(2), 3);
+ ASSERT_THAT(new_table->v_d(), NotNull());
+ EXPECT_EQ(new_table->v_d()->size(), 3);
+ EXPECT_EQ(new_table->v_d()->Get(0), 1);
+ EXPECT_EQ(new_table->v_d()->Get(1), 2);
+ EXPECT_EQ(new_table->v_d()->Get(2), 3);
+ EXPECT_EQ(new_table->v_str()->size(), 3);
+ EXPECT_EQ(new_table->v_str()->Get(0)->str(), "foo");
+ EXPECT_EQ(new_table->v_str()->Get(1)->str(), "bar");
+ EXPECT_EQ(new_table->v_str()->Get(2)->str(), "baz");
+ ASSERT_THAT(new_table->v_ei8(), NotNull());
+ EXPECT_EQ(new_table->v_ei8()->size(), 2);
+ EXPECT_EQ(new_table->v_ei8()->Get(0), internal::ByteEnum_First);
+ EXPECT_EQ(new_table->v_ei8()->Get(1), internal::ByteEnum_Second);
+ ASSERT_THAT(new_table->v_ei16(), NotNull());
+ EXPECT_EQ(new_table->v_ei16()->size(), 2);
+ EXPECT_EQ(new_table->v_ei16()->Get(0), internal::ShortEnum_First);
+ EXPECT_EQ(new_table->v_ei16()->Get(1), internal::ShortEnum_Second);
+ ASSERT_THAT(new_table->v_ei32(), NotNull());
+ EXPECT_EQ(new_table->v_ei32()->size(), 2);
+ EXPECT_EQ(new_table->v_ei32()->Get(0), internal::IntEnum_First);
+ EXPECT_EQ(new_table->v_ei32()->Get(1), internal::IntEnum_Second);
+ ASSERT_THAT(new_table->v_ei64(), NotNull());
+ EXPECT_EQ(new_table->v_ei64()->size(), 2);
+ EXPECT_EQ(new_table->v_ei64()->Get(0), internal::LongEnum_First);
+ EXPECT_EQ(new_table->v_ei64()->Get(1), internal::LongEnum_Second);
+ ASSERT_THAT(new_table->v_eu8(), NotNull());
+ EXPECT_EQ(new_table->v_eu8()->size(), 2);
+ EXPECT_EQ(new_table->v_eu8()->Get(0), internal::UByteEnum_First);
+ EXPECT_EQ(new_table->v_eu8()->Get(1), internal::UByteEnum_Second);
+ ASSERT_THAT(new_table->v_eu16(), NotNull());
+ EXPECT_EQ(new_table->v_eu16()->size(), 2);
+ EXPECT_EQ(new_table->v_eu16()->Get(0), internal::UShortEnum_First);
+ EXPECT_EQ(new_table->v_eu16()->Get(1), internal::UShortEnum_Second);
+ ASSERT_THAT(new_table->v_eu32(), NotNull());
+ EXPECT_EQ(new_table->v_eu32()->size(), 2);
+ EXPECT_EQ(new_table->v_eu32()->Get(0), internal::UIntEnum_First);
+ EXPECT_EQ(new_table->v_eu32()->Get(1), internal::UIntEnum_Second);
+ ASSERT_THAT(new_table->v_t(), NotNull());
+ EXPECT_EQ(new_table->v_t()->size(), 1);
+ ASSERT_THAT(new_table->v_t()->Get(0), NotNull());
+ EXPECT_EQ(new_table->v_t()->Get(0)->b(), true);
}
TEST(FlatbuffersTableDomainImplTest, InitGeneratesSeeds) {
@@ -300,12 +495,17 @@
TEST(FlatbuffersTableDomainImplTest, CanMutateAnyTableField) {
absl::flat_hash_map<std::string, bool> mutated_fields{
- {"b", false}, {"i8", false}, {"i16", false}, {"i32", false},
- {"i64", false}, {"u8", false}, {"u16", false}, {"u32", false},
- {"u64", false}, {"f", false}, {"d", false}, {"str", false},
- {"ei8", false}, {"ei16", false}, {"ei32", false}, {"ei64", false},
- {"eu8", false}, {"eu16", false}, {"eu32", false}, {"eu64", false},
- {"t", false},
+ {"b", false}, {"i8", false}, {"i16", false}, {"i32", false},
+ {"i64", false}, {"u8", false}, {"u16", false}, {"u32", false},
+ {"u64", false}, {"f", false}, {"d", false}, {"str", false},
+ {"ei8", false}, {"ei16", false}, {"ei32", false}, {"ei64", false},
+ {"eu8", false}, {"eu16", false}, {"eu32", false}, {"eu64", false},
+ {"t", false}, {"v_b", false}, {"v_i8", false}, {"v_i16", false},
+ {"v_i32", false}, {"v_i64", false}, {"v_u8", false}, {"v_u16", false},
+ {"v_u32", false}, {"v_u64", false}, {"v_f", false}, {"v_d", false},
+ {"v_str", false}, {"v_ei8", false}, {"v_ei16", false}, {"v_ei32", false},
+ {"v_ei64", false}, {"v_eu8", false}, {"v_eu16", false}, {"v_eu32", false},
+ {"v_eu64", false}, {"v_t", false},
};
auto domain = Arbitrary<const DefaultTable*>();
@@ -341,6 +541,27 @@
mutated_fields["eu32"] |= mut->eu32() != init->eu32();
mutated_fields["eu64"] |= mut->eu64() != init->eu64();
mutated_fields["t"] |= !Eq(mut->t(), init->t());
+ mutated_fields["v_b"] |= !VectorEq(mut->v_b(), init->v_b());
+ mutated_fields["v_i8"] |= !VectorEq(mut->v_i8(), init->v_i8());
+ mutated_fields["v_i16"] |= !VectorEq(mut->v_i16(), init->v_i16());
+ mutated_fields["v_i32"] |= !VectorEq(mut->v_i32(), init->v_i32());
+ mutated_fields["v_i64"] |= !VectorEq(mut->v_i64(), init->v_i64());
+ mutated_fields["v_u8"] |= !VectorEq(mut->v_u8(), init->v_u8());
+ mutated_fields["v_u16"] |= !VectorEq(mut->v_u16(), init->v_u16());
+ mutated_fields["v_u32"] |= !VectorEq(mut->v_u32(), init->v_u32());
+ mutated_fields["v_u64"] |= !VectorEq(mut->v_u64(), init->v_u64());
+ mutated_fields["v_f"] |= !VectorEq(mut->v_f(), init->v_f());
+ mutated_fields["v_d"] |= !VectorEq(mut->v_d(), init->v_d());
+ mutated_fields["v_str"] |= !VectorEq(mut->v_str(), init->v_str());
+ mutated_fields["v_ei8"] |= !VectorEq(mut->v_ei8(), init->v_ei8());
+ mutated_fields["v_ei16"] |= !VectorEq(mut->v_ei16(), init->v_ei16());
+ mutated_fields["v_ei32"] |= !VectorEq(mut->v_ei32(), init->v_ei32());
+ mutated_fields["v_ei64"] |= !VectorEq(mut->v_ei64(), init->v_ei64());
+ mutated_fields["v_eu8"] |= !VectorEq(mut->v_eu8(), init->v_eu8());
+ mutated_fields["v_eu16"] |= !VectorEq(mut->v_eu16(), init->v_eu16());
+ mutated_fields["v_eu32"] |= !VectorEq(mut->v_eu32(), init->v_eu32());
+ mutated_fields["v_eu64"] |= !VectorEq(mut->v_eu64(), init->v_eu64());
+ mutated_fields["v_t"] |= !VectorEq(mut->v_str(), init->v_str());
if (std::all_of(mutated_fields.begin(), mutated_fields.end(),
[](const auto& p) { return p.second; })) {
@@ -354,6 +575,28 @@
TEST(FlatbuffersTableDomainImplTest, OptionalTableEventuallyBecomeEmpty) {
flatbuffers::FlatBufferBuilder fbb;
auto bool_table_offset = internal::CreateBoolTable(fbb, true);
+ std::vector<uint8_t> v_b{true, false};
+ std::vector<int8_t> v_i8{};
+ std::vector<int16_t> v_i16{};
+ std::vector<int32_t> v_i32{};
+ std::vector<int64_t> v_i64{};
+ std::vector<uint8_t> v_u8{};
+ std::vector<uint16_t> v_u16{};
+ std::vector<uint32_t> v_u32{};
+ std::vector<uint64_t> v_u64{};
+ std::vector<float> v_f{};
+ std::vector<double> v_d{};
+ std::vector<flatbuffers::Offset<flatbuffers::String>> v_str{
+ fbb.CreateString(""), fbb.CreateString(""), fbb.CreateString("")};
+ std::vector<std::underlying_type_t<ByteEnum>> v_ei8{};
+ std::vector<std::underlying_type_t<ShortEnum>> v_ei16{};
+ std::vector<std::underlying_type_t<IntEnum>> v_ei32{};
+ std::vector<std::underlying_type_t<LongEnum>> v_ei64{};
+ std::vector<std::underlying_type_t<UByteEnum>> v_eu8{};
+ std::vector<std::underlying_type_t<UShortEnum>> v_eu16{};
+ std::vector<std::underlying_type_t<UIntEnum>> v_eu32{};
+ std::vector<std::underlying_type_t<ULongEnum>> v_eu64{};
+ std::vector<flatbuffers::Offset<BoolTable>> v_t{};
auto table_offset =
internal::CreateOptionalTableDirect(fbb,
true, // b
@@ -376,7 +619,28 @@
internal::UShortEnum_Second, // eu16
internal::UIntEnum_Second, // eu32
internal::ULongEnum_Second, // eu64
- bool_table_offset // t
+ bool_table_offset, // t
+ &v_b, // v_b
+ &v_i8, // v_i8
+ &v_i16, // v_i16
+ &v_i32, // v_i32
+ &v_i64, // v_i64
+ &v_u8, // v_u8
+ &v_u16, // v_u16
+ &v_u32, // v_u32
+ &v_u64, // v_u64
+ &v_f, // v_f
+ &v_d, // v_d
+ &v_str, // v_str
+ &v_ei8, // v_ei8
+ &v_ei16, // v_ei16
+ &v_ei32, // v_ei32
+ &v_ei64, // v_ei64
+ &v_eu8, // v_eu8
+ &v_eu16, // v_eu16
+ &v_eu32, // v_eu32
+ &v_eu64, // v_eu64
+ &v_t // v_t
);
fbb.Finish(table_offset);
auto table = flatbuffers::GetRoot<OptionalTable>(fbb.GetBufferPointer());
@@ -386,12 +650,17 @@
absl::BitGen bitgen;
absl::flat_hash_map<std::string, bool> null_fields{
- {"b", false}, {"i8", false}, {"i16", false}, {"i32", false},
- {"i64", false}, {"u8", false}, {"u16", false}, {"u32", false},
- {"u64", false}, {"f", false}, {"d", false}, {"str", false},
- {"ei8", false}, {"ei16", false}, {"ei32", false}, {"ei64", false},
- {"eu8", false}, {"eu16", false}, {"eu32", false}, {"eu64", false},
- {"t", false},
+ {"b", false}, {"i8", false}, {"i16", false}, {"i32", false},
+ {"i64", false}, {"u8", false}, {"u16", false}, {"u32", false},
+ {"u64", false}, {"f", false}, {"d", false}, {"str", false},
+ {"ei8", false}, {"ei16", false}, {"ei32", false}, {"ei64", false},
+ {"eu8", false}, {"eu16", false}, {"eu32", false}, {"eu64", false},
+ {"t", false}, {"v_b", false}, {"v_i8", false}, {"v_i16", false},
+ {"v_i32", false}, {"v_i64", false}, {"v_u8", false}, {"v_u16", false},
+ {"v_u32", false}, {"v_u64", false}, {"v_f", false}, {"v_d", false},
+ {"v_str", false}, {"v_ei8", false}, {"v_ei16", false}, {"v_ei32", false},
+ {"v_ei64", false}, {"v_eu8", false}, {"v_eu16", false}, {"v_eu32", false},
+ {"v_eu64", false}, {"v_t", false},
};
// Optional fields are mutated to null with probability 1/100.
@@ -422,6 +691,27 @@
null_fields["eu32"] |= !v->eu32().has_value();
null_fields["eu64"] |= !v->eu64().has_value();
null_fields["t"] |= v->t() == nullptr;
+ null_fields["v_b"] |= v->v_b() == nullptr;
+ null_fields["v_i8"] |= v->v_i8() == nullptr;
+ null_fields["v_i16"] |= v->v_i16() == nullptr;
+ null_fields["v_i32"] |= v->v_i32() == nullptr;
+ null_fields["v_i64"] |= v->v_i64() == nullptr;
+ null_fields["v_u8"] |= v->v_u8() == nullptr;
+ null_fields["v_u16"] |= v->v_u16() == nullptr;
+ null_fields["v_u32"] |= v->v_u32() == nullptr;
+ null_fields["v_u64"] |= v->v_u64() == nullptr;
+ null_fields["v_f"] |= v->v_f() == nullptr;
+ null_fields["v_d"] |= v->v_d() == nullptr;
+ null_fields["v_str"] |= v->v_str() == nullptr;
+ null_fields["v_ei8"] |= v->v_ei8() == nullptr;
+ null_fields["v_ei16"] |= v->v_ei16() == nullptr;
+ null_fields["v_ei32"] |= v->v_ei32() == nullptr;
+ null_fields["v_ei64"] |= v->v_ei64() == nullptr;
+ null_fields["v_eu8"] |= v->v_eu8() == nullptr;
+ null_fields["v_eu16"] |= v->v_eu16() == nullptr;
+ null_fields["v_eu32"] |= v->v_eu32() == nullptr;
+ null_fields["v_eu64"] |= v->v_eu64() == nullptr;
+ null_fields["v_t"] |= v->v_t() == nullptr;
if (std::all_of(null_fields.begin(), null_fields.end(),
[](const auto& p) { return p.second; })) {
@@ -460,39 +750,55 @@
printer.PrintCorpusValue(*corpus, &out,
domain_implementor::PrintMode::kHumanReadable);
- EXPECT_THAT(out, AllOf(HasSubstr("b: (true)"), // b
- HasSubstr("i8: (1)"), // i8
- HasSubstr("i16: (2)"), // i16
- HasSubstr("i32: (3)"), // i32
- HasSubstr("i64: (4)"), // i64
- HasSubstr("u8: (5)"), // u8
- HasSubstr("u16: (6)"), // u16
- HasSubstr("u32: (7)"), // u32
- HasSubstr("u64: (8)"), // u64
- HasSubstr("f: (9.f)"), // f
- HasSubstr("d: (10.)"), // d
- HasSubstr("str: (\"foo bar baz\")"), // str
- HasSubstr("ei8: (Second)"), // ei8
- HasSubstr("ei16: (Second)"), // ei16
- HasSubstr("ei32: (Second)"), // ei32
- HasSubstr("ei64: (Second)"), // ei64
- HasSubstr("eu8: (Second)"), // eu8
- HasSubstr("eu16: (Second)"), // eu16
- HasSubstr("eu32: (Second)"), // eu32
- HasSubstr("eu64: (Second)"), // eu64
- HasSubstr("t: ({b: (true)})") // t
- ));
+ EXPECT_THAT(out,
+ AllOf(HasSubstr("b: (true)"), // b
+ HasSubstr("i8: (1)"), // i8
+ HasSubstr("i16: (2)"), // i16
+ HasSubstr("i32: (3)"), // i32
+ HasSubstr("i64: (4)"), // i64
+ HasSubstr("u8: (5)"), // u8
+ HasSubstr("u16: (6)"), // u16
+ HasSubstr("u32: (7)"), // u32
+ HasSubstr("u64: (8)"), // u64
+ HasSubstr("f: (9.f)"), // f
+ HasSubstr("d: (10.)"), // d
+ HasSubstr("str: (\"foo bar baz\")"), // str
+ HasSubstr("ei8: (Second)"), // ei8
+ HasSubstr("ei16: (Second)"), // ei16
+ HasSubstr("ei32: (Second)"), // ei32
+ HasSubstr("ei64: (Second)"), // ei64
+ HasSubstr("eu8: (Second)"), // eu8
+ HasSubstr("eu16: (Second)"), // eu16
+ HasSubstr("eu32: (Second)"), // eu32
+ HasSubstr("eu64: (Second)"), // eu64
+ HasSubstr("t: ({b: (true)})"), // t
+ HasSubstr("v_b: ({true, false})"), // v_b
+ HasSubstr("v_i8: ({1, 2, 3})"), // v_i8
+ HasSubstr("v_i16: ({1, 2, 3})"), // v_i16
+ HasSubstr("v_i32: ({1, 2, 3})"), // v_i32
+ HasSubstr("v_i64: ({1, 2, 3})"), // v_i64
+ HasSubstr("v_u8: ({1, 2, 3})"), // v_u8
+ HasSubstr("v_u16: ({1, 2, 3})"), // v_u16
+ HasSubstr("v_u32: ({1, 2, 3})"), // v_u32
+ HasSubstr("v_u64: ({1, 2, 3})"), // v_u64
+ HasSubstr("v_f: ({1.f, 2.f, 3.f})"), // v_f
+ HasSubstr("v_d: ({1., 2., 3.})"), // v_d
+ HasSubstr("v_str: ({\"foo\", \"bar\", \"baz\"})"), // v_str
+ HasSubstr("v_ei8: ({First, Second})"), // v_ei8
+ HasSubstr("v_ei16: ({First, Second})"), // v_ei16
+ HasSubstr("v_ei32: ({First, Second})"), // v_ei32
+ HasSubstr("v_ei64: ({First, Second})"), // v_ei64
+ HasSubstr("v_eu8: ({First, Second})"), // v_eu8
+ HasSubstr("v_eu16: ({First, Second})"), // v_eu16
+ HasSubstr("v_eu32: ({First, Second})"), // v_eu32
+ HasSubstr("v_eu64: ({First, Second})"), // v_eu64
+ HasSubstr("v_t: ({{b: (true)}})") // v_t
+ ));
}
TEST(FlatbuffersTableDomainImplTest, UnsupportedTypesRemainNull) {
absl::flat_hash_map<std::string, bool> null_fields{
- {"u", true}, {"s", true}, {"v_b", true}, {"v_i8", true},
- {"v_i16", true}, {"v_i32", true}, {"v_i64", true}, {"v_u8", true},
- {"v_u16", true}, {"v_u32", true}, {"v_u64", true}, {"v_f", true},
- {"v_d", true}, {"v_str", true}, {"v_ei8", true}, {"v_ei16", true},
- {"v_ei32", true}, {"v_ei64", true}, {"v_eu8", true}, {"v_eu16", true},
- {"v_eu32", true}, {"v_eu64", true}, {"v_t", true}, {"v_u", true},
- {"v_s", true}};
+ {"u", true}, {"s", true}, {"v_u", true}, {"v_s", true}};
auto domain = Arbitrary<const UnsupportedTypesTable*>();
@@ -506,27 +812,6 @@
null_fields["u"] &= mut->u() == nullptr;
null_fields["s"] &= mut->s() == nullptr;
- null_fields["v_b"] &= mut->v_b() == nullptr;
- null_fields["v_i8"] &= mut->v_i8() == nullptr;
- null_fields["v_i16"] &= mut->v_i16() == nullptr;
- null_fields["v_i32"] &= mut->v_i32() == nullptr;
- null_fields["v_i64"] &= mut->v_i64() == nullptr;
- null_fields["v_u8"] &= mut->v_u8() == nullptr;
- null_fields["v_u16"] &= mut->v_u16() == nullptr;
- null_fields["v_u32"] &= mut->v_u32() == nullptr;
- null_fields["v_u64"] &= mut->v_u64() == nullptr;
- null_fields["v_f"] &= mut->v_f() == nullptr;
- null_fields["v_d"] &= mut->v_d() == nullptr;
- null_fields["v_str"] &= mut->v_str() == nullptr;
- null_fields["v_ei8"] &= mut->v_ei8() == nullptr;
- null_fields["v_ei16"] &= mut->v_ei16() == nullptr;
- null_fields["v_ei32"] &= mut->v_ei32() == nullptr;
- null_fields["v_ei64"] &= mut->v_ei64() == nullptr;
- null_fields["v_eu8"] &= mut->v_eu8() == nullptr;
- null_fields["v_eu16"] &= mut->v_eu16() == nullptr;
- null_fields["v_eu32"] &= mut->v_eu32() == nullptr;
- null_fields["v_eu64"] &= mut->v_eu64() == nullptr;
- null_fields["v_t"] &= mut->v_t() == nullptr;
null_fields["v_u"] &= mut->v_u() == nullptr;
null_fields["v_s"] &= mut->v_s() == nullptr;
@@ -573,7 +858,7 @@
auto domain = Arbitrary<const OptionalTable*>();
auto corpus = domain.FromValue(table);
ASSERT_TRUE(corpus.has_value());
- EXPECT_EQ(domain.CountNumberOfFields(corpus.value()), 21);
+ EXPECT_EQ(domain.CountNumberOfFields(corpus.value()), 42);
}
TEST(FlatbuffersTableDomainImplTest, RecursiveTable) {
diff --git a/fuzztest/internal/domains/flatbuffers_domain_impl.cc b/fuzztest/internal/domains/flatbuffers_domain_impl.cc
index 1a94bbf..51b29bb 100644
--- a/fuzztest/internal/domains/flatbuffers_domain_impl.cc
+++ b/fuzztest/internal/domains/flatbuffers_domain_impl.cc
@@ -137,7 +137,9 @@
}
field_counter++;
- if (field->type()->base_type() == reflection::BaseType::Obj) {
+ auto base_type = field->type()->base_type();
+
+ if (base_type == reflection::BaseType::Obj) {
auto sub_object = schema_->objects()->Get(field->type()->index());
if (!sub_object->is_struct()) {
field_counter +=
@@ -148,6 +150,21 @@
// TODO: Add support for structs.
}
+ if (base_type == reflection::BaseType::Vector ||
+ base_type == reflection::BaseType::Vector64) {
+ auto elem_type = field->type()->element();
+ if (elem_type == reflection::BaseType::Obj) {
+ auto sub_object = schema_->objects()->Get(field->type()->index());
+ if (!sub_object->is_struct()) {
+ field_counter +=
+ GetCachedDomain<FlatbuffersVectorTag<FlatbuffersTableTag>>(field)
+ .MutateSelectedField(val[field->id()], prng, metadata,
+ only_shrink,
+ selected_field_index - field_counter);
+ }
+ }
+ }
+
if (field_counter > selected_field_index) {
return field_counter;
}
@@ -266,6 +283,16 @@
auto sub_object = schema_->objects()->Get(field->type()->index());
return !sub_object->is_struct();
};
+ if (base_type == reflection::BaseType::Vector ||
+ base_type == reflection::BaseType::Vector64) {
+ auto elem_type = field->type()->element();
+ if (flatbuffers::IsScalar(elem_type)) return true;
+ if (elem_type == reflection::BaseType::String) return true;
+ if (elem_type == reflection::BaseType::Obj) {
+ auto sub_object = schema_->objects()->Get(field->type()->index());
+ return !sub_object->is_struct();
+ }
+ }
return false;
}
diff --git a/fuzztest/internal/domains/flatbuffers_domain_impl.h b/fuzztest/internal/domains/flatbuffers_domain_impl.h
index 23f6bd6..eacb756 100644
--- a/fuzztest/internal/domains/flatbuffers_domain_impl.h
+++ b/fuzztest/internal/domains/flatbuffers_domain_impl.h
@@ -20,6 +20,7 @@
#include <cstdint>
#include <initializer_list>
#include <limits>
+#include <list>
#include <optional>
#include <string>
#include <type_traits>
@@ -42,10 +43,12 @@
#include "flatbuffers/reflection_generated.h"
#include "flatbuffers/string.h"
#include "flatbuffers/table.h"
+#include "flatbuffers/vector.h"
#include "flatbuffers/verifier.h"
#include "./fuzztest/domain_core.h"
#include "./fuzztest/internal/any.h"
#include "./fuzztest/internal/domains/arbitrary_impl.h"
+#include "./fuzztest/internal/domains/container_of_impl.h"
#include "./fuzztest/internal/domains/domain_base.h"
#include "./fuzztest/internal/domains/domain_type_erasure.h"
#include "./fuzztest/internal/domains/element_of_impl.h"
@@ -53,6 +56,7 @@
#include "./fuzztest/internal/meta.h"
#include "./fuzztest/internal/serialization.h"
#include "./fuzztest/internal/status.h"
+#include "./fuzztest/internal/type_support.h"
namespace fuzztest::internal {
@@ -77,11 +81,141 @@
inline constexpr bool is_flatbuffers_enum_tag_v =
is_flatbuffers_enum_tag<T>::value;
+//
+// Flatbuffers vector detection.
+//
+template <typename T>
+struct FlatbuffersVectorTag {
+ using value_type = T;
+};
+
+template <typename T>
+struct is_flatbuffers_vector_tag : std::false_type {};
+
+template <typename T>
+struct is_flatbuffers_vector_tag<FlatbuffersVectorTag<T>> : std::true_type {};
+
+template <typename T>
+inline constexpr bool is_flatbuffers_vector_tag_v =
+ is_flatbuffers_vector_tag<T>::value;
+
struct FlatbuffersArrayTag;
struct FlatbuffersTableTag;
struct FlatbuffersStructTag;
struct FlatbuffersUnionTag;
-struct FlatbuffersVectorTag;
+
+// Dynamic to static dispatch visitor pattern for flatbuffers vector elements.
+template <typename Visitor>
+auto VisitFlatbufferVectorElementField(const reflection::Schema* schema,
+ const reflection::Field* field,
+ Visitor visitor) {
+ auto field_index = field->type()->index();
+ auto element_type = field->type()->element();
+ switch (element_type) {
+ case reflection::BaseType::Bool:
+ visitor.template Visit<FlatbuffersVectorTag<bool>>(field);
+ break;
+ case reflection::BaseType::Byte:
+ if (field_index >= 0) {
+ visitor
+ .template Visit<FlatbuffersVectorTag<FlatbuffersEnumTag<int8_t>>>(
+ field);
+ } else {
+ visitor.template Visit<FlatbuffersVectorTag<int8_t>>(field);
+ }
+ break;
+ case reflection::BaseType::Short:
+ if (field_index >= 0) {
+ visitor
+ .template Visit<FlatbuffersVectorTag<FlatbuffersEnumTag<int16_t>>>(
+ field);
+ } else {
+ visitor.template Visit<FlatbuffersVectorTag<int16_t>>(field);
+ }
+ break;
+ case reflection::BaseType::Int:
+ if (field_index >= 0) {
+ visitor
+ .template Visit<FlatbuffersVectorTag<FlatbuffersEnumTag<int32_t>>>(
+ field);
+ } else {
+ visitor.template Visit<FlatbuffersVectorTag<int32_t>>(field);
+ }
+ break;
+ case reflection::BaseType::Long:
+ if (field_index >= 0) {
+ visitor
+ .template Visit<FlatbuffersVectorTag<FlatbuffersEnumTag<int64_t>>>(
+ field);
+ } else {
+ visitor.template Visit<FlatbuffersVectorTag<int64_t>>(field);
+ }
+ break;
+ case reflection::BaseType::UByte:
+ if (field_index >= 0) {
+ visitor
+ .template Visit<FlatbuffersVectorTag<FlatbuffersEnumTag<uint8_t>>>(
+ field);
+ } else {
+ visitor.template Visit<FlatbuffersVectorTag<uint8_t>>(field);
+ }
+ break;
+ case reflection::BaseType::UShort:
+ if (field_index >= 0) {
+ visitor
+ .template Visit<FlatbuffersVectorTag<FlatbuffersEnumTag<uint16_t>>>(
+ field);
+ } else {
+ visitor.template Visit<FlatbuffersVectorTag<uint16_t>>(field);
+ }
+ break;
+ case reflection::BaseType::UInt:
+ if (field_index >= 0) {
+ visitor
+ .template Visit<FlatbuffersVectorTag<FlatbuffersEnumTag<uint32_t>>>(
+ field);
+ } else {
+ visitor.template Visit<FlatbuffersVectorTag<uint32_t>>(field);
+ }
+ break;
+ case reflection::BaseType::ULong:
+ if (field_index >= 0) {
+ visitor
+ .template Visit<FlatbuffersVectorTag<FlatbuffersEnumTag<uint64_t>>>(
+ field);
+ } else {
+ visitor.template Visit<FlatbuffersVectorTag<uint64_t>>(field);
+ }
+ break;
+ case reflection::BaseType::Float:
+ visitor.template Visit<FlatbuffersVectorTag<float>>(field);
+ break;
+ case reflection::BaseType::Double:
+ visitor.template Visit<FlatbuffersVectorTag<double>>(field);
+ break;
+ case reflection::BaseType::String:
+ visitor.template Visit<FlatbuffersVectorTag<std::string>>(field);
+ break;
+ case reflection::BaseType::Obj: {
+ auto sub_object = schema->objects()->Get(field_index);
+ if (sub_object->is_struct()) {
+ visitor.template Visit<FlatbuffersVectorTag<FlatbuffersStructTag>>(
+ field);
+ } else {
+ visitor.template Visit<FlatbuffersVectorTag<FlatbuffersTableTag>>(
+ field);
+ }
+ break;
+ }
+ case reflection::BaseType::Union:
+ visitor.template Visit<FlatbuffersVectorTag<FlatbuffersUnionTag>>(field);
+ break;
+ case reflection::BaseType::UType:
+ break;
+ default: // Vector of vectors and vector of arrays are not supported.
+ FUZZTEST_INTERNAL_CHECK(false, "Unsupported vector base type");
+ }
+}
// Dynamic to static dispatch visitor pattern.
template <typename Visitor>
@@ -160,7 +294,7 @@
break;
case reflection::BaseType::Vector:
case reflection::BaseType::Vector64:
- visitor.template Visit<FlatbuffersVectorTag>(field);
+ VisitFlatbufferVectorElementField<Visitor>(schema, field, visitor);
break;
case reflection::BaseType::Array:
visitor.template Visit<FlatbuffersArrayTag>(field);
@@ -297,7 +431,7 @@
// Domain implementation for flatbuffers untyped tables.
// The corpus type is a map of field ids to field values.
class FlatbuffersTableUntypedDomainImpl
- : public fuzztest::domain_implementor::DomainBase<
+ : public domain_implementor::DomainBase<
/*Derived=*/FlatbuffersTableUntypedDomainImpl,
/*ValueType=*/const flatbuffers::Table* absl_nonnull,
/*CorpusType=*/
@@ -462,6 +596,56 @@
"Field must be a table type.");
inner_value =
user_value->GetPointer<const flatbuffers::Table*>(field->offset());
+ } else if constexpr (is_flatbuffers_vector_tag_v<T>) {
+ FUZZTEST_INTERNAL_CHECK(base_type == reflection::BaseType::Vector ||
+ base_type == reflection::BaseType::Vector64,
+ "Field must be a vector type.");
+ if (!user_value->CheckField(field->offset())) {
+ inner_value = std::nullopt;
+ } else {
+ using ElementType = typename T::value_type;
+ if constexpr (std::is_integral_v<ElementType> ||
+ std::is_floating_point_v<ElementType>) {
+ auto vec =
+ user_value->GetPointer<flatbuffers::Vector<ElementType>*>(
+ field->offset());
+ inner_value = std::optional(std::vector<ElementType>());
+ inner_value->reserve(vec->size());
+ for (auto i = 0; i < vec->size(); ++i) {
+ inner_value->push_back(vec->Get(i));
+ }
+ } else if constexpr (is_flatbuffers_enum_tag_v<ElementType>) {
+ using Underlaying = typename ElementType::type;
+ auto vec =
+ user_value->GetPointer<flatbuffers::Vector<Underlaying>*>(
+ field->offset());
+ inner_value = std::optional(std::vector<Underlaying>());
+ inner_value->reserve(vec->size());
+ for (auto i = 0; i < vec->size(); ++i) {
+ inner_value->push_back(vec->Get(i));
+ }
+ } else if constexpr (std::is_same_v<ElementType, std::string>) {
+ auto vec = user_value->GetPointer<
+ flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>*>(
+ field->offset());
+ inner_value = std::optional(std::vector<std::string>());
+ inner_value->reserve(vec->size());
+ for (auto i = 0; i < vec->size(); ++i) {
+ inner_value->push_back(vec->Get(i)->str());
+ }
+ } else if constexpr (std::is_same_v<ElementType,
+ FlatbuffersTableTag>) {
+ auto vec = user_value->GetPointer<
+ flatbuffers::Vector<flatbuffers::Offset<flatbuffers::Table>>*>(
+ field->offset());
+ inner_value =
+ std::optional(std::vector<const flatbuffers::Table*>());
+ inner_value->reserve(vec->size());
+ for (auto i = 0; i < vec->size(); ++i) {
+ inner_value->push_back(vec->Get(i));
+ }
+ }
+ }
}
auto inner = domain.FromValue(inner_value);
@@ -492,18 +676,69 @@
} else if constexpr (std::is_same_v<T, FlatbuffersTableTag>) {
FlatbuffersTableUntypedDomainImpl inner_domain(
self.schema_, self.schema_->objects()->Get(field->type()->index()));
- auto optional_corpus = corpus_value.GetAs<
- std::variant<std::monostate, fuzztest::GenericDomainCorpusType>>();
- if (std::holds_alternative<fuzztest::GenericDomainCorpusType>(
- optional_corpus)) {
- auto inner_corpus =
- std::get<fuzztest::GenericDomainCorpusType>(optional_corpus)
- .GetAs<corpus_type>();
+ auto optional_corpus =
+ corpus_value
+ .GetAs<std::variant<std::monostate, GenericDomainCorpusType>>();
+ if (std::holds_alternative<GenericDomainCorpusType>(optional_corpus)) {
+ auto inner_corpus = std::get<GenericDomainCorpusType>(optional_corpus)
+ .GetAs<corpus_type>();
auto offset = inner_domain.BuildTable(inner_corpus, builder);
offsets.insert({field->id(), offset});
}
// Else if the variant is std::monostate the optional field is null and
// there is no table to build.
+ } else if constexpr (is_flatbuffers_vector_tag_v<T>) {
+ VisitVector<typename T::value_type>(field,
+ self.GetCachedDomain<T>(field));
+ }
+ }
+
+ private:
+ template <typename Element, typename Domain>
+ void VisitVector(const reflection::Field* field,
+ const Domain& domain) const {
+ if constexpr (std::is_integral_v<Element> ||
+ std::is_floating_point_v<Element>) {
+ auto value = domain.GetValue(corpus_value);
+ if (!value) {
+ return;
+ }
+ offsets.insert({field->id(), builder.CreateVector(*value).o});
+ } else if constexpr (is_flatbuffers_enum_tag_v<Element>) {
+ auto value = domain.GetValue(corpus_value);
+ if (!value) {
+ return;
+ }
+ offsets.insert({field->id(), builder.CreateVector(*value).o});
+ }
+ if constexpr (std::is_same_v<Element, FlatbuffersTableTag>) {
+ FlatbuffersTableUntypedDomainImpl domain(
+ self.schema_, self.schema_->objects()->Get(field->type()->index()));
+ auto opt_corpus =
+ corpus_value
+ .GetAs<std::variant<std::monostate, GenericDomainCorpusType>>();
+ if (std::holds_alternative<std::monostate>(opt_corpus)) {
+ return;
+ }
+ auto container_corpus = std::get<GenericDomainCorpusType>(opt_corpus)
+ .GetAs<std::list<corpus_type>>();
+ std::vector<flatbuffers::Offset<flatbuffers::Table>> vec_offsets;
+ for (auto& inner_corpus : container_corpus) {
+ auto offset = domain.BuildTable(inner_corpus, builder);
+ vec_offsets.push_back(offset);
+ }
+ offsets.insert({field->id(), builder.CreateVector(vec_offsets).o});
+ } else if constexpr (std::is_same_v<Element, std::string>) {
+ auto value = domain.GetValue(corpus_value);
+ if (!value) {
+ return;
+ }
+ std::vector<flatbuffers::Offset<flatbuffers::String>> vec_offsets;
+ for (const auto& str : *value) {
+ auto offset = builder.CreateString(str);
+ vec_offsets.push_back(offset);
+ }
+ offsets.insert({field->id(), builder.CreateVector(vec_offsets).o});
}
}
};
@@ -513,8 +748,8 @@
struct TableBuilderVisitor {
const FlatbuffersTableUntypedDomainImpl& self;
flatbuffers::FlatBufferBuilder& builder;
- const absl::flat_hash_map<typename corpus_type::key_type,
- flatbuffers::uoffset_t>& offsets;
+ absl::flat_hash_map<typename corpus_type::key_type, flatbuffers::uoffset_t>&
+ offsets;
const typename corpus_type::value_type::second_type& corpus_value;
template <typename T>
@@ -528,7 +763,8 @@
}
// Store "inline field" value inline.
builder.AddElement(field->offset(), v.value());
- } else if constexpr (std::is_same_v<T, std::string>) {
+ } else if constexpr (std::is_same_v<T, std::string> ||
+ is_flatbuffers_vector_tag_v<T>) {
// "Out-of-line field". Store just offset.
if (auto it = offsets.find(field->id()); it != offsets.end()) {
builder.AddOffset(
@@ -633,7 +869,6 @@
domain_implementor::RawSink out,
domain_implementor::PrintMode mode) const {
std::vector<typename corpus_type::key_type> field_ids;
- field_ids.reserve(value.size());
for (const auto& [id, _] : value) {
field_ids.push_back(id);
}
@@ -669,7 +904,49 @@
void Visit(const reflection::Field* absl_nonnull field) const {
auto& domain = self.GetCachedDomain<T>(field);
absl::Format(out, "%s: ", field->name()->str());
- domain_implementor::PrintValue(domain, val, out, mode);
+ if constexpr (std::is_same_v<T, FlatbuffersVectorTag<uint8_t>> ||
+ std::is_same_v<
+ T, FlatbuffersVectorTag<FlatbuffersEnumTag<uint8_t>>>) {
+ // Handle the case where the field is a vector<uint8_t> or enum<uint8_t>
+ // since the container domain would try to print it as a string.
+ auto opt_corpus =
+ val.GetAs<std::variant<std::monostate, GenericDomainCorpusType>>();
+ if (std::holds_alternative<GenericDomainCorpusType>(opt_corpus)) {
+ absl::Format(out, "(");
+ if constexpr (std::is_same_v<T, FlatbuffersVectorTag<uint8_t>>) {
+ auto inner_corpus =
+ std::get<GenericDomainCorpusType>(opt_corpus)
+ .GetAs<corpus_type_t<ContainerOfImpl<
+ std::vector<uint8_t>, ArbitraryImpl<uint8_t>>>>();
+ auto inner_domain = Arbitrary<uint8_t>();
+ auto printer = ContainerPrinter<
+ ContainerOfImpl<std::vector<uint8_t>, ArbitraryImpl<uint8_t>>,
+ ArbitraryImpl<uint8_t>>{inner_domain};
+ printer.PrintCorpusValue(inner_corpus, out, mode);
+ } else if constexpr (std::is_same_v<
+ T, FlatbuffersVectorTag<
+ FlatbuffersEnumTag<uint8_t>>>) {
+ auto inner_corpus =
+ std::get<GenericDomainCorpusType>(opt_corpus)
+ .GetAs<corpus_type_t<
+ ContainerOfImpl<std::vector<uint8_t>,
+ FlatbuffersEnumDomainImpl<uint8_t>>>>();
+ auto enum_object =
+ self.schema_->enums()->Get(field->type()->index());
+ auto inner_domain = FlatbuffersEnumDomainImpl<uint8_t>(enum_object);
+ auto printer = ContainerPrinter<
+ ContainerOfImpl<std::vector<uint8_t>,
+ FlatbuffersEnumDomainImpl<uint8_t>>,
+ FlatbuffersEnumDomainImpl<uint8_t>>{inner_domain};
+ printer.PrintCorpusValue(inner_corpus, out, mode);
+ }
+ absl::Format(out, ")");
+ } else {
+ absl::Format(out, "std::nullopt");
+ }
+ } else {
+ domain.GetPrinter().PrintCorpusValue(val, out, mode);
+ }
}
};
};
@@ -696,9 +973,9 @@
} else if constexpr (std::is_same_v<T, FlatbuffersUnionTag>) {
// TODO: support unions.
return placeholder;
- } else if constexpr (std::is_same_v<T, FlatbuffersVectorTag>) {
- // TODO: support vectors.
- return placeholder;
+ } else if constexpr (is_flatbuffers_vector_tag_v<T>) {
+ return VectorOf(GetDefaultDomain<typename T::value_type>(schema, field))
+ .WithMaxSize(std::numeric_limits<flatbuffers::uoffset_t>::max());
} else {
return Arbitrary<T>();
}
@@ -718,7 +995,7 @@
// - The serialized buffer of the table.
template <typename T>
class FlatbuffersTableDomainImpl
- : public fuzztest::domain_implementor::DomainBase<
+ : public domain_implementor::DomainBase<
/*Derived=*/FlatbuffersTableDomainImpl<T>,
/*ValueType=*/const T*,
/*CorpusType=*/FlatbuffersTableDomainCorpusType> {
diff --git a/fuzztest/internal/test_flatbuffers.fbs b/fuzztest/internal/test_flatbuffers.fbs
index 02177c0..b83ae18 100644
--- a/fuzztest/internal/test_flatbuffers.fbs
+++ b/fuzztest/internal/test_flatbuffers.fbs
@@ -78,6 +78,27 @@
eu32: UIntEnum;
eu64: ULongEnum;
t: BoolTable;
+ v_b: [bool];
+ v_i8: [byte];
+ v_i16: [short];
+ v_i32: [int];
+ v_i64: [long];
+ v_u8: [ubyte];
+ v_u16: [ushort];
+ v_u32: [uint];
+ v_u64: [ulong];
+ v_f: [float];
+ v_d: [double];
+ v_str: [string];
+ v_ei8: [ByteEnum];
+ v_ei16: [ShortEnum];
+ v_ei32: [IntEnum];
+ v_ei64: [LongEnum];
+ v_eu8: [UByteEnum];
+ v_eu16: [UShortEnum];
+ v_eu32: [UIntEnum];
+ v_eu64: [ULongEnum];
+ v_t: [BoolTable];
}
table OptionalTable {
@@ -102,24 +123,6 @@
eu32: UIntEnum = null;
eu64: ULongEnum = null;
t: BoolTable;
-}
-
-table RequiredTable {
- str: string (required);
- t: BoolTable (required);
-}
-
-table RecursiveTable {
- t: NestedRecursiveTable;
-}
-
-table NestedRecursiveTable {
- t: RecursiveTable;
-}
-
-table UnsupportedTypesTable {
- u: Union;
- s: BoolStruct;
v_b: [bool];
v_i8: [byte];
v_i16: [short];
@@ -141,6 +144,45 @@
v_eu32: [UIntEnum];
v_eu64: [ULongEnum];
v_t: [BoolTable];
+}
+
+table RequiredTable {
+ str: string (required);
+ t: BoolTable (required);
+ v_b: [bool] (required);
+ v_i8: [byte] (required);
+ v_i16: [short] (required);
+ v_i32: [int] (required);
+ v_i64: [long] (required);
+ v_u8: [ubyte] (required);
+ v_u16: [ushort] (required);
+ v_u32: [uint] (required);
+ v_u64: [ulong] (required);
+ v_f: [float] (required);
+ v_d: [double] (required);
+ v_str: [string] (required);
+ v_ei8: [ByteEnum] (required);
+ v_ei16: [ShortEnum] (required);
+ v_ei32: [IntEnum] (required);
+ v_ei64: [LongEnum] (required);
+ v_eu8: [UByteEnum] (required);
+ v_eu16: [UShortEnum] (required);
+ v_eu32: [UIntEnum] (required);
+ v_eu64: [ULongEnum] (required);
+ v_t: [BoolTable] (required);
+}
+
+table RecursiveTable {
+ t: NestedRecursiveTable;
+}
+
+table NestedRecursiveTable {
+ t: RecursiveTable;
+}
+
+table UnsupportedTypesTable {
+ u: Union;
+ s: BoolStruct;
v_u: [Union];
v_s: [BoolStruct];
}