blob: e3130bbcdb60534134ef76a2638368b99cf9e23d [file] [log] [blame]
James Kuszmaulf5eb4682023-09-22 17:16:59 -07001#include "aos/flatbuffers/static_flatbuffers.h"
2
Stephan Pleines6191f1d2024-05-30 20:44:45 -07003#include <stddef.h>
4#include <stdint.h>
5
6#include <algorithm>
7#include <map>
8#include <optional>
9#include <ostream>
10#include <utility>
11#include <vector>
12
Austin Schuh99f7c6a2024-06-25 22:07:44 -070013#include "absl/log/check.h"
14#include "absl/log/log.h"
Stephan Pleines6191f1d2024-05-30 20:44:45 -070015#include "absl/strings/numbers.h"
James Kuszmaulf5eb4682023-09-22 17:16:59 -070016#include "absl/strings/str_cat.h"
17#include "absl/strings/str_format.h"
18#include "absl/strings/str_join.h"
19#include "absl/strings/str_replace.h"
Austin Schuhf8440852024-05-31 10:46:50 -070020#include "absl/strings/substitute.h"
Stephan Pleines6191f1d2024-05-30 20:44:45 -070021#include "flatbuffers/base.h"
22#include "flatbuffers/string.h"
23#include "flatbuffers/vector.h"
James Kuszmaulf5eb4682023-09-22 17:16:59 -070024
Stephan Pleines6191f1d2024-05-30 20:44:45 -070025#include "aos/flatbuffers/base.h"
James Kuszmaulf5eb4682023-09-22 17:16:59 -070026#include "aos/json_to_flatbuffer.h"
Stephan Pleines6191f1d2024-05-30 20:44:45 -070027
James Kuszmaulf5eb4682023-09-22 17:16:59 -070028namespace aos::fbs {
29namespace {
30// Represents a given field within a type with all of the data that we actually
31// care about.
32struct FieldData {
33 // Field name.
34 std::string name;
35 // Whether it is an inline data type (scalar/struct vs vector/table/string).
36 bool is_inline = true;
Austin Schuhf8440852024-05-31 10:46:50 -070037 // Whether the elements are inline (vector of ints vs vector of strings).
38 bool elements_are_inline = true;
James Kuszmaulf5eb4682023-09-22 17:16:59 -070039 // Whether this is a struct or not.
40 bool is_struct = false;
James Kuszmaul6be41022023-12-20 11:55:28 -080041 // Whether this is a repeated type (vector or string).
42 bool is_repeated = false;
James Kuszmaulf5eb4682023-09-22 17:16:59 -070043 // Full C++ type of this field.
44 std::string full_type = "";
45 // Full flatbuffer type for this field.
46 // Only specified for Tables.
47 std::optional<std::string> fbs_type = std::nullopt;
48 // Size of this field in the inline field data (i.e., size of the field for
49 // is_inline fields; 4 bytes for the offset for vectors/tables/strings).
50 size_t inline_size = 0u;
51 // Alignment of the inline data.
52 size_t inline_alignment = 0u;
53 // vtable offset of the field.
54 size_t vtable_offset = 0u;
Austin Schuhf8440852024-05-31 10:46:50 -070055 // Size of the elements in the vector, if this is a vector.
56 size_t element_size = 0u;
James Kuszmaulf5eb4682023-09-22 17:16:59 -070057};
58
59const reflection::Object *GetObject(const reflection::Schema *schema,
60 const int index) {
61 return (index == -1) ? schema->root_table() : schema->objects()->Get(index);
62}
63
64// Returns the flatbuffer field attribute with the specified name, if available.
65std::optional<std::string_view> GetAttribute(const reflection::Field *field,
66 std::string_view attribute) {
67 if (!field->has_attributes()) {
68 return std::nullopt;
69 }
70 const reflection::KeyValue *kv =
71 field->attributes()->LookupByKey(attribute.data());
72 if (kv == nullptr) {
73 return std::nullopt;
74 }
75 return kv->value()->string_view();
76}
77
78// Returns the implied value of an attribute that specifies a length (i.e., 0 if
79// the attribute is not specified; the integer value otherwise).
80int64_t GetLengthAttributeOrZero(const reflection::Field *field,
81 std::string_view attribute) {
82 std::optional<std::string_view> str = GetAttribute(field, attribute);
83 if (!str.has_value()) {
84 return 0;
85 }
86 int64_t value;
87 CHECK(absl::SimpleAtoi(str.value(), &value))
88 << ": Field " << field->name()->string_view()
89 << " must specify a positive integer for the " << attribute
90 << " attribute. Got \"" << str.value() << "\".";
91 CHECK_LE(0, value) << ": Field " << field->name()->string_view()
92 << " must have a non-negative " << attribute << ".";
93 return value;
94}
95
96const std::string ScalarCppType(const reflection::BaseType type) {
97 switch (type) {
98 case reflection::BaseType::Bool:
99 return "bool";
100 case reflection::BaseType::Byte:
101 return "int8_t";
102 case reflection::BaseType::UByte:
103 return "uint8_t";
104 case reflection::BaseType::Short:
105 return "int16_t";
106 case reflection::BaseType::UShort:
107 return "uint16_t";
108 case reflection::BaseType::Int:
109 return "int32_t";
110 case reflection::BaseType::UInt:
111 return "uint32_t";
112 case reflection::BaseType::Long:
113 return "int64_t";
114 case reflection::BaseType::ULong:
115 return "uint64_t";
116 case reflection::BaseType::Float:
117 return "float";
118 case reflection::BaseType::Double:
119 return "double";
120 case reflection::BaseType::UType:
121 case reflection::BaseType::String:
122 case reflection::BaseType::Vector:
123 case reflection::BaseType::Obj:
124 case reflection::BaseType::None:
125 case reflection::BaseType::Union:
126 case reflection::BaseType::Array:
127 case reflection::BaseType::MaxBaseType:
128 LOG(FATAL) << ": Type " << reflection::EnumNameBaseType(type)
129 << " not a scalar.";
130 }
131 LOG(FATAL) << "Unreachable";
132}
133
134const std::string FlatbufferNameToCppName(const std::string_view input) {
135 return absl::StrReplaceAll(input, {{".", "::"}});
136}
137
138const std::string AosNameForRawFlatbuffer(const std::string_view base_name) {
139 return absl::StrCat(base_name, "Static");
140}
141
142const std::string IncludePathForFbs(
143 std::string_view fbs_file, std::string_view include_suffix = "static") {
James Kuszmaul1e57af92023-12-20 15:34:54 -0800144 // Special case for the reflection_generated.h, which is checked into the
145 // repo.
146 // Note that we *do* autogenerated the reflection_static.h but that because
147 // it uses a special import path, we end up overriding the include anyways
148 // (note that we could muck around with the paths on the bazel side to instead
149 // get a cc_library with the correct include paths specified, although it is
150 // not clear that that would be any simpler than the extra else-if).
151 if (fbs_file == "reflection/reflection.fbs") {
152 if (include_suffix == "generated") {
153 return "flatbuffers/reflection_generated.h";
154 } else if (include_suffix == "static") {
155 return "aos/flatbuffers/reflection/reflection_static.h";
156 } else {
157 LOG(FATAL) << "This should be unreachable.";
158 }
159 }
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700160 fbs_file.remove_suffix(4);
161 return absl::StrCat(fbs_file, "_", include_suffix, ".h");
162}
163
164std::string ScalarOrEnumType(const reflection::Schema *schema,
165 const reflection::BaseType type, int index) {
166 return (index < 0) ? ScalarCppType(type)
167 : FlatbufferNameToCppName(
168 schema->enums()->Get(index)->name()->string_view());
169}
170
171void PopulateTypeData(const reflection::Schema *schema,
172 const reflection::Field *field_fbs, FieldData *field) {
173 VLOG(1) << aos::FlatbufferToJson(field_fbs);
174 const reflection::Type *type = field_fbs->type();
175 field->inline_size = type->base_size();
176 field->inline_alignment = type->base_size();
Austin Schuhf8440852024-05-31 10:46:50 -0700177 field->element_size = type->element_size();
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700178 switch (type->base_type()) {
179 case reflection::BaseType::Bool:
180 case reflection::BaseType::Byte:
181 case reflection::BaseType::UByte:
182 case reflection::BaseType::Short:
183 case reflection::BaseType::UShort:
184 case reflection::BaseType::Int:
185 case reflection::BaseType::UInt:
186 case reflection::BaseType::Long:
187 case reflection::BaseType::ULong:
188 case reflection::BaseType::Float:
189 case reflection::BaseType::Double:
190 // We have a scalar field, so things are relatively
191 // straightforwards.
192 field->is_inline = true;
Austin Schuhf8440852024-05-31 10:46:50 -0700193 field->elements_are_inline = true;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700194 field->is_struct = false;
James Kuszmaul6be41022023-12-20 11:55:28 -0800195 field->is_repeated = false;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700196 field->full_type =
197 ScalarOrEnumType(schema, type->base_type(), type->index());
198 return;
199 case reflection::BaseType::String: {
200 field->is_inline = false;
Austin Schuhf8440852024-05-31 10:46:50 -0700201 field->elements_are_inline = true;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700202 field->is_struct = false;
James Kuszmaul6be41022023-12-20 11:55:28 -0800203 field->is_repeated = true;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700204 field->full_type =
205 absl::StrFormat("::aos::fbs::String<%d>",
206 GetLengthAttributeOrZero(field_fbs, "static_length"));
207 return;
208 }
209 case reflection::BaseType::Vector: {
210 // We need to extract the name of the elements of the vector.
211 std::string element_type;
212 bool elements_are_inline = true;
James Kuszmaul6be41022023-12-20 11:55:28 -0800213 field->is_repeated = true;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700214 if (type->base_type() == reflection::BaseType::Vector) {
215 switch (type->element()) {
216 case reflection::BaseType::Obj: {
217 const reflection::Object *element_object =
218 GetObject(schema, type->index());
219 element_type =
220 FlatbufferNameToCppName(element_object->name()->string_view());
221 elements_are_inline = element_object->is_struct();
222 if (!element_object->is_struct()) {
223 element_type = AosNameForRawFlatbuffer(element_type);
224 field->fbs_type = element_object->name()->string_view();
225 }
226 break;
227 }
228 case reflection::BaseType::String:
229 element_type =
230 absl::StrFormat("::aos::fbs::String<%d>",
231 GetLengthAttributeOrZero(
232 field_fbs, "static_vector_string_length"));
233 elements_are_inline = false;
234 break;
235 case reflection::BaseType::Vector:
236 LOG(FATAL) << "Vectors of vectors do not exist in flatbuffers.";
237 default:
238 element_type =
239 ScalarOrEnumType(schema, type->element(), type->index());
240 };
241 }
242 field->is_inline = false;
Austin Schuhf8440852024-05-31 10:46:50 -0700243 field->elements_are_inline = elements_are_inline;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700244 field->is_struct = false;
245 field->full_type =
246 absl::StrFormat("::aos::fbs::Vector<%s, %d, %s, %s>", element_type,
247 GetLengthAttributeOrZero(field_fbs, "static_length"),
248 elements_are_inline ? "true" : "false",
249 GetAttribute(field_fbs, "force_align").value_or("0"));
250 return;
251 }
252 case reflection::BaseType::Obj: {
253 const reflection::Object *object = GetObject(schema, type->index());
254 field->is_inline = object->is_struct();
Austin Schuhf8440852024-05-31 10:46:50 -0700255 field->elements_are_inline = field->is_inline;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700256 field->is_struct = object->is_struct();
James Kuszmaul6be41022023-12-20 11:55:28 -0800257 field->is_repeated = false;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700258 const std::string flatbuffer_name =
259 FlatbufferNameToCppName(object->name()->string_view());
260 if (field->is_inline) {
261 field->full_type = flatbuffer_name;
262 field->inline_size = object->bytesize();
263 field->inline_alignment = object->minalign();
264 } else {
265 field->fbs_type = object->name()->string_view();
266 field->full_type = AosNameForRawFlatbuffer(flatbuffer_name);
267 }
268 return;
269 }
270 case reflection::BaseType::None:
271 case reflection::BaseType::UType:
272 case reflection::BaseType::Union:
273 case reflection::BaseType::Array:
274 case reflection::BaseType::MaxBaseType:
275 LOG(FATAL) << ": Type " << reflection::EnumNameBaseType(type->base_type())
276 << " not supported currently.";
277 };
278}
279
280std::string MakeMoveConstructor(std::string_view type_name) {
281 return absl::StrFormat(R"code(
282 // We need to provide a MoveConstructor to allow this table to be
283 // used inside of vectors, but we do not want it readily available to
284 // users. See TableMover for more details.
285 %s(%s &&) = default;
286 friend struct ::aos::fbs::internal::TableMover<%s>;
287 )code",
288 type_name, type_name, type_name);
289}
290
291std::string MakeConstructor(std::string_view type_name) {
292 const std::string constructor_body =
293 R"code(
294 CHECK_EQ(buffer.size(), kSize);
Austin Schuhf8440852024-05-31 10:46:50 -0700295 CHECK_EQ(0u, reinterpret_cast<size_t>(buffer.data() + kAlignOffset) % kAlign);
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700296 PopulateVtable();
297)code";
298 return absl::StrFormat(R"code(
299 // Constructors for creating a flatbuffer object.
300 // Users should typically use the Builder class to create these objects,
301 // in order to allow it to populate the root table offset.
302
303 // The buffer provided to these constructors should be aligned to kAlign
304 // and kSize in length.
305 // The parent/allocator may not be nullptr.
306 %s(std::span<uint8_t> buffer, ::aos::fbs::ResizeableObject *parent) : Table(buffer, parent) {
307 %s
308 }
309 %s(std::span<uint8_t> buffer, ::aos::fbs::Allocator *allocator) : Table(buffer, allocator) {
310 %s
311 }
312 %s(std::span<uint8_t> buffer, ::std::unique_ptr<::aos::fbs::Allocator> allocator) : Table(buffer, ::std::move(allocator)) {
313 %s
314 }
315)code",
316 type_name, constructor_body, type_name,
317 constructor_body, type_name, constructor_body);
318}
319
320std::string MemberName(const FieldData &data) {
321 return absl::StrCat(data.name, "_");
322}
323
324std::string ObjectAbsoluteOffsetName(const FieldData &data) {
325 return absl::StrCat("object_absolute_offset_", data.name);
326}
327
328std::string InlineAbsoluteOffsetName(const FieldData &data) {
329 return absl::StrCat("kInlineAbsoluteOffset_", data.name);
330}
331
332// Generate the clear_* method for the requested field.
333std::string MakeClearer(const FieldData &field) {
334 std::string logical_clearer;
335 if (!field.is_inline) {
336 logical_clearer = MemberName(field) + ".reset();";
337 }
338 return absl::StrFormat(R"code(
339 // Clears the %s field. This will cause has_%s() to return false.
340 void clear_%s() {
341 %s
342 ClearField(%s, %d, %d);
343 }
344 )code",
345 field.name, field.name, field.name, logical_clearer,
346 InlineAbsoluteOffsetName(field), field.inline_size,
347 field.vtable_offset);
348}
349
350// Generate the has_* method for the requested field.
351std::string MakeHaser(const FieldData &field) {
352 return absl::StrFormat(R"code(
353 // Returns true if the %s field is set and can be accessed.
354 bool has_%s() const {
355 return AsFlatbuffer().has_%s();
356 }
357 )code",
358 field.name, field.name, field.name);
359}
360
361// Generates the accessors for fields which are stored inline in the flatbuffer
362// table (scalars, structs, and enums) .
363std::string MakeInlineAccessors(const FieldData &field,
364 const size_t inline_absolute_offset) {
Austin Schuhf8440852024-05-31 10:46:50 -0700365 constexpr size_t kVtablePointerSize = sizeof(soffset_t);
366 CHECK_EQ(
367 (inline_absolute_offset - kVtablePointerSize) % field.inline_alignment,
368 0u)
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700369 << ": Unaligned field " << field.name << " on " << field.full_type
370 << " with inline offset of " << inline_absolute_offset
Austin Schuhf8440852024-05-31 10:46:50 -0700371 << " and alignment of " << field.inline_alignment
372 << " and an alignment offset of " << kVtablePointerSize;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700373 const std::string setter =
374 absl::StrFormat(R"code(
375 // Sets the %s field, causing it to be populated if it is not already.
376 // This will populate the field even if the specified value is the default.
377 void set_%s(const %s &value) {
378 SetField<%s>(%s, %d, value);
379 }
380 )code",
381 field.name, field.name, field.full_type, field.full_type,
382 InlineAbsoluteOffsetName(field), field.vtable_offset);
383 const std::string getters = absl::StrFormat(
384 R"code(
385 // Returns the value of %s if set; nullopt otherwise.
386 std::optional<%s> %s() const {
Austin Schuhf8440852024-05-31 10:46:50 -0700387 return has_%s() ? std::make_optional(Get<%s>(%s)) : std::nullopt;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700388 }
389 // Returns a pointer to modify the %s field.
390 // The pointer may be invalidated by mutations/movements of the underlying buffer.
391 // Returns nullptr if the field is not set.
392 %s* mutable_%s() {
393 return has_%s() ? MutableGet<%s>(%s) : nullptr;
394 }
395 )code",
396 field.name, field.full_type, field.name, field.name, field.full_type,
397 InlineAbsoluteOffsetName(field), field.name, field.full_type, field.name,
398 field.name, field.full_type, InlineAbsoluteOffsetName(field));
399 const std::string clearer = MakeClearer(field);
400 return setter + getters + clearer + MakeHaser(field);
401}
402
403// Generates the accessors for fields which are not inline fields and have an
404// offset to the actual field content stored inline in the flatbuffer table.
405std::string MakeOffsetDataAccessors(const FieldData &field) {
Austin Schuhf8440852024-05-31 10:46:50 -0700406 const std::string setter = absl::Substitute(
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700407 R"code(
Austin Schuhf8440852024-05-31 10:46:50 -0700408 // Creates an empty object for the $0 field, which you can
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700409 // then populate/modify as desired.
410 // The field must not be populated yet.
Austin Schuhf8440852024-05-31 10:46:50 -0700411 $1* add_$0() {
412 CHECK(!$2.has_value());
413 constexpr size_t kVtableIndex = $3;
414 // If this object does not normally have its initial memory statically allocated,
415 // allocate it now (this is used for zero-length vectors).
416 if constexpr ($1::kPreallocatedSize == 0) {
417 const size_t object_absolute_offset = $4;
418 std::optional<std::span<uint8_t>> inserted_bytes =
419 InsertBytes(buffer().data() + object_absolute_offset, $1::kSize, ::aos::fbs::SetZero::kYes);
420 if (!inserted_bytes.has_value()) {
421 return nullptr;
422 }
423 // Undo changes to the object absolute offset that will have been made by
424 // the InsertBytes call.
425 // The InsertBytes() call normally goes through and "fixes" any offsets
426 // that will have been affected by the memory insertion. Unfortunately,
427 // if this object currently takes up zero bytes then the InsertBytes()
428 // cannot distinguish between this offset and the (identical) offsets for any other objects
429 // that may have been "sharing" this location. The effect of this logic
430 // is that the first object that gets populated at any given location will
431 // bump all other objects to later. This is fine, although it does mean
432 // that the order in which objects appear in memory may vary depending
433 // on the order in which they are constructed (if they start out sharing a start pointer).
434 $4 = object_absolute_offset;
435
436 // Construct the *Static object that we will use for managing this subtable.
437 $5.emplace(BufferForObject($4, $1::$7), this);
438 } else {
439 // Construct the *Static object that we will use for managing this subtable.
440 $5.emplace(BufferForObject($4, $1::kSize), this);
441 }
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700442 // Actually set the appropriate fields in the flatbuffer memory itself.
Austin Schuhf8440852024-05-31 10:46:50 -0700443 SetField<::flatbuffers::uoffset_t>($6, kVtableIndex, $4 - $6);
444 return &$5.value().t;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700445 }
446 )code",
Austin Schuhf8440852024-05-31 10:46:50 -0700447 field.name, // $0
448 field.full_type, // $1
449 MemberName(field), // $2
450 field.vtable_offset, // $3
451 ObjectAbsoluteOffsetName(field), // $4
452 MemberName(field), // $5
453 InlineAbsoluteOffsetName(field), // $6
454 (field.elements_are_inline
455 // When building vectors of inline elements, we want this object to
456 // consume as much of the memory that was allocated as possible. This
457 // lets the vector use the padding for storage, saving space. Round
458 // this down to the size of memory that the max number of elements
459 // fit in perfectly so the padding after that isn't owned.
460 ? "RoundedLength(inserted_bytes.value().size())"
461 // For vectors of elements, we need to pad out the inline portion of
462 // the vector storing offsets to the alignment of the actual elements
463 // so we can insert elements at the end without having to allocate
464 // padding. This saves space in the long run, and lets us consume
465 // the padding for offsets if needed.
466 : "kSize") // $7
467 );
468 const std::string getters = absl::Substitute(
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700469 R"code(
Austin Schuhf8440852024-05-31 10:46:50 -0700470 // Returns a pointer to the $0 field, if set. nullptr otherwise.
471 const $1* $0() const {
472 return $2.has_value() ? &$2.value().t : nullptr;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700473 }
Austin Schuhf8440852024-05-31 10:46:50 -0700474 $1* mutable_$0() {
475 return $2.has_value() ? &$2.value().t : nullptr;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700476 }
477 )code",
Austin Schuhf8440852024-05-31 10:46:50 -0700478 field.name, // $0
479 field.full_type, // $1
480 MemberName(field) // $2
481 );
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700482 return setter + getters + MakeClearer(field) + MakeHaser(field);
483}
484
485std::string MakeAccessors(const FieldData &field,
486 size_t inline_absolute_offset) {
487 return field.is_inline ? MakeInlineAccessors(field, inline_absolute_offset)
488 : MakeOffsetDataAccessors(field);
489}
490
491std::string MakeMembers(const FieldData &field,
492 std::string_view offset_data_absolute_offset,
493 size_t inline_absolute_offset) {
494 if (field.is_inline) {
495 return absl::StrFormat(
496 R"code(
497 // Offset from the start of the buffer to the inline data for the %s field.
498 static constexpr size_t %s = %d;
499 )code",
500 field.name, InlineAbsoluteOffsetName(field), inline_absolute_offset);
501 } else {
502 return absl::StrFormat(
503 R"code(
504 // Members relating to the %s field.
505 //
506 // *Static object used for managing this subtable. Will be nullopt
507 // when the field is not populated.
508 // We use the TableMover to be able to make this object moveable.
509 std::optional<::aos::fbs::internal::TableMover<%s>> %s;
510 // Offset from the start of the buffer to the start of the actual
511 // data for this field. Will be updated even when the table is not
512 // populated, so that we know where to construct it when requested.
Austin Schuhf8440852024-05-31 10:46:50 -0700513 static constexpr size_t kDefaultObjectAbsoluteOffset%s = %s;
514 size_t %s = kDefaultObjectAbsoluteOffset%s;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700515 // Offset from the start of the buffer to the offset in the inline data for
516 // this field.
517 static constexpr size_t %s = %d;
518 )code",
Austin Schuhf8440852024-05-31 10:46:50 -0700519 field.name, field.full_type, MemberName(field), field.name,
520 offset_data_absolute_offset, ObjectAbsoluteOffsetName(field),
521 field.name, InlineAbsoluteOffsetName(field), inline_absolute_offset);
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700522 }
523}
524
525std::string MakeFullClearer(const std::vector<FieldData> &fields) {
526 std::vector<std::string> clearers;
527 for (const FieldData &field : fields) {
528 clearers.emplace_back(absl::StrFormat("clear_%s();", field.name));
529 }
530 return absl::StrFormat(R"code(
531 // Clears every field of the table, removing any existing state.
532 void Clear() { %s })code",
533 absl::StrJoin(clearers, "\n"));
534}
535
James Kuszmaul6be41022023-12-20 11:55:28 -0800536// Creates the FromFlatbuffer() method that copies from a flatbuffer object API
537// object (i.e., the FlatbufferT types).
538std::string MakeObjectCopier(const std::vector<FieldData> &fields) {
539 std::vector<std::string> copiers;
540 for (const FieldData &field : fields) {
541 if (field.is_struct) {
542 // Structs are stored as unique_ptr<FooStruct>
543 copiers.emplace_back(absl::StrFormat(R"code(
544 if (other.%s) {
545 set_%s(*other.%s);
546 }
547 )code",
548 field.name, field.name, field.name));
549 } else if (field.is_inline) {
550 // Inline non-struct elements are stored as FooType.
551 copiers.emplace_back(absl::StrFormat(R"code(
552 set_%s(other.%s);
553 )code",
554 field.name, field.name));
555 } else if (field.is_repeated) {
556 // strings are stored as std::string's.
557 // vectors are stored as std::vector's.
558 copiers.emplace_back(absl::StrFormat(R"code(
559 // Unconditionally copy strings/vectors, even if it will just end up
560 // being 0-length (this maintains consistency with the flatbuffer Pack()
561 // behavior).
Austin Schuh6bdcc372024-06-27 14:49:11 -0700562 {
563 %s* added_%s = add_%s();
564 CHECK(added_%s != nullptr);
565 if (!added_%s->FromFlatbuffer(other.%s)) {
James Kuszmaul6be41022023-12-20 11:55:28 -0800566 // Fail if we were unable to copy (e.g., if we tried to copy in a long
567 // vector and do not have the space for it).
568 return false;
569 }
570 }
571 )code",
Austin Schuh6bdcc372024-06-27 14:49:11 -0700572 field.full_type, field.name,
573 field.name, field.name, field.name,
574 field.name));
575 } else {
576 // Tables are stored as unique_ptr<FooTable>
577 copiers.emplace_back(absl::StrFormat(R"code(
578 if (other.%s) {
579 %s* added_%s = add_%s();
580 CHECK(added_%s != nullptr);
581 if (!added_%s->FromFlatbuffer(*other.%s)) {
582 // Fail if we were unable to copy (e.g., if we tried to copy in a long
583 // vector and do not have the space for it).
584 return false;
585 }
586 }
587 )code",
588 field.name, field.full_type,
589 field.name, field.name, field.name,
590 field.name, field.name));
James Kuszmaul6be41022023-12-20 11:55:28 -0800591 }
592 }
593 return absl::StrFormat(
594 R"code(
595 // Copies the contents of the provided flatbuffer into this flatbuffer,
596 // returning true on success.
597 // Because the Flatbuffer Object API does not provide any concept of an
598 // optionally populated scalar field, all scalar fields will be populated
599 // after a call to FromFlatbufferObject().
600 // This is a deep copy, and will call FromFlatbufferObject on
601 // any constituent objects.
602 [[nodiscard]] bool FromFlatbuffer(const Flatbuffer::NativeTableType &other) {
603 Clear();
604 %s
605 return true;
606 }
607 [[nodiscard]] bool FromFlatbuffer(const flatbuffers::unique_ptr<Flatbuffer::NativeTableType>& other) {
608 return FromFlatbuffer(*other);
609 }
610)code",
611 absl::StrJoin(copiers, "\n"));
612}
613
614// Creates the FromFlatbuffer() method that copies from an actual flatbuffer
615// object.
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700616std::string MakeCopier(const std::vector<FieldData> &fields) {
617 std::vector<std::string> copiers;
618 for (const FieldData &field : fields) {
619 if (field.is_struct) {
620 copiers.emplace_back(absl::StrFormat(R"code(
James Kuszmaul692780f2023-12-20 14:01:56 -0800621 if (other.has_%s()) {
622 set_%s(*other.%s());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700623 }
624 )code",
625 field.name, field.name, field.name));
626 } else if (field.is_inline) {
627 copiers.emplace_back(absl::StrFormat(R"code(
James Kuszmaul692780f2023-12-20 14:01:56 -0800628 if (other.has_%s()) {
629 set_%s(other.%s());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700630 }
631 )code",
632 field.name, field.name, field.name));
633 } else {
634 copiers.emplace_back(absl::StrFormat(R"code(
James Kuszmaul692780f2023-12-20 14:01:56 -0800635 if (other.has_%s()) {
Austin Schuh6bdcc372024-06-27 14:49:11 -0700636 %s* added_%s = add_%s();
637 CHECK(added_%s != nullptr);
638 if (!added_%s->FromFlatbuffer(other.%s())) {
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700639 // Fail if we were unable to copy (e.g., if we tried to copy in a long
640 // vector and do not have the space for it).
641 return false;
642 }
643 }
644 )code",
Austin Schuh6bdcc372024-06-27 14:49:11 -0700645 field.name, field.full_type,
646 field.name, field.name, field.name,
647 field.name, field.name));
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700648 }
649 }
650 return absl::StrFormat(
651 R"code(
652 // Copies the contents of the provided flatbuffer into this flatbuffer,
653 // returning true on success.
James Kuszmaul710883b2023-12-14 14:34:48 -0800654 // This is a deep copy, and will call FromFlatbuffer on any constituent
655 // objects.
James Kuszmaul692780f2023-12-20 14:01:56 -0800656 [[nodiscard]] bool FromFlatbuffer(const Flatbuffer &other) {
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700657 Clear();
658 %s
659 return true;
660 }
James Kuszmaul692780f2023-12-20 14:01:56 -0800661 // Equivalent to FromFlatbuffer(const Flatbuffer&); this overload is provided
662 // to ease implementation of the aos::fbs::Vector internals.
663 [[nodiscard]] bool FromFlatbuffer(const Flatbuffer *other) {
Austin Schuh6bdcc372024-06-27 14:49:11 -0700664 CHECK(other != nullptr);
665 return FromFlatbuffer(*other);
James Kuszmaul692780f2023-12-20 14:01:56 -0800666 }
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700667)code",
668 absl::StrJoin(copiers, "\n"));
669}
670
671std::string MakeSubObjectList(const std::vector<FieldData> &fields) {
672 size_t num_object_fields = 0;
673 std::vector<std::string> object_offsets;
674 std::vector<std::string> objects;
675 std::vector<std::string> inline_offsets;
676 for (const FieldData &field : fields) {
677 if (!field.is_inline) {
678 ++num_object_fields;
679 object_offsets.push_back(
680 absl::StrFormat("&%s", ObjectAbsoluteOffsetName(field)));
681 objects.push_back(absl::StrFormat("&%s->t", MemberName(field)));
682 inline_offsets.push_back(InlineAbsoluteOffsetName(field));
683 }
684 }
685 if (num_object_fields == 0) {
686 return R"code(
687 // This object has no non-inline subobjects, so we don't have to do anything special.
688 size_t NumberOfSubObjects() const final { return 0; }
689 using ::aos::fbs::ResizeableObject::SubObject;
690 SubObject GetSubObject(size_t) final { LOG(FATAL) << "No subobjects."; }
691 )code";
692 }
693 return absl::StrFormat(R"code(
694 size_t NumberOfSubObjects() const final { return %d; }
695 using ::aos::fbs::ResizeableObject::SubObject;
696 SubObject GetSubObject(size_t index) final {
697 SubObject object;
698 // Note: The below arrays are local variables rather than class members to
699 // avoid having to deal with what happens to them if the object is moved.
700
701 // Array of the members that we use for tracking where the buffers for
702 // each subobject belong.
703 // Pointers because these may need to be modified when memory is
704 // inserted into the buffer.
705 const std::array<size_t*, %d> subobject_object_offsets{%s};
706 // Actual subobjects; note that the pointers will be invalid when the
707 // field is not populated.
708 const std::array<::aos::fbs::ResizeableObject*, %d> subobject_objects{%s};
709 // Absolute offsets from the start of the buffer to where the inline
710 // entry is for each table. These offsets do not need to change at
711 // runtime (because memory is never inserted into the start of
712 // a given table), but the offsets pointed to by these offsets
713 // may need to be updated.
714 const std::array<size_t, %d> subobject_inline_offsets{%s};
715 object.inline_entry = MutableGet<::flatbuffers::uoffset_t>(subobject_inline_offsets[index]);
716 object.object = (*object.inline_entry == 0) ? nullptr : subobject_objects[index];
717 object.absolute_offset = subobject_object_offsets[index];
718 return object;
719 }
720 )code",
721 num_object_fields, num_object_fields,
722 absl::StrJoin(object_offsets, ", "), num_object_fields,
723 absl::StrJoin(objects, ", "), num_object_fields,
724 absl::StrJoin(inline_offsets, ", "));
725}
726
727std::string AlignCppString(const std::string_view expression,
Austin Schuhf8440852024-05-31 10:46:50 -0700728 const std::string_view alignment,
729 const std::string_view offset) {
730 return absl::StrCat("::aos::fbs::AlignOffset(", expression, ", ", alignment,
731 ", ", offset, ")");
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700732}
733
734std::string MakeInclude(std::string_view path, bool system = false) {
735 return absl::StrFormat("#include %s%s%s\n", system ? "<" : "\"", path,
736 system ? ">" : "\"");
737}
738
739} // namespace
740GeneratedObject GenerateCodeForObject(const reflection::Schema *schema,
741 int object_index) {
742 return GenerateCodeForObject(schema, GetObject(schema, object_index));
743}
744GeneratedObject GenerateCodeForObject(const reflection::Schema *schema,
745 const reflection::Object *object) {
746 std::vector<FieldData> fields;
747 for (const reflection::Field *field_fbs : *object->fields()) {
748 if (field_fbs->deprecated()) {
749 // Don't codegen anything for deprecated fields.
750 continue;
751 }
752 FieldData field{.name = field_fbs->name()->str(),
753 .vtable_offset = field_fbs->offset()};
754 PopulateTypeData(schema, field_fbs, &field);
755 fields.push_back(field);
756 }
Austin Schuhf8440852024-05-31 10:46:50 -0700757 std::sort(fields.begin(), fields.end(),
758 [](const FieldData &f1, const FieldData &f2) {
759 return std::make_tuple(f1.inline_alignment, f1.element_size,
760 f1.vtable_offset) >
761 std::make_tuple(f2.inline_alignment, f2.element_size,
762 f2.vtable_offset);
763 });
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700764 const size_t nominal_min_align = object->minalign();
765 std::string out_of_line_member_size = "";
766 // inline_absolute_offset tracks the current position of the inline table
767 // contents so that we can assign static offsets to each field.
Austin Schuhf8440852024-05-31 10:46:50 -0700768 constexpr size_t kVtablePointerSize = sizeof(soffset_t);
769 size_t inline_absolute_offset = kVtablePointerSize;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700770 // offset_data_relative_offset tracks the current size of the various
771 // sub-tables/vectors/strings that get stored at the end of the buffer.
772 // For simplicity, the offset data will start at a fully aligned offset
773 // (which may be larger than the soffset_t at the start of the table).
774 // Note that this is a string because it's irritating to actually pipe the
775 // numbers for size/alignment up here, so we just accumulate them here and
776 // then write the expression directly into the C++.
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700777 const std::string offset_data_start_expression =
Austin Schuhf8440852024-05-31 10:46:50 -0700778 "(kVtableStart + kVtableSize)";
779 std::string offset_data_relative_offset = offset_data_start_expression;
780 std::vector<std::string> accessors;
781 std::vector<std::string> members;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700782 std::set<std::string> includes = {
783 MakeInclude("optional", true),
784 MakeInclude("aos/flatbuffers/static_table.h"),
785 MakeInclude("aos/flatbuffers/static_vector.h")};
786 for (const reflection::SchemaFile *file : *schema->fbs_files()) {
787 includes.insert(
788 MakeInclude(IncludePathForFbs(file->filename()->string_view())));
789 includes.insert(MakeInclude(
790 IncludePathForFbs(file->filename()->string_view(), "generated")));
791 for (const flatbuffers::String *included : *file->included_filenames()) {
792 includes.insert(MakeInclude(IncludePathForFbs(included->string_view())));
793 }
794 }
795 std::vector<std::string> alignments;
796 std::set<std::string> subobject_names;
797 for (const FieldData &field : fields) {
Austin Schuhf8440852024-05-31 10:46:50 -0700798 inline_absolute_offset = AlignOffset(
799 inline_absolute_offset, field.inline_alignment, kVtablePointerSize);
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700800 if (!field.is_inline) {
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700801 alignments.push_back(field.full_type + "::kAlign");
Austin Schuhf8440852024-05-31 10:46:50 -0700802 // We care about aligning each field relative to the alignment point in
803 // this flatbuffer (which is kAlignOffset into the block of memory). We
804 // then need to report out the offset relative to the start, not the
805 // alignment point.
806 offset_data_relative_offset =
807 AlignCppString(offset_data_relative_offset + " - kAlignOffset",
808 alignments.back(),
809 field.full_type + "::kAlignOffset") +
810 " + kAlignOffset";
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700811 } else {
812 alignments.push_back(std::to_string(field.inline_alignment));
813 }
Austin Schuhf8440852024-05-31 10:46:50 -0700814 const std::string offset_data_absolute_offset = offset_data_relative_offset;
815 accessors.emplace_back(MakeAccessors(field, inline_absolute_offset));
816 members.emplace_back(MakeMembers(field, offset_data_absolute_offset,
817 inline_absolute_offset));
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700818
819 inline_absolute_offset += field.inline_size;
820 if (!field.is_inline) {
Austin Schuhf8440852024-05-31 10:46:50 -0700821 offset_data_relative_offset = absl::StrFormat(
822 "kDefaultObjectAbsoluteOffset%s + %s::kPreallocatedSize", field.name,
823 field.full_type);
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700824 }
825 if (field.fbs_type.has_value()) {
826 // Is this not getting populate for the root schema?
827 subobject_names.insert(field.fbs_type.value());
828 }
829 }
830
James Kuszmaula75cd7c2023-12-07 15:52:51 -0800831 const std::string alignment = absl::StrCat(
832 "static constexpr size_t kAlign = std::max<size_t>({kMinAlign, ",
833 absl::StrJoin(alignments, ", "), "});\n");
Austin Schuhf8440852024-05-31 10:46:50 -0700834 // Same here, we want to align the end relative to the alignment point, but
835 // then we want to report out the size including the offset.
836 const std::string size = absl::StrCat(
837 "static constexpr size_t kSize = ",
838 AlignCppString(offset_data_relative_offset + " - kAlignOffset", "kAlign",
839 "kAlignOffset"),
840 " + kAlignOffset;");
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700841 const size_t inline_data_size = inline_absolute_offset;
842 const std::string constants = absl::StrFormat(
843 R"code(
Austin Schuhf8440852024-05-31 10:46:50 -0700844 // Space taken up by the inline portion of the flatbuffer table data, in
845 // bytes.
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700846 static constexpr size_t kInlineDataSize = %d;
847 // Space taken up by the vtable for this object, in bytes.
Austin Schuhf8440852024-05-31 10:46:50 -0700848 static constexpr size_t kVtableSize =
849 sizeof(::flatbuffers::voffset_t) * (2 + %d);
850 // Offset from the start of the internal memory buffer to the start of the
851 // vtable.
852 static constexpr size_t kVtableStart = ::aos::fbs::AlignOffset(
853 kInlineDataSize, alignof(::flatbuffers::voffset_t));
854 // Required alignment of this object. The buffer that this object gets
855 // constructed into must be aligned to this value.
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700856 %s
Austin Schuhf8440852024-05-31 10:46:50 -0700857 // Offset into this object to measure the alignment at.
858 static constexpr size_t kAlignOffset = sizeof(::flatbuffers::soffset_t);
859 static_assert(
860 %d <= kAlign,
861 "Flatbuffer schema minalign should not exceed our required alignment.");
862 // Offset from the start of the memory buffer to the start of any out-of-line
863 // data (subtables, vectors, strings).
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700864 static constexpr size_t kOffsetDataStart = %s;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700865 // Various overrides to support the Table parent class.
866 size_t FixedVtableOffset() const final { return kVtableStart; }
867 size_t VtableSize() const final { return kVtableSize; }
868 size_t InlineTableSize() const final { return kInlineDataSize; }
869 size_t OffsetDataStart() const final { return kOffsetDataStart; }
870 size_t Alignment() const final { return kAlign; }
871 // Exposes the name of the flatbuffer type to allow interchangeable use
872 // of the Flatbuffer and FlatbufferStatic types in various AOS methods.
Austin Schuhf8440852024-05-31 10:46:50 -0700873 static const char *GetFullyQualifiedName() {
874 return Flatbuffer::GetFullyQualifiedName();
875 }
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700876)code",
Austin Schuhf8440852024-05-31 10:46:50 -0700877 inline_data_size, object->fields()->size(), alignment, nominal_min_align,
878 offset_data_start_expression);
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700879 const std::string_view fbs_type_name = object->name()->string_view();
880 const std::string type_namespace = FlatbufferNameToCppName(
881 fbs_type_name.substr(0, fbs_type_name.find_last_of(".")));
882 const std::string type_name = AosNameForRawFlatbuffer(
883 fbs_type_name.substr(fbs_type_name.find_last_of(".") + 1));
884 const std::string object_code = absl::StrFormat(
885 R"code(
886namespace %s {
887class %s : public ::aos::fbs::Table {
888 public:
889 // The underlying "raw" flatbuffer type for this type.
890 typedef %s Flatbuffer;
James Kuszmaul6be41022023-12-20 11:55:28 -0800891 typedef flatbuffers::unique_ptr<Flatbuffer::NativeTableType> FlatbufferObjectType;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700892 // Returns this object as a flatbuffer type. This reference may not be valid
893 // following mutations to the underlying flatbuffer, due to how memory may get
894 // may get moved around.
895 const Flatbuffer &AsFlatbuffer() const { return *GetFlatbuffer<Flatbuffer>(); }
896%s
897%s
898 virtual ~%s() {}
899%s
900%s
901%s
James Kuszmaul6be41022023-12-20 11:55:28 -0800902%s
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700903 private:
904%s
905%s
906%s
Austin Schuhf8440852024-05-31 10:46:50 -0700907 public:
908 // Nominal size of this object, in bytes. The object may grow beyond this
909 // size, but will always start at this size and so the initial buffer must
910 // match this size.
911 %s
912 // Always statically allocate memory for tables (set for consistency with
913 // static_vector.h).
914 static constexpr size_t kPreallocatedSize = kSize;
915 // Size required for a buffer that includes a root table offset at the start.
916 static constexpr size_t kRootSize =
917 ::aos::fbs::AlignOffset(kSize + sizeof(::flatbuffers::uoffset_t), kAlign);
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700918};
919}
920 )code",
921 type_namespace, type_name, FlatbufferNameToCppName(fbs_type_name),
Austin Schuhf8440852024-05-31 10:46:50 -0700922 constants, MakeConstructor(type_name), type_name,
923 absl::StrJoin(accessors, ""), MakeFullClearer(fields), MakeCopier(fields),
924 MakeObjectCopier(fields), MakeMoveConstructor(type_name),
925 absl::StrJoin(members, ""), MakeSubObjectList(fields), size);
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700926
927 GeneratedObject result;
928 result.name = fbs_type_name;
929 result.include_declarations = includes;
930 result.code = object_code;
931 result.subobjects = subobject_names;
932 return result;
933}
934
935namespace {
936
937// Generated C++ code for an entire fbs file.
938// This includes all of the actual C++ code that will be written to a file (call
939// GenerateCode() to actually get the desired contents of the file).
940struct GeneratedCode {
941 // Prefix (for include guards).
942 std::string contents_prefix;
943 // Full set of required #include declarations.
944 std::set<std::string> include_declarations;
945 // Ordered list of objects (order is necessary to ensure that any dependencies
946 // between objects are managed correctly).
947 std::vector<GeneratedObject> objects;
948 // Suffix (for include guards).
949 std::string contents_suffix;
950
951 // Combine the above things into the string that actually needs to be written
952 // to a file.
953 std::string GenerateCode() const;
954 // Combines the code for multiple objects into one.
955 static GeneratedCode MergeCode(const std::vector<GeneratedObject> &objects);
956};
957
958std::string GeneratedCode::GenerateCode() const {
959 std::string result =
960 contents_prefix + absl::StrJoin(include_declarations, "");
961 for (const auto &object : objects) {
962 result += object.code;
963 }
964 result += contents_suffix;
965 return result;
966}
967
968GeneratedCode GeneratedCode::MergeCode(
969 const std::vector<GeneratedObject> &objects) {
970 GeneratedCode result;
971 // TODO(james): Should we use #ifdef include guards instead?
972 result.contents_prefix =
973 "#pragma once\n// This is a generated file. Do not modify.\n";
974 // We need to get the ordering of objects correct in order to ensure that
975 // depended-on objects appear before their dependees.
976 // In order to do this, we:
977 // 1) Assume that any objects not in the provided vector must exist in
978 // #includes and so can be ignored.
979 // 2) Create a list of all the objects we have been provided but which we have
980 // not yet added to the results vector.
981 // 3) Until said list is empty, we iterate over it and find any object(s)
982 // which have no dependencies in the list itself, and add them to the
983 // result.
984 // We aren't going to worry about efficient graph traversal here or anything.
985 // We also don't currently attempt to support circular dependencies.
986 std::map<std::string_view, const GeneratedObject *> remaining_objects;
987 for (const auto &object : objects) {
988 remaining_objects[object.name] = &object;
989 }
990 while (!remaining_objects.empty()) {
991 std::string_view to_remove;
992 for (const auto &pair : remaining_objects) {
993 bool has_dependencies = false;
994 for (const std::string_view subobject : pair.second->subobjects) {
995 if (remaining_objects.contains(subobject)) {
996 has_dependencies = true;
997 }
998 }
999 if (has_dependencies) {
1000 continue;
1001 }
1002 to_remove = pair.first;
1003 result.objects.push_back(*pair.second);
1004 result.include_declarations.insert(
1005 pair.second->include_declarations.begin(),
1006 pair.second->include_declarations.end());
1007 break;
1008 }
1009 // In order to support circular dependencies, two main things have to
1010 // change:
1011 // 1. We have to dynamically allow depopulating table members (rather than
1012 // just supporting dynamically lengthed vectors).
1013 // 2. Some of the codegen needs to be tweaked so that we can have the
1014 // generated
1015 // C++ classes depend on one another.
1016 CHECK(!to_remove.empty())
1017 << ": Circular dependencies in flatbuffers schemas are not supported.";
1018 CHECK_EQ(1u, remaining_objects.erase(to_remove))
1019 << ": Failed to remove " << to_remove;
1020 }
1021 return result;
1022}
1023} // namespace
1024
James Kuszmaul9a2d5f02023-12-14 11:38:35 -08001025std::string GenerateCodeForRootTableFile(const reflection::Schema *schema,
1026 std::string_view file_hint) {
1027 const reflection::Object *root_object = GetObject(schema, -1);
James Kuszmaulf5eb4682023-09-22 17:16:59 -07001028 const std::string_view root_file =
James Kuszmaul9a2d5f02023-12-14 11:38:35 -08001029 (root_object == nullptr) ? file_hint
1030 : root_object->declaration_file()->string_view();
1031 std::vector<GeneratedObject> objects;
1032 if (root_object != nullptr) {
1033 objects.push_back(GenerateCodeForObject(schema, root_object));
1034 }
James Kuszmaulf5eb4682023-09-22 17:16:59 -07001035 for (const reflection::Object *object : *schema->objects()) {
1036 if (object->is_struct()) {
1037 continue;
1038 }
1039 if (object->declaration_file()->string_view() == root_file) {
1040 objects.push_back(GenerateCodeForObject(schema, object));
1041 }
1042 }
1043 return GeneratedCode::MergeCode(objects).GenerateCode();
1044}
1045} // namespace aos::fbs