Add flatbuffers domain

PiperOrigin-RevId: 781507709
diff --git a/.github/workflows/cmake_test.yml b/.github/workflows/cmake_test.yml
index 8ad0fd9..98b1133 100644
--- a/.github/workflows/cmake_test.yml
+++ b/.github/workflows/cmake_test.yml
@@ -77,6 +77,7 @@
             -D CMAKE_CXX_COMPILER_LAUNCHER=ccache \
             -D CMAKE_BUILD_TYPE=RelWithDebug \
             -D FUZZTEST_BUILD_TESTING=on \
+            -D FUZZTEST_BUILD_FLATBUFFERS=on \
           && cmake --build build -j $(nproc) \
           && ctest --test-dir build -j $(nproc) --output-on-failure
       - name: Run all tests in default mode with gcc
@@ -90,6 +91,7 @@
             -D CMAKE_CXX_COMPILER_LAUNCHER=ccache \
             -D CMAKE_BUILD_TYPE=RelWithDebug \
             -D FUZZTEST_BUILD_TESTING=on \
+            -D FUZZTEST_BUILD_FLATBUFFERS=on \
           && cmake --build build_gcc -j $(nproc) \
           && ctest --test-dir build_gcc -j $(nproc) --output-on-failure
       - name: Run end-to-end tests in fuzzing mode
@@ -104,6 +106,7 @@
             -D CMAKE_BUILD_TYPE=RelWithDebug \
             -D FUZZTEST_FUZZING_MODE=on \
             -D FUZZTEST_BUILD_TESTING=on \
+            -D FUZZTEST_BUILD_FLATBUFFERS=on \
           && cmake --build build -j $(nproc) \
           && ctest --test-dir build -j $(nproc) --output-on-failure -R "functional_test"
       - name: Save new cache based on main
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0d5c514..f9f71df 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -16,6 +16,7 @@
 project(fuzztest)
 
 option(FUZZTEST_BUILD_TESTING "Building the tests." OFF)
+option(FUZZTEST_BUILD_FLATBUFFERS "Building the flatbuffers support." OFF)
 option(FUZZTEST_FUZZING_MODE "Building the fuzztest in fuzzing mode." OFF)
 set(FUZZTEST_COMPATIBILITY_MODE "" CACHE STRING "Compatibility mode. Available options: <empty>, libfuzzer")
 set(CMAKE_CXX_STANDARD 17)
diff --git a/MODULE.bazel b/MODULE.bazel
index 8aa515e..f21437e 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -35,6 +35,10 @@
     name = "platforms",
     version = "0.0.10",
 )
+bazel_dep(
+    name = "flatbuffers",
+    version = "25.2.10"
+)
 # GoogleTest is not a dev dependency, because it's needed when FuzzTest is used
 # with GoogleTest integration (e.g., googletest_adaptor). Note that the FuzzTest
 # framework can be used without GoogleTest integration as well.
@@ -48,8 +52,6 @@
     name = "protobuf",
     version = "31.1",
 )
-# TODO(lszekeres): Make this a dev dependency, as the protobuf library is only
-# required for testing.
 bazel_dep(
     name = "rules_proto",
     version = "7.1.0",
diff --git a/cmake/BuildDependencies.cmake b/cmake/BuildDependencies.cmake
index de9323f..d8565c0 100644
--- a/cmake/BuildDependencies.cmake
+++ b/cmake/BuildDependencies.cmake
@@ -35,6 +35,9 @@
 set(nlohmann_json_URL https://github.com/nlohmann/json.git)
 set(nlohmann_json_TAG v3.11.3)
 
+set(flatbuffers_URL https://github.com/google/flatbuffers.git)
+set(flatbuffers_TAG v25.2.10)
+
 if(POLICY CMP0135)
 	cmake_policy(SET CMP0135 NEW)
 	set(CMAKE_POLICY_DEFAULT_CMP0135 NEW)
@@ -64,6 +67,14 @@
   URL_HASH MD5=${antlr_cpp_MD5}
 )
 
+if (FUZZTEST_BUILD_FLATBUFFERS)
+  FetchContent_Declare(
+    flatbuffers
+    GIT_REPOSITORY ${flatbuffers_URL}
+    GIT_TAG        ${flatbuffers_TAG}
+  )
+endif()
+
 if (FUZZTEST_BUILD_TESTING)
 
   FetchContent_Declare(
@@ -101,3 +112,9 @@
   FetchContent_MakeAvailable(nlohmann_json)
 
 endif ()
+
+if (FUZZTEST_BUILD_FLATBUFFERS)
+  set(FLATBUFFERS_BUILD_TESTS OFF)
+  set(FLATBUFFERS_BUILD_INSTALL OFF)
+  FetchContent_MakeAvailable(flatbuffers)
+endif()
diff --git a/cmake/FuzzTestHelpers.cmake b/cmake/FuzzTestHelpers.cmake
index 31a7db4..2869a77 100644
--- a/cmake/FuzzTestHelpers.cmake
+++ b/cmake/FuzzTestHelpers.cmake
@@ -338,4 +338,191 @@
 
   add_library(fuzztest::${FUZZTEST_PROTO_LIB_NAME} ALIAS ${_NAME})
 
-endfunction()
\ No newline at end of file
+endfunction()
+
+# fuzztest_flatbuffers_generate_headers()
+#
+# Modified version of `flatbuffers_generate_headers`
+# from https://github.com/google/flatbuffers/blob/master/CMake/BuildFlatBuffers.cmake
+# Supports having the embedded schema header in the output set.
+#
+# Creates a target that can be linked against that generates flatbuffer headers.
+#
+# This function takes a target name and a list of schemas. You can also specify
+# other flagc flags using the FLAGS option to change the behavior of the flatc
+# tool.
+#
+# When the target_link_libraries is done within a different directory than
+# flatbuffers_generate_headers is called, then the target should also be dependent
+# the custom generation target called fuzztest_GENERATE_<TARGET>.
+#
+# Arguments:
+#   TARGET: The name of the target to generate.
+#   SCHEMAS: The list of schema files to generate code for.
+#   BINARY_SCHEMAS_DIR: Optional. The directory in which to generate binary
+#       schemas. Binary schemas will only be generated if a path is provided.
+#   INCLUDE: Optional. Search for includes in the specified paths. (Use this
+#       instead of "-I <path>" and the FLAGS option so that CMake is aware of
+#       the directories that need to be searched).
+#   INCLUDE_PREFIX: Optional. The directory in which to place the generated
+#       files. Use this instead of the --include-prefix option.
+#   FLAGS: Optional. A list of any additional flags that you would like to pass
+#       to flatc.
+#   TESTONLY: When added, this target will only be built if both
+#             BUILD_TESTING=ON and FUZZTEST_BUILD_TESTING=ON.
+#
+# Example:
+#
+#     fuzztest_flatbuffers_library(
+#         TARGET my_generated_headers_target
+#         INCLUDE_PREFIX ${MY_INCLUDE_PREFIX}"
+#         SCHEMAS ${MY_SCHEMA_FILES}
+#         BINARY_SCHEMAS_DIR "${MY_BINARY_SCHEMA_DIRECTORY}"
+#         FLAGS --gen-object-api)
+#
+#     target_link_libraries(MyExecutableTarget
+#         PRIVATE fuzztest_my_generated_headers_target
+#     )
+#
+# Optional (only needed within different directory):
+#     add_dependencies(app fuzztest_GENERATE_my_generated_headers_target)
+function(fuzztest_flatbuffers_generate_headers)
+  # Parse function arguments.
+  set(options TESTONLY)
+  set(one_value_args
+    "TARGET"
+    "INCLUDE_PREFIX"
+    "BINARY_SCHEMAS_DIR")
+  set(multi_value_args
+    "SCHEMAS"
+    "INCLUDE"
+    "FLAGS")
+  cmake_parse_arguments(
+    PARSE_ARGV 0
+    FLATBUFFERS_GENERATE_HEADERS
+    "${options}"
+    "${one_value_args}"
+    "${multi_value_args}")
+
+  if(FLATBUFFERS_GENERATE_HEADERS_TESTONLY AND NOT (BUILD_TESTING AND FUZZTEST_BUILD_TESTING))
+    return()
+  endif()
+
+  # Test if including from FindFlatBuffers
+  if(FLATBUFFERS_FLATC_EXECUTABLE)
+    set(FLATC_TARGET "")
+    set(FLATC ${FLATBUFFERS_FLATC_EXECUTABLE})
+  elseif(TARGET flatbuffers::flatc)
+    set(FLATC_TARGET flatbuffers::flatc)
+    set(FLATC flatbuffers::flatc)
+  else()
+    set(FLATC_TARGET flatc)
+    set(FLATC flatc)
+  endif()
+
+  set(working_dir "${CMAKE_CURRENT_SOURCE_DIR}")
+
+  # Generate the include files parameters.
+  set(include_params "")
+  foreach (include_dir ${FLATBUFFERS_GENERATE_HEADERS_INCLUDE})
+    set(include_params -I ${include_dir} ${include_params})
+  endforeach()
+
+  # Create a directory to place the generated code.
+  set(generated_target_dir "${CMAKE_CURRENT_BINARY_DIR}")
+  set(generated_include_dir "${generated_target_dir}")
+  if (NOT ${FLATBUFFERS_GENERATE_HEADERS_INCLUDE_PREFIX} STREQUAL "")
+    set(generated_include_dir "${generated_include_dir}/${FLATBUFFERS_GENERATE_HEADERS_INCLUDE_PREFIX}")
+    list(APPEND FLATBUFFERS_GENERATE_HEADERS_FLAGS 
+         "--include-prefix" ${FLATBUFFERS_GENERATE_HEADERS_INCLUDE_PREFIX})
+  endif()
+
+  set(generated_custom_commands)
+
+  # Create rules to generate the code for each schema.
+  foreach(schema ${FLATBUFFERS_GENERATE_HEADERS_SCHEMAS})
+    get_filename_component(filename ${schema} NAME_WE)
+    set(generated_include "${generated_include_dir}/${filename}_generated.h")
+    # Add the embedded schema header in the output set if requested.
+    if("${FLATBUFFERS_GENERATE_HEADERS_FLAGS}" MATCHES "--bfbs-gen-embed")
+      list(APPEND generated_include "${generated_include_dir}/${filename}_bfbs_generated.h")
+    endif()
+
+    # Generate files for grpc if needed
+    set(generated_source_file)
+    if("${FLATBUFFERS_GENERATE_HEADERS_FLAGS}" MATCHES "--grpc")
+      # Check if schema file contain a rpc_service definition
+      file(STRINGS ${schema} has_grpc REGEX "rpc_service")
+      if(has_grpc)
+        list(APPEND generated_include "${generated_include_dir}/${filename}.grpc.fb.h")
+        set(generated_source_file "${generated_include_dir}/${filename}.grpc.fb.cc")
+      endif()
+    endif()
+
+    add_custom_command(
+      OUTPUT ${generated_include} ${generated_source_file}
+      COMMAND ${FLATC} ${FLATC_ARGS}
+      -o ${generated_include_dir}
+      ${include_params}
+      -c ${schema}
+      ${FLATBUFFERS_GENERATE_HEADERS_FLAGS}
+      DEPENDS ${FLATC_TARGET} ${schema}
+      WORKING_DIRECTORY "${working_dir}"
+      COMMENT "Building ${schema} flatbuffers...")
+    list(APPEND all_generated_header_files ${generated_include})
+    list(APPEND all_generated_source_files ${generated_source_file})
+    list(APPEND generated_custom_commands "${generated_include}" "${generated_source_file}")
+
+    # Generate the binary flatbuffers schemas if instructed to.
+    if (NOT ${FLATBUFFERS_GENERATE_HEADERS_BINARY_SCHEMAS_DIR} STREQUAL "")
+      set(binary_schema
+          "${FLATBUFFERS_GENERATE_HEADERS_BINARY_SCHEMAS_DIR}/${filename}.bfbs")
+      add_custom_command(
+        OUTPUT ${binary_schema}
+        COMMAND ${FLATC} -b --schema
+        -o ${FLATBUFFERS_GENERATE_HEADERS_BINARY_SCHEMAS_DIR}
+        ${include_params}
+        ${schema}
+        DEPENDS ${FLATC_TARGET} ${schema}
+        WORKING_DIRECTORY "${working_dir}")
+      list(APPEND generated_custom_commands "${binary_schema}")
+      list(APPEND all_generated_binary_files ${binary_schema})
+    endif()
+  endforeach()
+
+  # Create an additional target as add_custom_command scope is only within same directory (CMakeFile.txt)
+  set(generate_target fuzztest_GENERATE_${FLATBUFFERS_GENERATE_HEADERS_TARGET})
+  add_custom_target(${generate_target} ALL
+                    DEPENDS ${generated_custom_commands}
+                    COMMENT "Generating flatbuffer target fuzztest_${FLATBUFFERS_GENERATE_HEADERS_TARGET}")
+
+  # Set up interface library
+  add_library(fuzztest_${FLATBUFFERS_GENERATE_HEADERS_TARGET} INTERFACE)
+  add_dependencies(
+    fuzztest_${FLATBUFFERS_GENERATE_HEADERS_TARGET}
+    ${FLATC}
+    ${FLATBUFFERS_GENERATE_HEADERS_SCHEMAS})
+  target_include_directories(
+    fuzztest_${FLATBUFFERS_GENERATE_HEADERS_TARGET}
+    INTERFACE ${generated_target_dir})
+
+  # Organize file layout for IDEs.
+  source_group(
+    TREE "${generated_target_dir}"
+    PREFIX "Flatbuffers/Generated/Headers Files"
+    FILES ${all_generated_header_files})
+  source_group(
+    TREE "${generated_target_dir}"
+    PREFIX "Flatbuffers/Generated/Source Files"
+    FILES ${all_generated_source_files})
+  source_group(
+    TREE ${working_dir}
+    PREFIX "Flatbuffers/Schemas"
+    FILES ${FLATBUFFERS_GENERATE_HEADERS_SCHEMAS})
+  if (NOT ${FLATBUFFERS_GENERATE_HEADERS_BINARY_SCHEMAS_DIR} STREQUAL "")
+    source_group(
+      TREE "${FLATBUFFERS_GENERATE_HEADERS_BINARY_SCHEMAS_DIR}"
+      PREFIX "Flatbuffers/Generated/Binary Schemas"
+      FILES ${all_generated_binary_files})
+  endif()
+endfunction()
diff --git a/cmake/generate_cmake_from_bazel.py b/cmake/generate_cmake_from_bazel.py
index 1816c62..86bfd59 100755
--- a/cmake/generate_cmake_from_bazel.py
+++ b/cmake/generate_cmake_from_bazel.py
@@ -66,6 +66,7 @@
     "@abseil-cpp//absl/types:optional": "absl::optional",
     "@abseil-cpp//absl/types:span": "absl::span",
     "@abseil-cpp//absl/types:variant": "absl::variant",
+    "@flatbuffers//:runtime_cc": "flatbuffers",
     "@googletest//:gtest": "GTest::gtest",
     "@googletest//:gtest_main": "GTest::gmock_main",
     "@protobuf//:protobuf": "protobuf::libprotobuf",
diff --git a/domain_tests/BUILD b/domain_tests/BUILD
index a2584b1..0852619 100644
--- a/domain_tests/BUILD
+++ b/domain_tests/BUILD
@@ -38,6 +38,23 @@
 )
 
 cc_test(
+    name = "arbitrary_domains_flatbuffers_test",
+    srcs = ["arbitrary_domains_flatbuffers_test.cc"],
+    deps = [
+        ":domain_testing",
+        "@abseil-cpp//absl/container:flat_hash_map",
+        "@abseil-cpp//absl/random",
+        "@com_google_fuzztest//fuzztest:domain",
+        "@com_google_fuzztest//fuzztest:flatbuffers",
+        "@com_google_fuzztest//fuzztest:fuzztest_macros",
+        "@com_google_fuzztest//fuzztest/internal:meta",
+        "@com_google_fuzztest//fuzztest/internal:test_flatbuffers_cc_fbs",
+        "@flatbuffers//:runtime_cc",
+        "@googletest//:gtest_main",
+    ],
+)
+
+cc_test(
     name = "arbitrary_domains_protobuf_test",
     srcs = ["arbitrary_domains_protobuf_test.cc"],
     deps = [
diff --git a/domain_tests/CMakeLists.txt b/domain_tests/CMakeLists.txt
index 4a858b0..a9f9bb7 100644
--- a/domain_tests/CMakeLists.txt
+++ b/domain_tests/CMakeLists.txt
@@ -18,6 +18,30 @@
     GTest::gmock_main
 )
 
+if (FUZZTEST_BUILD_FLATBUFFERS)
+  fuzztest_cc_test(
+    NAME
+      arbitrary_domains_flatbuffers_test
+    SRCS
+      "arbitrary_domains_flatbuffers_test.cc"
+    DEPS
+      absl::flat_hash_set
+      absl::random_bit_gen_ref
+      absl::random_random
+      absl::strings
+      flatbuffers
+      fuzztest::domain
+      fuzztest::domain_testing
+      fuzztest::flatbuffers
+      fuzztest::fuzztest_macros
+      GTest::gmock_main
+      fuzztest_test_flatbuffers_headers
+  )
+  add_dependencies(fuzztest_arbitrary_domains_flatbuffers_test
+    fuzztest_GENERATE_test_flatbuffers_headers
+  )
+endif()
+
 fuzztest_cc_test(
   NAME
     arbitrary_domains_protobuf_test
diff --git a/domain_tests/arbitrary_domains_flatbuffers_test.cc b/domain_tests/arbitrary_domains_flatbuffers_test.cc
new file mode 100644
index 0000000..1c2ab25
--- /dev/null
+++ b/domain_tests/arbitrary_domains_flatbuffers_test.cc
@@ -0,0 +1,489 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstring>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/container/flat_hash_map.h"
+#include "absl/random/random.h"
+#include "flatbuffers/base.h"
+#include "flatbuffers/buffer.h"
+#include "flatbuffers/flatbuffer_builder.h"
+#include "flatbuffers/reflection_generated.h"
+#include "flatbuffers/string.h"
+#include "flatbuffers/vector.h"
+#include "./fuzztest/domain.h"
+#include "./domain_tests/domain_testing.h"
+#include "./fuzztest/flatbuffers.h"
+#include "./fuzztest/internal/meta.h"
+#include "./fuzztest/internal/test_flatbuffers_generated.h"
+
+namespace fuzztest {
+namespace {
+
+using ::fuzztest::internal::DefaultTable;
+using ::fuzztest::internal::OptionalTable;
+using ::fuzztest::internal::RequiredTable;
+using ::fuzztest::internal::UnsupportedTypesTable;
+using ::testing::_;
+using ::testing::AllOf;
+using ::testing::Each;
+using ::testing::HasSubstr;
+using ::testing::IsFalse;
+using ::testing::IsTrue;
+using ::testing::Pair;
+using ::testing::ResultOf;
+
+template <typename T>
+inline bool Eq(const T& lhs, const T& rhs) {
+  return rhs == lhs;
+}
+
+template <typename T>
+inline bool Eq(const T* lhs, const T* rhs) {
+  if (lhs == nullptr && rhs == nullptr) return true;
+  if (lhs == nullptr || rhs == nullptr) return false;
+  return Eq(*lhs, *rhs);
+}
+
+template <>
+inline bool Eq<flatbuffers::String>(const flatbuffers::String& lhs,
+                                    const flatbuffers::String& rhs) {
+  if (lhs.size() != rhs.size()) return false;
+  return memcmp(lhs.data(), rhs.data(), lhs.size()) == 0;
+}
+
+template <>
+inline bool Eq<DefaultTable>(const DefaultTable& lhs, const DefaultTable& rhs) {
+  const bool eq_b = lhs.b() == rhs.b();
+  const bool eq_i8 = lhs.i8() == rhs.i8();
+  const bool eq_i16 = lhs.i16() == rhs.i16();
+  const bool eq_i32 = lhs.i32() == rhs.i32();
+  const bool eq_i64 = lhs.i64() == rhs.i64();
+  const bool eq_u8 = lhs.u8() == rhs.u8();
+  const bool eq_u16 = lhs.u16() == rhs.u16();
+  const bool eq_u32 = lhs.u32() == rhs.u32();
+  const bool eq_u64 = lhs.u64() == rhs.u64();
+  const bool eq_f = lhs.f() == rhs.f();
+  const bool eq_d = lhs.d() == rhs.d();
+  const bool eq_str = Eq(lhs.str(), rhs.str());
+  const bool eq_ei8 = lhs.ei8() == rhs.ei8();
+  const bool eq_ei16 = lhs.ei16() == rhs.ei16();
+  const bool eq_ei32 = lhs.ei32() == rhs.ei32();
+  const bool eq_ei64 = lhs.ei64() == rhs.ei64();
+  const bool eq_eu8 = lhs.eu8() == rhs.eu8();
+  const bool eq_eu16 = lhs.eu16() == rhs.eu16();
+  const bool eq_eu32 = lhs.eu32() == rhs.eu32();
+  const bool eq_eu64 = lhs.eu64() == rhs.eu64();
+  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;
+}
+
+const internal::DefaultTable* CreateDefaultTable(
+    flatbuffers::FlatBufferBuilder& fbb) {
+  auto table_offset =
+      internal::CreateDefaultTableDirect(fbb,
+                                         /*b=*/true,
+                                         /*i8=*/1,
+                                         /*i16=*/2,
+                                         /*i32=*/3,
+                                         /*i64=*/4,
+                                         /*u8=*/5,
+                                         /*u16=*/6,
+                                         /*u32=*/7,
+                                         /*u64=*/8,
+                                         /*f=*/9.0,
+                                         /*d=*/10.0,
+                                         /*str=*/"foo bar baz",
+                                         /*ei8=*/internal::ByteEnum_Second,
+                                         /*ei16=*/internal::ShortEnum_Second,
+                                         /*ei32=*/internal::IntEnum_Second,
+                                         /*ei64=*/internal::LongEnum_Second,
+                                         /*eu8=*/internal::UByteEnum_Second,
+                                         /*eu16=*/internal::UShortEnum_Second,
+                                         /*eu32=*/internal::UIntEnum_Second,
+                                         /*eu64=*/internal::ULongEnum_Second);
+  fbb.Finish(table_offset);
+  return flatbuffers::GetRoot<DefaultTable>(fbb.GetBufferPointer());
+}
+
+// TODO: b/430818627 - Remove and replace usages with GenerateNonUniqueValues.
+template <typename Domain>
+std::vector<typename Domain::corpus_type> GenerateNonUniqueCorpusValues(
+    Domain domain, int num_seeds = 10, int num_mutations = 100,
+    const domain_implementor::MutationMetadata& metadata = {},
+    bool only_shrink = false) {
+  using CorpusT = typename Domain::corpus_type;
+  absl::BitGen bitgen;
+
+  std::vector<CorpusT> seeds;
+  seeds.reserve(num_seeds);
+  while (seeds.size() < num_seeds) {
+    seeds.push_back(domain.Init(bitgen));
+  }
+
+  std::vector<CorpusT> values = seeds;
+
+  for (const auto& seed : seeds) {
+    CorpusT value = seed;
+    std::vector<CorpusT> mutations;
+    mutations.reserve(num_mutations);
+    while (mutations.size() < num_mutations) {
+      domain.Mutate(value, bitgen, metadata, only_shrink);
+      mutations.push_back(value);
+    }
+    values.insert(values.end(), mutations.begin(), mutations.end());
+  }
+
+  return values;
+};
+
+// TODO: b/430818627 - Remove and replace usages with GenerateInitialValues.
+template <typename Domain>
+std::vector<typename Domain::corpus_type> GenerateInitialCorpusValues(
+    Domain domain, int n) {
+  std::vector<typename Domain::corpus_type> values;
+  absl::BitGen bitgen;
+  values.reserve(n);
+  for (int i = 0; i < n; ++i) {
+    values.push_back(domain.Init(bitgen));
+  }
+  return values;
+}
+
+TEST(FlatbuffersMetaTest, IsFlatbuffersTable) {
+  static_assert(internal::is_flatbuffers_table_v<DefaultTable>);
+  static_assert(!internal::is_flatbuffers_table_v<int>);
+  static_assert(!internal::is_flatbuffers_table_v<std::optional<bool>>);
+}
+
+TEST(FlatbuffersTableDomainImplTest, DefaultTableValueRoundTrip) {
+  flatbuffers::FlatBufferBuilder fbb;
+  auto table = CreateDefaultTable(fbb);
+
+  auto domain = Arbitrary<const DefaultTable*>();
+  auto corpus = domain.FromValue(table);
+  ASSERT_TRUE(corpus.has_value());
+  ASSERT_OK(domain.ValidateCorpusValue(*corpus));
+
+  auto ir = domain.SerializeCorpus(corpus.value());
+
+  auto new_corpus = domain.ParseCorpus(ir);
+  ASSERT_TRUE(new_corpus.has_value());
+  ASSERT_OK(domain.ValidateCorpusValue(*new_corpus));
+
+  auto new_table = domain.GetValue(*new_corpus);
+  EXPECT_EQ(new_table->b(), true);
+  EXPECT_EQ(new_table->i8(), 1);
+  EXPECT_EQ(new_table->i16(), 2);
+  EXPECT_EQ(new_table->i32(), 3);
+  EXPECT_EQ(new_table->i64(), 4);
+  EXPECT_EQ(new_table->u8(), 5);
+  EXPECT_EQ(new_table->u16(), 6);
+  EXPECT_EQ(new_table->u32(), 7);
+  EXPECT_EQ(new_table->u64(), 8);
+  EXPECT_EQ(new_table->f(), 9.0);
+  EXPECT_EQ(new_table->d(), 10.0);
+  EXPECT_EQ(new_table->str()->str(), "foo bar baz");
+  EXPECT_EQ(new_table->ei8(), internal::ByteEnum_Second);
+  EXPECT_EQ(new_table->ei16(), internal::ShortEnum_Second);
+  EXPECT_EQ(new_table->ei32(), internal::IntEnum_Second);
+  EXPECT_EQ(new_table->ei64(), internal::LongEnum_Second);
+  EXPECT_EQ(new_table->eu8(), internal::UByteEnum_Second);
+  EXPECT_EQ(new_table->eu16(), internal::UShortEnum_Second);
+  EXPECT_EQ(new_table->eu32(), internal::UIntEnum_Second);
+  EXPECT_EQ(new_table->eu64(), internal::ULongEnum_Second);
+}
+
+TEST(FlatbuffersTableDomainImplTest, InitGeneratesSeeds) {
+  flatbuffers::FlatBufferBuilder fbb;
+  auto table = CreateDefaultTable(fbb);
+
+  auto domain = Arbitrary<const DefaultTable*>().WithSeeds({table});
+
+  EXPECT_THAT(GenerateInitialCorpusValues(domain, IterationsToHitAll(1, 0.5)),
+              Contains(ResultOf(
+                  [table, &domain](
+                      const typename decltype(domain)::corpus_type& corpus) {
+                    return Eq(domain.GetValue(corpus), table);
+                  },
+                  IsTrue())));
+}
+
+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},
+  };
+
+  auto domain = Arbitrary<const DefaultTable*>();
+
+  absl::BitGen bitgen;
+  for (size_t i = 0; i < IterationsToHitAll(mutated_fields.size(),
+                                            1.0 / mutated_fields.size());
+       ++i) {
+    Value initial_val(domain, bitgen);
+    Value val(initial_val);
+    val.Mutate(domain, bitgen, {}, false);
+    const auto& mut = val.user_value;
+    const auto& init = initial_val.user_value;
+
+    mutated_fields["b"] |= mut->b() != init->b();
+    mutated_fields["i8"] |= mut->i8() != init->i8();
+    mutated_fields["i16"] |= mut->i16() != init->i16();
+    mutated_fields["i32"] |= mut->i32() != init->i32();
+    mutated_fields["i64"] |= mut->i64() != init->i64();
+    mutated_fields["u8"] |= mut->u8() != init->u8();
+    mutated_fields["u16"] |= mut->u16() != init->u16();
+    mutated_fields["u32"] |= mut->u32() != init->u32();
+    mutated_fields["u64"] |= mut->u64() != init->u64();
+    mutated_fields["f"] |= mut->f() != init->f();
+    mutated_fields["d"] |= mut->d() != init->d();
+    mutated_fields["str"] |= !Eq(mut->str(), init->str());
+    mutated_fields["ei8"] |= mut->ei8() != init->ei8();
+    mutated_fields["ei16"] |= mut->ei16() != init->ei16();
+    mutated_fields["ei32"] |= mut->ei32() != init->ei32();
+    mutated_fields["ei64"] |= mut->ei64() != init->ei64();
+    mutated_fields["eu8"] |= mut->eu8() != init->eu8();
+    mutated_fields["eu16"] |= mut->eu16() != init->eu16();
+    mutated_fields["eu32"] |= mut->eu32() != init->eu32();
+    mutated_fields["eu64"] |= mut->eu64() != init->eu64();
+
+    if (std::all_of(mutated_fields.begin(), mutated_fields.end(),
+                    [](const auto& p) { return p.second; })) {
+      break;
+    }
+  }
+
+  EXPECT_THAT(mutated_fields, Each(Pair(_, true)));
+}
+
+TEST(FlatbuffersTableDomainImplTest, OptionalTableEventuallyBecomeEmpty) {
+  flatbuffers::FlatBufferBuilder fbb;
+  auto table_offset =
+      internal::CreateOptionalTableDirect(fbb,
+                                          true,                         // b
+                                          1,                            // i8
+                                          2,                            // i16
+                                          3,                            // i32
+                                          4,                            // i64
+                                          5,                            // u8
+                                          6,                            // u16
+                                          7,                            // u32
+                                          8,                            // u64
+                                          9.0,                          // f
+                                          10.0,                         // d
+                                          "foo bar baz",                // str
+                                          internal::ByteEnum_Second,    // ei8
+                                          internal::ShortEnum_Second,   // ei16
+                                          internal::IntEnum_Second,     // ei32
+                                          internal::LongEnum_Second,    // ei64
+                                          internal::UByteEnum_Second,   // eu8
+                                          internal::UShortEnum_Second,  // eu16
+                                          internal::UIntEnum_Second,    // eu32
+                                          internal::ULongEnum_Second    // eu64
+      );
+  fbb.Finish(table_offset);
+  auto table = flatbuffers::GetRoot<OptionalTable>(fbb.GetBufferPointer());
+
+  auto domain = Arbitrary<const OptionalTable*>();
+  Value val(domain, table);
+  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},
+  };
+
+  for (size_t i = 0; i < 100'000; ++i) {
+    val.Mutate(domain, bitgen, {}, true);
+    const auto& v = val.user_value;
+
+    null_fields["b"] |= !v->b().has_value();
+    null_fields["i8"] |= !v->i8().has_value();
+    null_fields["i16"] |= !v->i16().has_value();
+    null_fields["i32"] |= !v->i32().has_value();
+    null_fields["i64"] |= !v->i64().has_value();
+    null_fields["u8"] |= !v->u8().has_value();
+    null_fields["u16"] |= !v->u16().has_value();
+    null_fields["u32"] |= !v->u32().has_value();
+    null_fields["u64"] |= !v->u64().has_value();
+    null_fields["f"] |= !v->f().has_value();
+    null_fields["d"] |= !v->d().has_value();
+    null_fields["str"] |= v->str() == nullptr;
+    null_fields["ei8"] |= !v->ei8().has_value();
+    null_fields["ei16"] |= !v->ei16().has_value();
+    null_fields["ei32"] |= !v->ei32().has_value();
+    null_fields["ei64"] |= !v->ei64().has_value();
+    null_fields["eu8"] |= !v->eu8().has_value();
+    null_fields["eu16"] |= !v->eu16().has_value();
+    null_fields["eu32"] |= !v->eu32().has_value();
+    null_fields["eu64"] |= !v->eu64().has_value();
+
+    if (std::all_of(null_fields.begin(), null_fields.end(),
+                    [](const auto& p) { return p.second; })) {
+      break;
+    }
+  }
+
+  EXPECT_THAT(null_fields, Each(Pair(_, true)));
+}
+
+TEST(FlatbuffersTableDomainImplTest, RequiredTableFieldsAlwaysSet) {
+  auto domain = Arbitrary<const RequiredTable*>();
+
+  EXPECT_THAT(GenerateNonUniqueCorpusValues(
+                  domain,
+                  /*num_seeds=*/1,
+                  /*num_mutations=*/IterationsToHitAll(1, 1.0 / 100), {},
+                  /*only_shrink=*/true),
+              Each(ResultOf(
+                  [&](const typename decltype(domain)::corpus_type& corpus) {
+                    return domain.GetValue(corpus)->str() == nullptr;
+                  },
+                  IsFalse())));
+}
+
+TEST(FlatbuffersTableDomainImplTest, Printer) {
+  flatbuffers::FlatBufferBuilder fbb;
+  auto table = CreateDefaultTable(fbb);
+  auto domain = Arbitrary<const DefaultTable*>();
+  auto corpus = domain.FromValue(table);
+  ASSERT_TRUE(corpus.has_value());
+
+  auto printer = domain.GetPrinter();
+  std::string out;
+  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: (1)"),                // ei8
+                         HasSubstr("ei16: (1)"),               // ei16
+                         HasSubstr("ei32: (1)"),               // ei32
+                         HasSubstr("ei64: (1)"),               // ei64
+                         HasSubstr("eu8: (1)"),                // eu8
+                         HasSubstr("eu16: (1)"),               // eu16
+                         HasSubstr("eu32: (1)"),               // eu32
+                         HasSubstr("eu64: (1)")                // eu64
+                         ));
+}
+
+TEST(FlatbuffersTableDomainImplTest, UnsupportedTypesRemainNull) {
+  absl::flat_hash_map<std::string, bool> null_fields{
+      {"t", true},      {"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}};
+
+  auto domain = Arbitrary<const UnsupportedTypesTable*>();
+
+  absl::BitGen bitgen;
+  for (size_t i = 0;
+       i < IterationsToHitAll(null_fields.size(), 1.0 / null_fields.size());
+       ++i) {
+    Value val(domain, bitgen);
+    val.Mutate(domain, bitgen, {}, false);
+    const auto& mut = val.user_value;
+
+    null_fields["t"] &= mut->t() == nullptr;
+    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;
+
+    if (std::any_of(null_fields.begin(), null_fields.end(),
+                    [](const auto& p) { return !p.second; })) {
+      break;
+    }
+  }
+
+  EXPECT_THAT(null_fields, Each(Pair(_, true)));
+}
+
+TEST(FlatbuffersTableDomainImplTest, MutateAlwaysChangesValues) {
+  auto domain = Arbitrary<const DefaultTable*>();
+  const reflection::Schema* schema =
+      reflection::GetSchema(DefaultTable::BinarySchema::data());
+  const reflection::Object* object =
+      schema->objects()->LookupByKey(DefaultTable::GetFullyQualifiedName());
+
+  absl::BitGen bitgen;
+  size_t iterations = IterationsToHitAll(object->fields()->size(),
+                                         1.0 / object->fields()->size());
+  typename decltype(domain)::corpus_type corpus = domain.Init(bitgen);
+  for (size_t i = 0; i < iterations; ++i) {
+    auto mutated_corpus = corpus;
+    domain.Mutate(mutated_corpus, bitgen, {}, false);
+    EXPECT_FALSE(Eq(domain.GetValue(mutated_corpus), domain.GetValue(corpus)));
+    corpus = mutated_corpus;
+  }
+}
+
+TEST(FlatbuffersTableDomainImplTest, UnsupportedFieldsCountIsZero) {
+  auto domain = Arbitrary<const UnsupportedTypesTable*>();
+  auto corpus = domain.Init(absl::BitGen());
+  EXPECT_EQ(domain.CountNumberOfFields(corpus), 0);
+}
+
+}  // namespace
+}  // namespace fuzztest
diff --git a/e2e_tests/functional_test.cc b/e2e_tests/functional_test.cc
index 9ee676e..d8e3593 100644
--- a/e2e_tests/functional_test.cc
+++ b/e2e_tests/functional_test.cc
@@ -1857,6 +1857,34 @@
   ExpectTargetAbort(status, std_err);
 }
 
+TEST_P(FuzzingModeCrashFindingTest, FlatbuffersFailsWhenFieldsAreNotDefault) {
+  TempDir out_dir;
+  auto [status, std_out, std_err] =
+      Run("MySuite.FlatbuffersFailsWhenFieldsAreNotDefault");
+  EXPECT_THAT(std_err,
+              AllOf(HasSubstr("argument 0: {b: ("),  // b
+                    HasSubstr("i8: ("),              // i8
+                    HasSubstr("i16: ("),             // i16
+                    HasSubstr("i32: ("),             // i32
+                    HasSubstr("i64: ("),             // i64
+                    HasSubstr("u8: ("),              // u8
+                    HasSubstr("u16: ("),             // u16
+                    HasSubstr("u32: ("),             // u32
+                    HasSubstr("u64: ("),             // u64
+                    HasSubstr("f: ("),               // f
+                    HasSubstr("d: ("),               // d
+                    HasSubstr("str: "),              // str
+                    HasSubstr("ei8: ("),             // ei8
+                    HasSubstr("ei16: ("),            // ei16
+                    HasSubstr("ei32: ("),            // ei32
+                    HasSubstr("ei64: ("),            // ei64
+                    HasSubstr("eu8: ("),             // eu8
+                    HasSubstr("eu16: ("),            // eu16
+                    HasSubstr("eu32: ("),            // eu32
+                    HasSubstr("eu64: (")));          // eu64
+  ExpectTargetAbort(status, std_err);
+}
+
 INSTANTIATE_TEST_SUITE_P(FuzzingModeCrashFindingTestWithExecutionModel,
                          FuzzingModeCrashFindingTest,
                          testing::ValuesIn(GetAvailableExecutionModels()));
diff --git a/e2e_tests/testdata/BUILD b/e2e_tests/testdata/BUILD
index 3c623f3..2abbffa 100644
--- a/e2e_tests/testdata/BUILD
+++ b/e2e_tests/testdata/BUILD
@@ -101,9 +101,11 @@
         "@abseil-cpp//absl/time",
         "@abseil-cpp//absl/types:span",
         "@com_google_fuzztest//fuzztest",
+        "@com_google_fuzztest//fuzztest:flatbuffers",
         "@com_google_fuzztest//fuzztest:fuzztest_gtest_main",
         "@com_google_fuzztest//fuzztest:googletest_fixture_adapter",
         "@com_google_fuzztest//fuzztest/internal:logging",
+        "@com_google_fuzztest//fuzztest/internal:test_flatbuffers_cc_fbs",
         "@com_google_fuzztest//fuzztest/internal:test_protobuf_cc_proto",
         "@protobuf",
         "@re2",
diff --git a/e2e_tests/testdata/CMakeLists.txt b/e2e_tests/testdata/CMakeLists.txt
index 4e802b1..946d373 100644
--- a/e2e_tests/testdata/CMakeLists.txt
+++ b/e2e_tests/testdata/CMakeLists.txt
@@ -39,8 +39,10 @@
   absl::strings
   absl::time
   re2::re2
+  fuzztest_flatbuffers
   fuzztest_googletest_fixture_adapter
   fuzztest_logging
+  fuzztest_test_flatbuffers_headers
 )
 link_fuzztest(fuzz_tests_for_functional_testing.stripped)
 set_target_properties(
diff --git a/e2e_tests/testdata/fuzz_tests_for_functional_testing.cc b/e2e_tests/testdata/fuzz_tests_for_functional_testing.cc
index 8049505..b23a49a 100644
--- a/e2e_tests/testdata/fuzz_tests_for_functional_testing.cc
+++ b/e2e_tests/testdata/fuzz_tests_for_functional_testing.cc
@@ -30,6 +30,7 @@
 #include <utility>
 #include <vector>
 
+#include "./fuzztest/flatbuffers.h"  // IWYU pragma: keep
 #include "./fuzztest/fuzztest.h"
 #include "absl/algorithm/container.h"
 #include "absl/functional/function_ref.h"
@@ -39,6 +40,7 @@
 #include "absl/time/clock.h"
 #include "absl/time/time.h"
 #include "./fuzztest/internal/logging.h"
+#include "./fuzztest/internal/test_flatbuffers_generated.h"
 #include "./fuzztest/internal/test_protobuf.pb.h"
 #include "google/protobuf/descriptor.h"
 #include "google/protobuf/message.h"
@@ -56,6 +58,7 @@
 using ::fuzztest::StructOf;
 using ::fuzztest::TupleOf;
 using ::fuzztest::VectorOf;
+using ::fuzztest::internal::DefaultTable;
 using ::fuzztest::internal::ProtoExtender;
 using ::fuzztest::internal::SingleInt32Field;
 using ::fuzztest::internal::TestProtobuf;
@@ -847,4 +850,68 @@
 };
 FUZZ_TEST_F(FaultySetupTest, NoOp);
 
+void FlatbuffersFailsWhenFieldsAreNotDefault(const DefaultTable* table) {
+  // Abort if any of the fields are not set to their default values.
+  if (table->b() != false) {
+    std::abort();
+  }
+  if (table->i8() != 0) {
+    std::abort();
+  }
+  if (table->i16() != 0) {
+    std::abort();
+  }
+  if (table->i32() != 0) {
+    std::abort();
+  }
+  if (table->i64() != 0) {
+    std::abort();
+  }
+  if (table->u8() != 0) {
+    std::abort();
+  }
+  if (table->u16() != 0) {
+    std::abort();
+  }
+  if (table->u32() != 0) {
+    std::abort();
+  }
+  if (table->u64() != 0) {
+    std::abort();
+  }
+  if (table->f() != 0.0f) {
+    std::abort();
+  }
+  if (table->d() != 0.0) {
+    std::abort();
+  }
+  if (table->str() != nullptr) {
+    std::abort();
+  }
+  if (table->ei8() != fuzztest::internal::ByteEnum_First) {
+    std::abort();
+  }
+  if (table->ei16() != fuzztest::internal::ShortEnum_First) {
+    std::abort();
+  }
+  if (table->ei32() != fuzztest::internal::IntEnum_First) {
+    std::abort();
+  }
+  if (table->ei64() != fuzztest::internal::LongEnum_First) {
+    std::abort();
+  }
+  if (table->eu8() != fuzztest::internal::UByteEnum_First) {
+    std::abort();
+  }
+  if (table->eu16() != fuzztest::internal::UShortEnum_First) {
+    std::abort();
+  }
+  if (table->eu32() != fuzztest::internal::UIntEnum_First) {
+    std::abort();
+  }
+  if (table->eu64() != fuzztest::internal::ULongEnum_First) {
+    std::abort();
+  }
+}
+FUZZ_TEST(MySuite, FlatbuffersFailsWhenFieldsAreNotDefault);
 }  // namespace
diff --git a/fuzztest/BUILD b/fuzztest/BUILD
index 7dc2a14..d184dc1 100644
--- a/fuzztest/BUILD
+++ b/fuzztest/BUILD
@@ -205,6 +205,14 @@
 )
 
 cc_library(
+    name = "flatbuffers",
+    hdrs = ["flatbuffers.h"],
+    deps = [
+        "@com_google_fuzztest//fuzztest/internal/domains:flatbuffers_domain_impl",
+    ],
+)
+
+cc_library(
     name = "fuzzing_bit_gen",
     srcs = ["fuzzing_bit_gen.cc"],
     hdrs = ["fuzzing_bit_gen.h"],
diff --git a/fuzztest/CMakeLists.txt b/fuzztest/CMakeLists.txt
index 7d191f7..5a664f2 100644
--- a/fuzztest/CMakeLists.txt
+++ b/fuzztest/CMakeLists.txt
@@ -88,6 +88,40 @@
     fuzztest::fuzztest_macros
 )
 
+if (FUZZTEST_BUILD_FLATBUFFERS)
+  fuzztest_cc_library(
+    NAME
+      flatbuffers
+    HDRS
+      "flatbuffers.h"
+      "internal/domains/flatbuffers_domain_impl.h"
+    SRCS
+      "internal/domains/flatbuffers_domain_impl.cc"
+    DEPS
+      absl::algorithm_container
+      absl::core_headers
+      absl::flat_hash_map
+      absl::flat_hash_set
+      absl::nullability
+      absl::random_bit_gen_ref
+      absl::random_distributions
+      absl::random_random
+      absl::status
+      absl::statusor
+      absl::str_format
+      absl::strings
+      absl::synchronization
+      flatbuffers
+      fuzztest::any
+      fuzztest::domain_core
+      fuzztest::logging
+      fuzztest::meta
+      fuzztest::serialization
+      fuzztest::status
+      fuzztest::type_support
+  )
+endif()
+
 fuzztest_cc_library(
   NAME
     fuzztest_gtest_main
diff --git a/fuzztest/flatbuffers.h b/fuzztest/flatbuffers.h
new file mode 100644
index 0000000..b70ed36
--- /dev/null
+++ b/fuzztest/flatbuffers.h
@@ -0,0 +1,19 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef FUZZTEST_FUZZTEST_FLATBUFFERS_H_
+#define FUZZTEST_FUZZTEST_FLATBUFFERS_H_
+
+#include "./fuzztest/internal/domains/flatbuffers_domain_impl.h"  // IWYU pragma: export
+#endif  // FUZZTEST_FUZZTEST_FLATBUFFERS_H_
diff --git a/fuzztest/internal/BUILD b/fuzztest/internal/BUILD
index 5f88da0..c79706d 100644
--- a/fuzztest/internal/BUILD
+++ b/fuzztest/internal/BUILD
@@ -12,6 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+load("@flatbuffers//:build_defs.bzl", "flatbuffer_library_public")
 load("@rules_proto//proto:defs.bzl", "proto_library")
 
 package(default_visibility = ["@com_google_fuzztest//:__subpackages__"])
@@ -539,3 +540,35 @@
         "@protobuf",
     ],
 )
+
+flatbuffer_library_public(
+    name = "test_flatbuffers_fbs",
+    srcs = ["test_flatbuffers.fbs"],
+    outs = [
+        "test_flatbuffers_bfbs_generated.h",
+        "test_flatbuffers_generated.h",
+    ],
+    flatc_args = [
+        "--no-union-value-namespacing",
+        "--gen-name-strings",
+        "--bfbs-gen-embed",
+    ],
+    language_flag = "-c",
+)
+
+cc_library(
+    name = "test_flatbuffers_cc_fbs",
+    srcs = [":test_flatbuffers_fbs"],
+    hdrs = [":test_flatbuffers_fbs"],
+    features = ["-parse_headers"],
+    deps = ["@flatbuffers//:runtime_cc"],
+)
+
+# Stops build_cleaner from generating flatbuffers_library for the test flatbuffers schema.
+filegroup(
+    name = "build_cleaner_ignore",
+    srcs = [
+        "test_flatbuffers_fbs",
+    ],
+    tags = ["ignore_srcs"],
+)
diff --git a/fuzztest/internal/CMakeLists.txt b/fuzztest/internal/CMakeLists.txt
index 8fd2e4e..61f6da8 100644
--- a/fuzztest/internal/CMakeLists.txt
+++ b/fuzztest/internal/CMakeLists.txt
@@ -494,6 +494,21 @@
   TESTONLY
 )
 
+if (FUZZTEST_BUILD_FLATBUFFERS)
+  fuzztest_flatbuffers_generate_headers(
+    TARGET 
+      test_flatbuffers_headers
+    SCHEMAS
+      "test_flatbuffers.fbs"
+    FLAGS 
+      --bfbs-gen-embed --gen-name-strings
+    TESTONLY
+  )
+  add_dependencies(fuzztest_test_flatbuffers_headers
+    fuzztest_GENERATE_test_flatbuffers_headers
+  )
+endif()
+
 fuzztest_cc_library(
   NAME
     type_support
diff --git a/fuzztest/internal/domains/BUILD b/fuzztest/internal/domains/BUILD
index 5a1d738..f5a37a9 100644
--- a/fuzztest/internal/domains/BUILD
+++ b/fuzztest/internal/domains/BUILD
@@ -188,6 +188,36 @@
 )
 
 cc_library(
+    name = "flatbuffers_domain_impl",
+    srcs = ["flatbuffers_domain_impl.cc"],
+    hdrs = ["flatbuffers_domain_impl.h"],
+    deps = [
+        ":core_domains_impl",
+        "@abseil-cpp//absl/algorithm:container",
+        "@abseil-cpp//absl/base:core_headers",
+        "@abseil-cpp//absl/base:nullability",
+        "@abseil-cpp//absl/container:flat_hash_map",
+        "@abseil-cpp//absl/container:flat_hash_set",
+        "@abseil-cpp//absl/random",
+        "@abseil-cpp//absl/random:bit_gen_ref",
+        "@abseil-cpp//absl/random:distributions",
+        "@abseil-cpp//absl/status",
+        "@abseil-cpp//absl/status:statusor",
+        "@abseil-cpp//absl/strings",
+        "@abseil-cpp//absl/strings:str_format",
+        "@abseil-cpp//absl/synchronization",
+        "@com_google_fuzztest//fuzztest:domain_core",
+        "@com_google_fuzztest//fuzztest/internal:any",
+        "@com_google_fuzztest//fuzztest/internal:logging",
+        "@com_google_fuzztest//fuzztest/internal:meta",
+        "@com_google_fuzztest//fuzztest/internal:serialization",
+        "@com_google_fuzztest//fuzztest/internal:status",
+        "@com_google_fuzztest//fuzztest/internal:type_support",
+        "@flatbuffers//:runtime_cc",
+    ],
+)
+
+cc_library(
     name = "regexp_dfa",
     srcs = ["regexp_dfa.cc"],
     hdrs = ["regexp_dfa.h"],
diff --git a/fuzztest/internal/domains/arbitrary_impl.h b/fuzztest/internal/domains/arbitrary_impl.h
index d151d50..f6b1702 100644
--- a/fuzztest/internal/domains/arbitrary_impl.h
+++ b/fuzztest/internal/domains/arbitrary_impl.h
@@ -458,7 +458,9 @@
                         // Monostates have their own domain.
                         !is_monostate_v<T> &&
                         // std::array uses the Tuple domain.
-                        !is_array_v<T>>>
+                        !is_array_v<T> &&
+                        // Flatbuffers tables have their own domain.
+                        !is_flatbuffers_table_v<T>>>
     : public decltype(DetectAggregateOfImpl<T>()) {};
 
 // Arbitrary for std::pair.
diff --git a/fuzztest/internal/domains/flatbuffers_domain_impl.cc b/fuzztest/internal/domains/flatbuffers_domain_impl.cc
new file mode 100644
index 0000000..0306e9a
--- /dev/null
+++ b/fuzztest/internal/domains/flatbuffers_domain_impl.cc
@@ -0,0 +1,297 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include "./fuzztest/internal/domains/flatbuffers_domain_impl.h"
+
+#include <cstdint>
+#include <optional>
+#include <utility>
+
+#include "absl/base/nullability.h"
+#include "absl/container/flat_hash_map.h"
+#include "absl/random/bit_gen_ref.h"
+#include "absl/random/distributions.h"
+#include "absl/status/status.h"
+#include "absl/synchronization/mutex.h"
+#include "flatbuffers/base.h"
+#include "flatbuffers/flatbuffer_builder.h"
+#include "flatbuffers/reflection.h"
+#include "flatbuffers/reflection_generated.h"
+#include "./fuzztest/domain_core.h"
+#include "./fuzztest/internal/any.h"
+#include "./fuzztest/internal/domains/domain_base.h"
+#include "./fuzztest/internal/domains/domain_type_erasure.h"
+#include "./fuzztest/internal/serialization.h"
+
+namespace fuzztest::internal {
+
+FlatbuffersTableUntypedDomainImpl::FlatbuffersTableUntypedDomainImpl(
+    const reflection::Schema* absl_nonnull schema,
+    const reflection::Object* absl_nonnull table_object)
+    : schema_(schema), table_object_(table_object) {}
+
+FlatbuffersTableUntypedDomainImpl::FlatbuffersTableUntypedDomainImpl(
+    const FlatbuffersTableUntypedDomainImpl& other)
+    : DomainBase(other),
+      schema_(other.schema_),
+      table_object_(other.table_object_) {
+  absl::MutexLock l_other(&other.mutex_);
+  absl::MutexLock l_this(&mutex_);
+  domains_ = other.domains_;
+}
+
+FlatbuffersTableUntypedDomainImpl& FlatbuffersTableUntypedDomainImpl::operator=(
+    const FlatbuffersTableUntypedDomainImpl& other) {
+  DomainBase::operator=(other);
+  schema_ = other.schema_;
+  table_object_ = other.table_object_;
+  absl::MutexLock l_other(&other.mutex_);
+  absl::MutexLock l_this(&mutex_);
+  domains_ = other.domains_;
+  return *this;
+}
+
+FlatbuffersTableUntypedDomainImpl::FlatbuffersTableUntypedDomainImpl(
+    FlatbuffersTableUntypedDomainImpl&& other)
+    : schema_(other.schema_), table_object_(other.table_object_) {
+  absl::MutexLock l_other(&other.mutex_);
+  absl::MutexLock l_this(&mutex_);
+  domains_ = std::move(other.domains_);
+  DomainBase::operator=(std::move(other));
+}
+
+FlatbuffersTableUntypedDomainImpl& FlatbuffersTableUntypedDomainImpl::operator=(
+    FlatbuffersTableUntypedDomainImpl&& other) {
+  schema_ = other.schema_;
+  table_object_ = other.table_object_;
+  absl::MutexLock l_other(&other.mutex_);
+  absl::MutexLock l_this(&mutex_);
+  domains_ = std::move(other.domains_);
+  DomainBase::operator=(std::move(other));
+  return *this;
+}
+
+FlatbuffersTableUntypedDomainImpl::corpus_type
+FlatbuffersTableUntypedDomainImpl::Init(absl::BitGenRef prng) {
+  if (auto seed = this->MaybeGetRandomSeed(prng)) {
+    return *seed;
+  }
+  corpus_type val;
+  for (const auto* field : *table_object_->fields()) {
+    VisitFlatbufferField(field, InitializeVisitor{*this, prng, val});
+  }
+  return val;
+}
+
+// Mutates the corpus value.
+void FlatbuffersTableUntypedDomainImpl::Mutate(
+    corpus_type& val, absl::BitGenRef prng,
+    const domain_implementor::MutationMetadata& metadata, bool only_shrink) {
+  uint64_t field_count = 0;
+  for (const auto* field : *table_object_->fields()) {
+    VisitFlatbufferField(field, CountNumberOfMutableFieldsVisitor{
+                                    *this, field_count, val, only_shrink});
+  }
+  auto selected_field_index = absl::Uniform(prng, 0ul, field_count);
+
+  MutateSelectedField(val, prng, metadata, only_shrink, selected_field_index);
+}
+
+uint64_t FlatbuffersTableUntypedDomainImpl::CountNumberOfFields(
+    corpus_type& val) {
+  uint64_t field_count = 0;
+  for (const auto* field : *table_object_->fields()) {
+    VisitFlatbufferField(
+        field, CountNumberOfMutableFieldsVisitor{*this, field_count, val});
+  }
+  return field_count;
+}
+
+uint64_t FlatbuffersTableUntypedDomainImpl::MutateSelectedField(
+    corpus_type& val, absl::BitGenRef prng,
+    const domain_implementor::MutationMetadata& metadata, bool only_shrink,
+    uint64_t selected_field_index) {
+  uint64_t field_counter = 0;
+  for (const auto* field : *table_object_->fields()) {
+    if (!IsSupportedField(field)) {
+      if (only_shrink && !val.contains(field->id())) continue;
+    }
+
+    if (field_counter == selected_field_index) {
+      VisitFlatbufferField(
+          field, MutateVisitor{*this, prng, metadata, only_shrink, val});
+      return field_counter;
+    }
+    field_counter++;
+
+    // TODO: Add support for tables.
+    // TODO: Add support for vectors.
+    // TODO: Add support for unions.
+
+    if (field_counter > selected_field_index) {
+      return field_counter;
+    }
+  }
+  return field_counter;
+}
+
+absl::Status FlatbuffersTableUntypedDomainImpl::ValidateCorpusValue(
+    const corpus_type& corpus_value) const {
+  for (const auto* field : *table_object_->fields()) {
+    absl::Status result;
+    GenericDomainCorpusType field_corpus;
+    if (auto it = corpus_value.find(field->id()); it != corpus_value.end()) {
+      field_corpus = it->second;
+    }
+    VisitFlatbufferField(field, ValidateVisitor{*this, field_corpus, result});
+    if (!result.ok()) return result;
+  }
+  return absl::OkStatus();
+}
+
+std::optional<FlatbuffersTableUntypedDomainImpl::corpus_type>
+FlatbuffersTableUntypedDomainImpl::FromValue(const value_type& value) const {
+  if (value == nullptr) {
+    return std::nullopt;
+  }
+  corpus_type ret;
+  for (const auto* field : *table_object_->fields()) {
+    VisitFlatbufferField(field, FromValueVisitor{*this, value, ret});
+  }
+  return ret;
+}
+
+std::optional<FlatbuffersTableUntypedDomainImpl::corpus_type>
+FlatbuffersTableUntypedDomainImpl::ParseCorpus(const IRObject& obj) const {
+  corpus_type out;
+  auto subs = obj.Subs();
+  if (!subs) {
+    return std::nullopt;
+  }
+  // Follows the structure created by `SerializeCorpus` to deserialize the
+  // IRObject.
+
+  // subs->size() represents the number of fields in the table.
+  out.reserve(subs->size());
+  for (const auto& sub : *subs) {
+    auto pair_subs = sub.Subs();
+    // Each field is represented by a pair of field id and the serialized
+    // corpus value.
+    if (!pair_subs.has_value() || pair_subs->size() != 2) {
+      return std::nullopt;
+    }
+
+    // Deserialize the field id.
+    auto id = (*pair_subs)[0].GetScalar<typename corpus_type::key_type>();
+    if (!id.has_value()) {
+      return std::nullopt;
+    }
+
+    // Get information about the field from reflection.
+    const reflection::Field* absl_nullable field = GetFieldById(*id);
+    if (field == nullptr) {
+      return std::nullopt;
+    }
+
+    // Deserialize the field corpus value.
+    std::optional<GenericDomainCorpusType> inner_parsed;
+    VisitFlatbufferField(field,
+                         ParseVisitor{*this, (*pair_subs)[1], inner_parsed});
+    if (!inner_parsed) {
+      return std::nullopt;
+    }
+    out[id.value()] = *std::move(inner_parsed);
+  }
+  return out;
+}
+
+IRObject FlatbuffersTableUntypedDomainImpl::SerializeCorpus(
+    const corpus_type& value) const {
+  IRObject out;
+  auto& subs = out.MutableSubs();
+  subs.reserve(value.size());
+
+  // Each field is represented by a pair of field id and the serialized
+  // corpus value.
+  for (const auto& [id, field_corpus] : value) {
+    // Get information about the field from reflection.
+    const reflection::Field* absl_nullable field = GetFieldById(id);
+    if (field == nullptr) {
+      continue;
+    }
+    IRObject& pair = subs.emplace_back();
+    auto& pair_subs = pair.MutableSubs();
+    pair_subs.reserve(2);
+
+    // Serialize the field id.
+    pair_subs.emplace_back(field->id());
+
+    // Serialize the field corpus value.
+    VisitFlatbufferField(
+        field, SerializeVisitor{*this, field_corpus, pair_subs.emplace_back()});
+  }
+  return out;
+}
+
+bool FlatbuffersTableUntypedDomainImpl::IsSupportedField(
+    const reflection::Field* absl_nonnull field) const {
+  auto base_type = field->type()->base_type();
+  if (base_type == reflection::BaseType::UType) return false;
+  if (flatbuffers::IsScalar(base_type)) return true;
+  if (base_type == reflection::BaseType::String) return true;
+  return false;
+}
+
+uint32_t FlatbuffersTableUntypedDomainImpl::BuildTable(
+    const corpus_type& value, flatbuffers::FlatBufferBuilder& builder) const {
+  // Add all the fields to the builder.
+
+  // Offsets is the map of field id to its offset in the table.
+  absl::flat_hash_map<typename corpus_type::key_type, flatbuffers::uoffset_t>
+      offsets;
+
+  // Some fields are stored inline in the flatbuffer table itself (a.k.a
+  // "inline fields") and some are referenced by their offsets (a.k.a. "out of
+  // line fields").
+  //
+  // "Out of line fields" shall be added to the builder first, so that we can
+  // refer to them in the final table.
+  for (const auto& [id, field_corpus] : value) {
+    const reflection::Field* absl_nullable field = GetFieldById(id);
+    if (field == nullptr) {
+      continue;
+    }
+    // Take care of strings, and tables.
+    VisitFlatbufferField(
+        field, TableFieldBuilderVisitor{*this, builder, offsets, field_corpus});
+  }
+
+  // Now it is time to build the final table.
+  uint32_t table_start = builder.StartTable();
+  for (const auto& [id, field_corpus] : value) {
+    const reflection::Field* absl_nullable field = GetFieldById(id);
+    if (field == nullptr) {
+      continue;
+    }
+
+    // Visit all fields.
+    //
+    // Inline fields will be stored in the table itself, out of line fields
+    // will be referenced by their offsets.
+    VisitFlatbufferField(
+        field, TableBuilderVisitor{*this, builder, offsets, field_corpus});
+  }
+  return builder.EndTable(table_start);
+}
+
+}  // namespace fuzztest::internal
diff --git a/fuzztest/internal/domains/flatbuffers_domain_impl.h b/fuzztest/internal/domains/flatbuffers_domain_impl.h
new file mode 100644
index 0000000..82e44bc
--- /dev/null
+++ b/fuzztest/internal/domains/flatbuffers_domain_impl.h
@@ -0,0 +1,690 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef FUZZTEST_FUZZTEST_INTERNAL_DOMAINS_FLATBUFFERS_DOMAIN_IMPL_H_
+#define FUZZTEST_FUZZTEST_INTERNAL_DOMAINS_FLATBUFFERS_DOMAIN_IMPL_H_
+
+#include <algorithm>
+#include <cstdint>
+#include <optional>
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "absl/algorithm/container.h"
+#include "absl/base/nullability.h"
+#include "absl/base/thread_annotations.h"
+#include "absl/container/flat_hash_map.h"
+#include "absl/random/bit_gen_ref.h"
+#include "absl/status/status.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/str_format.h"
+#include "absl/synchronization/mutex.h"
+#include "flatbuffers/base.h"
+#include "flatbuffers/flatbuffer_builder.h"
+#include "flatbuffers/reflection_generated.h"
+#include "flatbuffers/string.h"
+#include "flatbuffers/table.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/domain_base.h"
+#include "./fuzztest/internal/domains/domain_type_erasure.h"
+#include "./fuzztest/internal/domains/element_of_impl.h"
+#include "./fuzztest/internal/logging.h"
+#include "./fuzztest/internal/meta.h"
+#include "./fuzztest/internal/serialization.h"
+#include "./fuzztest/internal/status.h"
+
+namespace fuzztest::internal {
+
+//
+// Flatbuffers enum detection.
+//
+template <typename Underlying,
+          typename = std::enable_if_t<std::is_integral_v<Underlying> &&
+                                      !std::is_same_v<Underlying, bool>>>
+struct FlatbuffersEnumTag {
+  using type = Underlying;
+};
+
+template <typename T>
+struct is_flatbuffers_enum_tag : std::false_type {};
+
+template <typename Underlying>
+struct is_flatbuffers_enum_tag<FlatbuffersEnumTag<Underlying>>
+    : std::true_type {};
+
+template <typename T>
+inline constexpr bool is_flatbuffers_enum_tag_v =
+    is_flatbuffers_enum_tag<T>::value;
+
+struct FlatbuffersArrayTag;
+struct FlatbuffersObjTag;
+struct FlatbuffersUnionTag;
+struct FlatbuffersVectorTag;
+
+// Dynamic to static dispatch visitor pattern.
+template <typename Visitor>
+auto VisitFlatbufferField(const reflection::Field* absl_nonnull field,
+                          Visitor visitor) {
+  auto field_index = field->type()->index();
+  switch (field->type()->base_type()) {
+    case reflection::BaseType::Bool:
+      visitor.template Visit<bool>(field);
+      break;
+    case reflection::BaseType::Byte:
+      if (field_index >= 0) {
+        visitor.template Visit<FlatbuffersEnumTag<int8_t>>(field);
+      } else {
+        visitor.template Visit<int8_t>(field);
+      }
+      break;
+    case reflection::BaseType::Short:
+      if (field_index >= 0) {
+        visitor.template Visit<FlatbuffersEnumTag<int16_t>>(field);
+      } else {
+        visitor.template Visit<int16_t>(field);
+      }
+      break;
+    case reflection::BaseType::Int:
+      if (field_index >= 0) {
+        visitor.template Visit<FlatbuffersEnumTag<int32_t>>(field);
+      } else {
+        visitor.template Visit<int32_t>(field);
+      }
+      break;
+    case reflection::BaseType::Long:
+      if (field_index >= 0) {
+        visitor.template Visit<FlatbuffersEnumTag<int64_t>>(field);
+      } else {
+        visitor.template Visit<int64_t>(field);
+      }
+      break;
+    case reflection::BaseType::UByte:
+      if (field_index >= 0) {
+        visitor.template Visit<FlatbuffersEnumTag<uint8_t>>(field);
+      } else {
+        visitor.template Visit<uint8_t>(field);
+      }
+      break;
+    case reflection::BaseType::UShort:
+      if (field_index >= 0) {
+        visitor.template Visit<FlatbuffersEnumTag<uint16_t>>(field);
+      } else {
+        visitor.template Visit<uint16_t>(field);
+      }
+      break;
+    case reflection::BaseType::UInt:
+      if (field_index >= 0) {
+        visitor.template Visit<FlatbuffersEnumTag<uint32_t>>(field);
+      } else {
+        visitor.template Visit<uint32_t>(field);
+      }
+      break;
+    case reflection::BaseType::ULong:
+      if (field_index >= 0) {
+        visitor.template Visit<FlatbuffersEnumTag<uint64_t>>(field);
+      } else {
+        visitor.template Visit<uint64_t>(field);
+      }
+      break;
+    case reflection::BaseType::Float:
+      visitor.template Visit<float>(field);
+      break;
+    case reflection::BaseType::Double:
+      visitor.template Visit<double>(field);
+      break;
+    case reflection::BaseType::String:
+      visitor.template Visit<std::string>(field);
+      break;
+    case reflection::BaseType::Vector:
+    case reflection::BaseType::Vector64:
+      visitor.template Visit<FlatbuffersVectorTag>(field);
+      break;
+    case reflection::BaseType::Array:
+      visitor.template Visit<FlatbuffersArrayTag>(field);
+      break;
+    case reflection::BaseType::Obj:
+      visitor.template Visit<FlatbuffersObjTag>(field);
+      break;
+    case reflection::BaseType::Union:
+      visitor.template Visit<FlatbuffersUnionTag>(field);
+      break;
+    case reflection::BaseType::UType:
+      // Noop
+      break;
+    default:
+      FUZZTEST_INTERNAL_CHECK(false, absl::StrCat("Unsupported base type: ",
+                                                  field->type()->base_type()));
+  }
+}
+
+// Forward declaration of the domain factory for flatbuffers fields.
+template <typename T>
+auto GetDefaultDomain(const reflection::Schema* absl_nonnull schema,
+                      const reflection::Field* absl_nonnull field);
+
+// 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<
+          /*Derived=*/FlatbuffersTableUntypedDomainImpl,
+          /*ValueType=*/const flatbuffers::Table* absl_nonnull,
+          /*CorpusType=*/
+          absl::flat_hash_map<
+              decltype(static_cast<reflection::Field*>(nullptr)->id()),
+              GenericDomainCorpusType>> {
+ public:
+  template <typename T>
+  friend class FlatbuffersTableDomainImpl;
+
+  using typename FlatbuffersTableUntypedDomainImpl::DomainBase::corpus_type;
+  using typename FlatbuffersTableUntypedDomainImpl::DomainBase::value_type;
+
+  explicit FlatbuffersTableUntypedDomainImpl(
+      const reflection::Schema* absl_nonnull schema,
+      const reflection::Object* absl_nonnull table_object);
+
+  FlatbuffersTableUntypedDomainImpl(
+      const FlatbuffersTableUntypedDomainImpl& other);
+
+  FlatbuffersTableUntypedDomainImpl& operator=(
+      const FlatbuffersTableUntypedDomainImpl& other);
+
+  FlatbuffersTableUntypedDomainImpl(FlatbuffersTableUntypedDomainImpl&& other);
+
+  FlatbuffersTableUntypedDomainImpl& operator=(
+      FlatbuffersTableUntypedDomainImpl&& other);
+
+  // Initializes the corpus value.
+  corpus_type Init(absl::BitGenRef prng);
+
+  // Mutates the corpus value.
+  void Mutate(corpus_type& val, absl::BitGenRef prng,
+              const domain_implementor::MutationMetadata& metadata,
+              bool only_shrink);
+
+  // Counts the number of fields that can be mutated.
+  uint64_t CountNumberOfFields(corpus_type& val);
+
+  // Mutates the selected field.
+  // The selected field index is based on the flattened tree.
+  uint64_t MutateSelectedField(
+      corpus_type& val, absl::BitGenRef prng,
+      const domain_implementor::MutationMetadata& metadata, bool only_shrink,
+      uint64_t selected_field_index);
+
+  auto GetPrinter() const { return Printer{*this}; }
+
+  absl::Status ValidateCorpusValue(const corpus_type& corpus_value) const;
+
+  value_type GetValue(const corpus_type& value) const {
+    FUZZTEST_INTERNAL_CHECK(
+        false, "GetValue is not supported for the untyped Flatbuffers domain.");
+    // Untyped domain does not support GetValue since if it is a nested table it
+    // would need the top level table corpus value to be able to build it.
+    return nullptr;
+  }
+
+  // Converts the table pointer to a corpus value.
+  std::optional<corpus_type> FromValue(const value_type& value) const;
+
+  // Converts the IRObject to a corpus value.
+  std::optional<corpus_type> ParseCorpus(const IRObject& obj) const;
+
+  // Converts the corpus value to an IRObject.
+  IRObject SerializeCorpus(const corpus_type& value) const;
+
+ private:
+  const reflection::Schema* absl_nonnull schema_;
+  const reflection::Object* absl_nonnull table_object_;
+  mutable absl::Mutex mutex_;
+  mutable absl::flat_hash_map<typename corpus_type::key_type, CopyableAny>
+      domains_ ABSL_GUARDED_BY(mutex_);
+
+  bool IsSupportedField(const reflection::Field* absl_nonnull field) const;
+
+  uint32_t BuildTable(const corpus_type& value,
+                      flatbuffers::FlatBufferBuilder& builder) const;
+
+  // Returns the domain for the given field.
+  // The domain is cached, and the same instance is returned for the same field.
+  template <typename T>
+  auto& GetCachedDomain(const reflection::Field* field) const {
+    auto get_opt_domain = [this, field]() {
+      auto opt_domain = OptionalOf(GetDefaultDomain<T>(schema_, field));
+      if (!field->optional()) {
+        opt_domain.SetWithoutNull();
+      }
+      return Domain<value_type_t<decltype(opt_domain)>>{opt_domain};
+    };
+
+    using DomainT = decltype(get_opt_domain());
+    // Do the operation under a lock to prevent race conditions in `const`
+    // methods.
+    absl::MutexLock l(&mutex_);
+    auto it = domains_.find(field->id());
+    if (it == domains_.end()) {
+      it = domains_
+               .try_emplace(field->id(), std::in_place_type<DomainT>,
+                            get_opt_domain())
+               .first;
+    }
+    return it->second.template GetAs<DomainT>();
+  }
+
+  const reflection::Field* absl_nullable GetFieldById(
+      typename corpus_type::key_type id) const {
+    const auto it =
+        absl::c_find_if(*table_object_->fields(),
+                        [id](const auto* field) { return field->id() == id; });
+    return it != table_object_->fields()->end() ? *it : nullptr;
+  }
+
+  struct SerializeVisitor {
+    const FlatbuffersTableUntypedDomainImpl& self;
+    const GenericDomainCorpusType& corpus_value;
+    IRObject& out;
+
+    template <typename T>
+    void Visit(const reflection::Field* absl_nonnull field) {
+      out = self.GetCachedDomain<T>(field).SerializeCorpus(corpus_value);
+    }
+  };
+
+  struct FromValueVisitor {
+    const FlatbuffersTableUntypedDomainImpl& self;
+    value_type user_value;
+    corpus_type& corpus_value;
+
+    template <typename T>
+    void Visit(const reflection::Field* absl_nonnull field) const {
+      [[maybe_unused]]
+      reflection::BaseType base_type = field->type()->base_type();
+      auto& domain = self.GetCachedDomain<T>(field);
+      value_type_t<std::decay_t<decltype(domain)>> inner_value;
+
+      if constexpr (is_flatbuffers_enum_tag_v<T>) {
+        if (!field->optional() || user_value->CheckField(field->offset())) {
+          inner_value = user_value->GetField<typename T::type>(
+              field->offset(), field->default_integer());
+        }
+      } else if constexpr (std::is_integral_v<T>) {
+        if (!field->optional() || user_value->CheckField(field->offset())) {
+          inner_value = std::optional(user_value->GetField<T>(
+              field->offset(), field->default_integer()));
+        }
+      } else if constexpr (std::is_floating_point_v<T>) {
+        if (!field->optional() || user_value->CheckField(field->offset())) {
+          inner_value = std::optional(
+              user_value->GetField<T>(field->offset(), field->default_real()));
+        }
+      } else if constexpr (std::is_same_v<T, std::string>) {
+        if (user_value->CheckField(field->offset())) {
+          inner_value = std::optional(
+              user_value->GetPointer<flatbuffers::String*>(field->offset())
+                  ->str());
+        }
+      }
+
+      auto inner = domain.FromValue(inner_value);
+      if (inner) {
+        corpus_value[field->id()] = *std::move(inner);
+      }
+    };
+  };
+
+  // Create out-of-line table fields, see `BuildTable` for details.
+  struct TableFieldBuilderVisitor {
+    const FlatbuffersTableUntypedDomainImpl& self;
+    flatbuffers::FlatBufferBuilder& builder;
+    absl::flat_hash_map<typename corpus_type::key_type, flatbuffers::uoffset_t>&
+        offsets;
+    const typename corpus_type::mapped_type& corpus_value;
+
+    template <typename T>
+    void Visit(const reflection::Field* absl_nonnull field) const {
+      if constexpr (std::is_same_v<T, std::string>) {
+        auto& domain = self.GetCachedDomain<T>(field);
+        auto user_value = domain.GetValue(corpus_value);
+        if (user_value.has_value()) {
+          auto offset =
+              builder.CreateString(user_value->data(), user_value->size()).o;
+          offsets.insert({field->id(), offset});
+        }
+      }
+    }
+  };
+
+  // Create complete table: store "inline fields" values inline, and store just
+  // offsets for "out-of-line fields". See `BuildTable` for details.
+  struct TableBuilderVisitor {
+    const FlatbuffersTableUntypedDomainImpl& self;
+    flatbuffers::FlatBufferBuilder& builder;
+    const 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>
+    void Visit(const reflection::Field* absl_nonnull field) const {
+      if constexpr (std::is_integral_v<T> || std::is_floating_point_v<T> ||
+                    is_flatbuffers_enum_tag_v<T>) {
+        auto& domain = self.GetCachedDomain<T>(field);
+        auto v = domain.GetValue(corpus_value);
+        if (!v) {
+          return;
+        }
+        // Store "inline field" value inline.
+        builder.AddElement(field->offset(), v.value());
+      } else if constexpr (std::is_same_v<T, std::string>) {
+        // "Out-of-line field". Store just offset.
+        if (auto it = offsets.find(field->id()); it != offsets.end()) {
+          builder.AddOffset(
+              field->offset(),
+              flatbuffers::Offset<flatbuffers::String>(it->second));
+        }
+      }
+    }
+  };
+
+  struct ParseVisitor {
+    const FlatbuffersTableUntypedDomainImpl& self;
+    const IRObject& obj;
+    std::optional<GenericDomainCorpusType>& out;
+
+    template <typename T>
+    void Visit(const reflection::Field* absl_nonnull field) {
+      out = self.GetCachedDomain<T>(field).ParseCorpus(obj);
+    }
+  };
+
+  struct ValidateVisitor {
+    const FlatbuffersTableUntypedDomainImpl& self;
+    const GenericDomainCorpusType& corpus_value;
+    absl::Status& out;
+
+    template <typename T>
+    void Visit(const reflection::Field* absl_nonnull field) {
+      auto& domain = self.GetCachedDomain<T>(field);
+      out = domain.ValidateCorpusValue(corpus_value);
+      if (!out.ok()) {
+        out = Prefix(out, absl::StrCat("Invalid value for field ",
+                                       field->name()->str()));
+      }
+    }
+  };
+
+  struct InitializeVisitor {
+    FlatbuffersTableUntypedDomainImpl& self;
+    absl::BitGenRef prng;
+    corpus_type& val;
+
+    template <typename T>
+    void Visit(const reflection::Field* absl_nonnull field) {
+      auto& domain = self.GetCachedDomain<T>(field);
+      val[field->id()] = domain.Init(prng);
+    }
+  };
+
+  struct CountNumberOfMutableFieldsVisitor {
+    const FlatbuffersTableUntypedDomainImpl& self;
+    uint64_t& total_weight;
+    corpus_type& val;
+    bool only_shrink = false;
+
+    template <typename T>
+    void Visit(const reflection::Field* absl_nonnull field) const {
+      if (!self.IsSupportedField(field)) return;
+      if (only_shrink && !val.contains(field->id())) return;
+
+      // Add the weight of the field itself.
+      total_weight += 1;
+
+      auto& domain = self.GetCachedDomain<T>(field);
+      if (auto it = val.find(field->id()); it != val.end()) {
+        // Add the weight of the field corpus.
+        total_weight += domain.CountNumberOfFields(it->second);
+      }
+    }
+  };
+
+  struct MutateVisitor {
+    FlatbuffersTableUntypedDomainImpl& self;
+    absl::BitGenRef prng;
+    const domain_implementor::MutationMetadata& metadata;
+    bool only_shrink;
+    corpus_type& corpus_value;
+
+    template <typename T>
+    void Visit(const reflection::Field* absl_nonnull field) {
+      auto& domain = self.GetCachedDomain<T>(field);
+      auto it = corpus_value.find(field->id());
+      if (it == corpus_value.end()) {
+        if (only_shrink) return;
+        it = corpus_value.try_emplace(field->id(), domain.Init(prng)).first;
+      }
+      domain.Mutate(it->second, prng, metadata, only_shrink);
+    }
+  };
+
+  struct Printer {
+    const FlatbuffersTableUntypedDomainImpl& self;
+
+    void PrintCorpusValue(const corpus_type& value,
+                          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);
+      }
+      // Sort the field ids to make the output deterministic.
+      std::sort(field_ids.begin(), field_ids.end());
+
+      absl::Format(out, "{");
+      bool first = true;
+      for (const auto id : field_ids) {
+        if (!first) {
+          absl::Format(out, ", ");
+        }
+        const reflection::Field* absl_nullable field = self.GetFieldById(id);
+        if (field == nullptr) {
+          absl::Format(out, "<unknown field: %d>", id);
+        } else {
+          VisitFlatbufferField(field,
+                               PrinterVisitor{self, value.at(id), out, mode});
+        }
+        first = false;
+      }
+      absl::Format(out, "}");
+    }
+  };
+
+  struct PrinterVisitor {
+    const FlatbuffersTableUntypedDomainImpl& self;
+    const GenericDomainCorpusType& val;
+    domain_implementor::RawSink out;
+    domain_implementor::PrintMode mode;
+
+    template <typename T>
+    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);
+    }
+  };
+};
+
+// Domain factory for flatbuffers fields.
+template <typename T>
+auto GetDefaultDomain(const reflection::Schema* absl_nonnull schema,
+                      const reflection::Field* absl_nonnull field) {
+  // Used to satisfy the compiler return type deduction rules.
+  auto placeholder = Arbitrary<bool>();
+
+  if constexpr (std::is_same_v<T, FlatbuffersArrayTag>) {
+    // TODO: support arrays.
+    return placeholder;
+  } else if constexpr (is_flatbuffers_enum_tag_v<T>) {
+    auto enum_object = schema->enums()->Get(field->type()->index());
+    // For enums, build the list of valid labels.
+    std::vector<typename T::type> values;
+    values.reserve(enum_object->values()->size());
+    for (const auto* value : *enum_object->values()) {
+      values.push_back(value->value());
+    }
+    // Delay instantiation. The Domain class is not fully defined at this
+    // point yet, and neither is ElementOfImpl.
+    using LazyInt = MakeDependentType<typename T::type, T>;
+    return ElementOfImpl<LazyInt>(std::move(values));
+  } else if constexpr (std::is_same_v<T, FlatbuffersObjTag>) {
+    // TODO: support objects.
+    return placeholder;
+  } 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 {
+    return Arbitrary<T>();
+  }
+}
+
+// Corpus type for the table domain
+struct FlatbuffersTableDomainCorpusType {
+  // Map of field ids to field values.
+  typename FlatbuffersTableUntypedDomainImpl::corpus_type untyped_corpus;
+  // Serialized flatbuffer.
+  mutable std::vector<uint8_t> buffer;
+};
+
+// Domain implementation for flatbuffers generated table classes.
+// The corpus type is a pair of:
+// - A map of field ids to field values.
+// - The serialized buffer of the table.
+template <typename T>
+class FlatbuffersTableDomainImpl
+    : public fuzztest::domain_implementor::DomainBase<
+          /*Derived=*/FlatbuffersTableDomainImpl<T>,
+          /*ValueType=*/const T*,
+          /*CorpusType=*/FlatbuffersTableDomainCorpusType> {
+ public:
+  using typename FlatbuffersTableDomainImpl::DomainBase::corpus_type;
+  using typename FlatbuffersTableDomainImpl::DomainBase::value_type;
+
+  FlatbuffersTableDomainImpl() {
+    flatbuffers::Verifier verifier(T::BinarySchema::data(),
+                                   T::BinarySchema::size());
+    FUZZTEST_INTERNAL_CHECK(reflection::VerifySchemaBuffer(verifier),
+                            "Invalid schema for flatbuffers table.");
+    auto schema = reflection::GetSchema(T::BinarySchema::data());
+    auto table_object =
+        schema->objects()->LookupByKey(T::GetFullyQualifiedName());
+    inner_ = FlatbuffersTableUntypedDomainImpl{schema, table_object};
+  }
+
+  // Initializes the table with random values.
+  corpus_type Init(absl::BitGenRef prng) {
+    if (auto seed = this->MaybeGetRandomSeed(prng)) {
+      return *seed;
+    }
+    // Create new map of field ids to field values
+    auto val = inner_->Init(prng);
+    // Return corpus value: pair of the map and the serialized buffer.
+    return FlatbuffersTableDomainCorpusType{val, {}};
+  }
+
+  // Returns the number of fields in the table.
+  uint64_t CountNumberOfFields(corpus_type& val) {
+    return inner_->CountNumberOfFields(val.untyped_corpus);
+  }
+
+  // Mutates the given corpus value.
+  void Mutate(corpus_type& val, absl::BitGenRef prng,
+              const domain_implementor::MutationMetadata& metadata,
+              bool only_shrink) {
+    // Modify values in the map.
+    inner_->Mutate(val.untyped_corpus, prng, metadata, only_shrink);
+  }
+
+  // Converts corpus value into the exact flatbuffer.
+  value_type GetValue(const corpus_type& value) const {
+    value.buffer = BuildBuffer(value.untyped_corpus);
+    return flatbuffers::GetRoot<T>(value.buffer.data());
+  }
+
+  // Creates corpus value from the exact flatbuffer.
+  std::optional<corpus_type> FromValue(const value_type& value) const {
+    auto val = inner_->FromValue((const flatbuffers::Table*)value);
+    if (!val.has_value()) return std::nullopt;
+    return std::optional(
+        FlatbuffersTableDomainCorpusType{*val, BuildBuffer(*val)});
+  }
+
+  // Returns the printer for the table.
+  auto GetPrinter() const { return Printer{*inner_}; }
+
+  // Returns the parsed corpus value.
+  std::optional<corpus_type> ParseCorpus(const IRObject& obj) const {
+    auto val = inner_->ParseCorpus(obj);
+    if (!val.has_value()) return std::nullopt;
+    return std::optional(
+        FlatbuffersTableDomainCorpusType{*val, BuildBuffer(*val)});
+  }
+
+  // Returns the serialized corpus value.
+  IRObject SerializeCorpus(const corpus_type& corpus_value) const {
+    return inner_->SerializeCorpus(corpus_value.untyped_corpus);
+  }
+
+  // Returns the status of the given corpus value.
+  absl::Status ValidateCorpusValue(const corpus_type& corpus_value) const {
+    return inner_->ValidateCorpusValue(corpus_value.untyped_corpus);
+  }
+
+ private:
+  std::optional<FlatbuffersTableUntypedDomainImpl> inner_;
+
+  struct Printer {
+    const FlatbuffersTableUntypedDomainImpl& inner;
+
+    void PrintCorpusValue(const corpus_type& value,
+                          domain_implementor::RawSink out,
+                          domain_implementor::PrintMode mode) const {
+      inner.GetPrinter().PrintCorpusValue(value.untyped_corpus, out, mode);
+    }
+  };
+
+  std::vector<uint8_t> BuildBuffer(
+      const corpus_type_t<FlatbuffersTableUntypedDomainImpl>& val) const {
+    flatbuffers::FlatBufferBuilder builder;
+    auto offset = inner_->BuildTable(val, builder);
+    builder.Finish(flatbuffers::Offset<flatbuffers::Table>(offset));
+    auto buffer =
+        std::vector<uint8_t>(builder.GetBufferPointer(),
+                             builder.GetBufferPointer() + builder.GetSize());
+    return buffer;
+  }
+};
+
+template <typename T>
+class ArbitraryImpl<const T*, std::enable_if_t<is_flatbuffers_table_v<T>>>
+    : public FlatbuffersTableDomainImpl<T> {};
+
+}  // namespace fuzztest::internal
+#endif  // FUZZTEST_FUZZTEST_INTERNAL_DOMAINS_FLATBUFFERS_DOMAIN_IMPL_H_
diff --git a/fuzztest/internal/meta.h b/fuzztest/internal/meta.h
index 4ddada1..c36d80b 100644
--- a/fuzztest/internal/meta.h
+++ b/fuzztest/internal/meta.h
@@ -200,6 +200,22 @@
 inline constexpr bool is_protocol_buffer_enum_v =
     IsProtocolBufferEnumImpl<T>(true);
 
+template <typename, typename = void>
+inline constexpr bool is_flatbuffers_table_v = false;
+
+// Flatbuffers tables generated structs do not have a public base class, so we
+// check for a few specific methods:
+//  - T is a struct.
+//  - T has a `Builder` type.
+//  - T has a `BinarySchema` type with a static method `data()` (only available
+//    when passing `--bfbs-gen-embed` to the flatbuffer compiler).
+//  - T has a static method called `GetFullyQualifiedName` (only available when
+//    passing `--gen-name-strings` to the flatbuffer compiler).
+template <typename T>
+inline constexpr bool is_flatbuffers_table_v<
+    T, std::void_t<typename T::Builder, decltype(T::BinarySchema::data()),
+                   decltype(T::GetFullyQualifiedName())>> = true;
+
 template <typename T>
 inline constexpr bool has_size_v =
     Requires<T>([](auto v) -> decltype(v.size()) {});
diff --git a/fuzztest/internal/test_flatbuffers.fbs b/fuzztest/internal/test_flatbuffers.fbs
new file mode 100644
index 0000000..80a7664
--- /dev/null
+++ b/fuzztest/internal/test_flatbuffers.fbs
@@ -0,0 +1,138 @@
+namespace fuzztest.internal;
+
+enum ByteEnum: byte {
+  First,
+  Second
+}
+enum ShortEnum: short {
+  First,
+  Second
+}
+
+enum IntEnum: int {
+  First,
+  Second
+}
+
+enum LongEnum: long {
+  First,
+  Second
+}
+
+enum UByteEnum: ubyte {
+  First,
+  Second
+}
+
+enum UShortEnum: ushort {
+  First,
+  Second
+}
+enum UIntEnum: uint {
+  First,
+  Second
+}
+enum ULongEnum: ulong {
+  First,
+  Second
+}
+
+struct BoolStruct {
+  b: bool;
+  a_b: [bool:2];
+}
+
+table BoolTable {
+  b: bool;
+}
+
+table StringTable {
+  str: string;
+}
+
+union Union {
+  BoolTable,
+  StringTable,
+  BoolStruct,
+}
+
+table DefaultTable {
+  b: bool;
+  i8: byte;
+  i16: short;
+  i32: int;
+  i64: long;
+  u8: ubyte;
+  u16: ushort;
+  u32: uint;
+  u64: ulong;
+  f: float;
+  d: double;
+  str: string;
+  ei8: ByteEnum;
+  ei16: ShortEnum;
+  ei32: IntEnum;
+  ei64: LongEnum;
+  eu8: UByteEnum;
+  eu16: UShortEnum;
+  eu32: UIntEnum;
+  eu64: ULongEnum;
+}
+
+table OptionalTable {
+  b: bool = null;
+  i8: byte = null;
+  i16: short = null;
+  i32: int = null;
+  i64: long = null;
+  u8: ubyte = null;
+  u16: ushort = null;
+  u32: uint = null;
+  u64: ulong = null;
+  f: float = null;
+  d: double = null;
+  str: string; // always optional, no need to specify the default value.
+  ei8: ByteEnum = null;
+  ei16: ShortEnum = null;
+  ei32: IntEnum = null;
+  ei64: LongEnum = null;
+  eu8: UByteEnum = null;
+  eu16: UShortEnum = null;
+  eu32: UIntEnum = null;
+  eu64: ULongEnum = null;
+}
+
+table RequiredTable {
+  str: string (required);
+}
+
+table UnsupportedTypesTable {
+  t: BoolTable;
+  u: Union;
+  s: BoolStruct;
+  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];
+  v_u: [Union];
+  v_s: [BoolStruct];
+}
+
+root_type DefaultTable;