Add support for Flatbuffers with 64-bit offsets.

This change updates the Flatbuffers domain implementation to use flatbuffers::FlatBufferBuilder64 and handle 64-bit offsets where appropriate, based on the reflection schema. A new test case is added to verify round-trip functionality for a Flatbuffer schema using 64-bit offsets.

PiperOrigin-RevId: 930534503
diff --git a/domain_tests/arbitrary_domains_flatbuffers_test.cc b/domain_tests/arbitrary_domains_flatbuffers_test.cc
index 508ad26..d96c24d 100644
--- a/domain_tests/arbitrary_domains_flatbuffers_test.cc
+++ b/domain_tests/arbitrary_domains_flatbuffers_test.cc
@@ -36,6 +36,7 @@
 #include "./domain_tests/domain_testing.h"
 #include "./fuzztest/flatbuffers.h"
 #include "./fuzztest/internal/meta.h"
+#include "./fuzztest/internal/test_flatbuffers_64bits_generated.h"
 #include "./fuzztest/internal/test_flatbuffers_generated.h"
 
 namespace fuzztest {
@@ -43,6 +44,7 @@
 
 using ::fuzztest::internal::BoolTable;
 using ::fuzztest::internal::DefaultTable;
+using ::fuzztest::internal::DefaultTable64;
 using ::fuzztest::internal::OptionalTable;
 using ::fuzztest::internal::RecursiveTable;
 using ::fuzztest::internal::RequiredTable;
@@ -592,5 +594,29 @@
   ASSERT_THAT(new_table, IsNull());
 }
 
+TEST(FlatbuffersTableDomainImplTest, DefaultTable64ValueRoundTrip) {
+  flatbuffers::FlatBufferBuilder64 fbb;
+  auto str_offset = fbb.CreateString<flatbuffers::Offset64>("foo bar baz");
+  auto table_offset = internal::CreateDefaultTable64(fbb, str_offset);
+  fbb.Finish(table_offset);
+  auto table = flatbuffers::GetRoot<DefaultTable64>(fbb.GetBufferPointer());
+
+  auto domain = Arbitrary<const DefaultTable64*>();
+  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);
+  ASSERT_THAT(new_table, NotNull());
+  ASSERT_THAT(new_table->str(), NotNull());
+  EXPECT_EQ(new_table->str()->str(), "foo bar baz");
+}
+
 }  // namespace
 }  // namespace fuzztest
diff --git a/fuzztest/internal/BUILD b/fuzztest/internal/BUILD
index f515591..2bf470f 100644
--- a/fuzztest/internal/BUILD
+++ b/fuzztest/internal/BUILD
@@ -616,8 +616,13 @@
 
 flatbuffer_library_public(
     name = "test_flatbuffers_fbs",
-    srcs = ["test_flatbuffers.fbs"],
+    srcs = [
+        "test_flatbuffers.fbs",
+        "test_flatbuffers_64bits.fbs",
+    ],
     outs = [
+        "test_flatbuffers_64bits_bfbs_generated.h",
+        "test_flatbuffers_64bits_generated.h",
         "test_flatbuffers_bfbs_generated.h",
         "test_flatbuffers_generated.h",
     ],
diff --git a/fuzztest/internal/CMakeLists.txt b/fuzztest/internal/CMakeLists.txt
index 0eb75ab..5c1173e 100644
--- a/fuzztest/internal/CMakeLists.txt
+++ b/fuzztest/internal/CMakeLists.txt
@@ -574,6 +574,7 @@
       test_flatbuffers_headers
     SCHEMAS
       "test_flatbuffers.fbs"
+      "test_flatbuffers_64bits.fbs"
     FLAGS
       --bfbs-gen-embed --gen-name-strings
     TESTONLY
diff --git a/fuzztest/internal/domains/flatbuffers_domain_impl.cc b/fuzztest/internal/domains/flatbuffers_domain_impl.cc
index 7e8e144..9031344 100644
--- a/fuzztest/internal/domains/flatbuffers_domain_impl.cc
+++ b/fuzztest/internal/domains/flatbuffers_domain_impl.cc
@@ -276,11 +276,11 @@
 }
 
 uint32_t FlatbuffersTableUntypedDomainImpl::BuildTable(
-    const corpus_type& value, flatbuffers::FlatBufferBuilder& builder) const {
+    const corpus_type& value, flatbuffers::FlatBufferBuilder64& 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>
+  absl::flat_hash_map<typename corpus_type::key_type, flatbuffers::uoffset64_t>
       offsets;
 
   // Some fields are stored inline in the flatbuffer table itself (a.k.a
diff --git a/fuzztest/internal/domains/flatbuffers_domain_impl.h b/fuzztest/internal/domains/flatbuffers_domain_impl.h
index 1482a59..328b2e7 100644
--- a/fuzztest/internal/domains/flatbuffers_domain_impl.h
+++ b/fuzztest/internal/domains/flatbuffers_domain_impl.h
@@ -37,6 +37,7 @@
 #include "absl/strings/str_format.h"
 #include "absl/synchronization/mutex.h"
 #include "flatbuffers/base.h"
+#include "flatbuffers/buffer.h"
 #include "flatbuffers/flatbuffer_builder.h"
 #include "flatbuffers/reflection_generated.h"
 #include "flatbuffers/string.h"
@@ -365,7 +366,7 @@
   bool IsSupportedField(const reflection::Field* absl_nonnull field) const;
 
   uint32_t BuildTable(const corpus_type& value,
-                      flatbuffers::FlatBufferBuilder& builder) const;
+                      flatbuffers::FlatBufferBuilder64& builder) const;
 
   // Returns the domain for the given field.
   // The domain is cached, and the same instance is returned for the same field.
@@ -441,9 +442,15 @@
         }
       } 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());
+          if (field->offset64()) {
+            inner_value = std::optional(
+                user_value->GetPointer64<flatbuffers::String*>(field->offset())
+                    ->str());
+          } else {
+            inner_value = std::optional(
+                user_value->GetPointer<flatbuffers::String*>(field->offset())
+                    ->str());
+          }
         }
       } else if constexpr (std::is_same_v<T, FlatbuffersTableTag>) {
         auto sub_object = self.schema_->objects()->Get(field->type()->index());
@@ -464,9 +471,9 @@
   // 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;
+    flatbuffers::FlatBufferBuilder64& builder;
+    absl::flat_hash_map<typename corpus_type::key_type,
+                        flatbuffers::uoffset64_t>& offsets;
     const typename corpus_type::mapped_type& corpus_value;
 
     template <typename T>
@@ -475,8 +482,16 @@
         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;
+          flatbuffers::uoffset64_t offset;
+          if (field->offset64()) {
+            offset = builder
+                         .CreateString<flatbuffers::Offset64>(
+                             user_value->data(), user_value->size())
+                         .o;
+          } else {
+            offset =
+                builder.CreateString(user_value->data(), user_value->size()).o;
+          }
           offsets.insert({field->id(), offset});
         }
       } else if constexpr (std::is_same_v<T, FlatbuffersTableTag>) {
@@ -502,9 +517,9 @@
   // 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;
+    flatbuffers::FlatBufferBuilder64& builder;
+    absl::flat_hash_map<typename corpus_type::key_type,
+                        flatbuffers::uoffset64_t>& offsets;
     const typename corpus_type::value_type::second_type& corpus_value;
 
     template <typename T>
@@ -521,9 +536,15 @@
       } 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));
+          if (field->offset64()) {
+            builder.AddOffset(
+                field->offset(),
+                flatbuffers::Offset64<flatbuffers::String>(it->second));
+          } else {
+            builder.AddOffset(
+                field->offset(),
+                flatbuffers::Offset<flatbuffers::String>(it->second));
+          }
         }
       } else if constexpr (std::is_same_v<T, FlatbuffersTableTag>) {
         // "Out-of-line field". Store just offset.
@@ -753,7 +774,7 @@
 
   // Converts corpus value into the exact flatbuffer.
   value_type GetValue(const corpus_type& value) const {
-    flatbuffers::FlatBufferBuilder builder;
+    flatbuffers::FlatBufferBuilder64 builder;
     const uint32_t offset = inner_->BuildTable(value.untyped_corpus, builder);
     builder.Finish(flatbuffers::Offset<flatbuffers::Table>(offset));
     value.buffer =
diff --git a/fuzztest/internal/test_flatbuffers_64bits.fbs b/fuzztest/internal/test_flatbuffers_64bits.fbs
new file mode 100644
index 0000000..b893c88
--- /dev/null
+++ b/fuzztest/internal/test_flatbuffers_64bits.fbs
@@ -0,0 +1,21 @@
+// 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.
+
+namespace fuzztest.internal;
+
+table DefaultTable64 {
+  str:string (offset64);
+}
+
+root_type DefaultTable64;