blob: b556ed3ac620c899ed55f8edb7ab707a9f5412bc [file] [log] [blame]
James Kuszmaulf5eb4682023-09-22 17:16:59 -07001#ifndef AOS_FLATBUFFERS_BUILDER_H_
2#define AOS_FLATBUFFERS_BUILDER_H_
3#include "aos/flatbuffers.h"
4#include "aos/flatbuffers/static_table.h"
5namespace aos::fbs {
6
7// Builder class to handle the memory for a static flatbuffer object. This
8// fulfills a similar role to the FlatBufferBuilder type in the traditional API.
9// Typical usage:
10// aos::fbs::VectorAllocator allocator;
11// Builder<TestTableStatic> builder(&allocator);
12// TestTableStatic *object = builder.get();
13// object->set_scalar(123);
14//
15// At all points you will have a valid and complete flatbuffer, so you never
16// need to call Finish() or anything. You can just directly use the flatbuffer
17// as if it is a real flatbuffer.
18template <typename T>
19class Builder final : public ResizeableObject {
20 public:
21 static constexpr size_t kBufferSize = T::kUnalignedBufferSize;
22 Builder(Allocator *allocator)
23 : ResizeableObject(
24 allocator->AllocateOrDie(kBufferSize, T::kAlign, SetZero::kNo),
25 allocator),
26 flatbuffer_start_(BufferStart(buffer_)),
27 flatbuffer_(internal::GetSubSpan(buffer_, flatbuffer_start_, T::kSize),
28 this) {
29 SetPrefix();
30 }
31 Builder(std::unique_ptr<Allocator> allocator)
32 : ResizeableObject(
33 allocator->AllocateOrDie(kBufferSize, T::kAlign, SetZero::kNo),
34 std::move(allocator)),
35 flatbuffer_start_(BufferStart(buffer_)),
36 flatbuffer_(internal::GetSubSpan(buffer_, flatbuffer_start_, T::kSize),
37 this) {
38 SetPrefix();
39 }
40 Builder(Builder &&other)
41 : ResizeableObject(std::move(other)),
42 flatbuffer_(std::move(other.flatbuffer_)) {
43 flatbuffer_start_ = other.flatbuffer_start_;
44 other.flatbuffer_start_ = 0;
45 }
46
47 ~Builder() {
48 if (allocator() != nullptr) {
49 allocator()->Deallocate(buffer_);
50 }
51 }
52
53 // Returns an object containing the current raw flatbuffer type. Note that if
54 // the allocator type allows changes to the structure/amount of allocated
55 // memory, the underlying buffer will not be stable and so the returned
56 // FlatbufferSpan may be invalidated by mutations to the flatbuffer.
57 FlatbufferSpan<typename T::Flatbuffer> AsFlatbufferSpan() {
58 return {buffer()};
59 }
60
61 // Returns true if the flatbuffer is validly constructed. Should always return
62 // true (barring some sort of memory corruption). Exposed for convenience.
63 bool Verify() { return AsFlatbufferSpan().Verify(); }
64
65 // Returns the actual object for you to operate on and construct the
66 // flatbuffer. Unlike AsFlatbufferSpan(), this will be stable.
67 T *get() { return &flatbuffer_.t; }
68
69 private:
70 size_t Alignment() const override { return flatbuffer_.t.Alignment(); }
71 size_t AbsoluteOffsetOffset() const override { return 0; }
72 size_t NumberOfSubObjects() const override { return 1; }
73 void SetPrefix() {
74 // We can't do much if the provided buffer isn't at least 4-byte aligned,
75 // because we are required to put the root table offset at the start of the
76 // buffer.
77 CHECK_EQ(reinterpret_cast<size_t>(buffer_.data()) % alignof(uoffset_t), 0u);
78 *reinterpret_cast<uoffset_t *>(buffer_.data()) = flatbuffer_start_;
79 }
80 // Because the allocator API doesn't provide a way for us to request a
81 // strictly aligned buffer, manually align the start of the actual flatbuffer
82 // data if needed.
83 static size_t BufferStart(std::span<uint8_t> buffer) {
84 return aos::fbs::PaddedSize(
85 reinterpret_cast<size_t>(buffer.data()) + sizeof(uoffset_t),
86 T::kAlign) -
87 reinterpret_cast<size_t>(buffer.data());
88 }
89
90 // Some allocators don't do a great job of supporting arbitrary alignments; if
91 // the alignment of the buffer changes, we need to reshuffle everything to
92 // continue guaranteeing alignment.
93 void ObserveBufferModification() override {
94 const size_t new_start = BufferStart(buffer_);
95 if (new_start != flatbuffer_start_) {
96 const size_t used_size = flatbuffer_.t.buffer().size();
97 CHECK_LT(flatbuffer_start_ + used_size, buffer_.size());
98 CHECK_LT(new_start + used_size, buffer_.size());
99 memmove(buffer_.data() + new_start, buffer_.data() + flatbuffer_start_,
100 used_size);
101 flatbuffer_.t.UpdateBuffer(
102 internal::GetSubSpan(buffer_, new_start, used_size),
103 buffer_.data() + new_start, 0);
104 flatbuffer_start_ = new_start;
105 SetPrefix();
106 }
107 }
108 using ResizeableObject::SubObject;
109 SubObject GetSubObject(size_t index) override {
110 CHECK_EQ(0u, index);
111 return {reinterpret_cast<uoffset_t *>(buffer_.data()), &flatbuffer_.t,
112 &flatbuffer_start_};
113 }
114 // Offset from the start of the buffer to the actual start of the flatbuffer
115 // (identical to the root offset of the flatbuffer).
116 size_t flatbuffer_start_;
117 internal::TableMover<T> flatbuffer_;
118};
119} // namespace aos::fbs
120#endif // AOS_FLATBUFFERS_BUILDER_H_