blob: 0b5d2170bcda5852e4fdff9fb37378e7729eacf4 [file] [log] [blame]
James Kuszmaulf5eb4682023-09-22 17:16:59 -07001#ifndef AOS_FLATBUFFERS_STATIC_VECTOR_H_
2#define AOS_FLATBUFFERS_STATIC_VECTOR_H_
3#include <span>
4
5#include "flatbuffers/base.h"
6#include "glog/logging.h"
7
8#include "aos/containers/inlined_vector.h"
9#include "aos/containers/sized_array.h"
10#include "aos/flatbuffers/base.h"
11
12namespace aos::fbs {
13
14namespace internal {
15// Helper class for managing how we specialize the Vector object for different
16// contained types.
17// Users of the Vector class should never need to care about this.
18// Template arguments:
19// T: The type that the vector stores.
20// kInline: Whether the type in question is stored inline or not.
21// Enable: Used for SFINAE around struct values; can be ignored.
22// The struct provides the following types:
23// Type: The type of the data that will be stored inline in the vector.
24// ObjectType: The type of the actual data (only used for non-inline objects).
25// FlatbufferType: The type used by flatbuffers::Vector to store this type.
26// ConstFlatbufferType: The type used by a const flatbuffers::Vector to store
27// this type.
28// kDataAlign: Alignment required by the stored type.
29// kDataSize: Nominal size required by each non-inline data member. This is
30// what will be initially allocated; once created, individual members may
31// grow to accommodate dynamically lengthed vectors.
32template <typename T, bool kInline, class Enable = void>
33struct InlineWrapper;
34} // namespace internal
35
36// This Vector class provides a mutable, resizeable, flatbuffer vector.
37//
38// Upon creation, the Vector will start with enough space allocated for
39// kStaticLength elements, and must be provided with a memory buffer that
40// is large enough to serialize all the kStaticLength members (kStaticLength may
41// be zero).
42//
43// Once created, the Vector may be grown using calls to reserve().
44// This will result in the Vector attempting to allocate memory via its
45// parent object; such calls may fail if there is no space available in the
46// allocator.
47//
48// Note that if you are using the Vector class in a realtime context (and thus
49// must avoid dynamic memory allocations) you must only be using a Vector of
50// inline data (i.e., scalars, enums, or structs). Flatbuffer tables and strings
51// require overhead to manage and so require some form of dynamic memory
52// allocation. If we discover a strong use-case for such things, then we may
53// provide some interface that allows managing said metadata on the stack or
54// in another realtime-safe manner.
55//
56// Template arguments:
57// T: Type contained by the vector; either a scalar/struct/enum type or a
58// static flatbuffer type of some sort (a String or an implementation of
59// aos::fbs::Table).
60// kStaticLength: Number of elements to statically allocate memory for.
61// May be zero.
62// kInline: Whether the type T will be stored inline in the vector.
63// kForceAlign: Alignment to force for the start of the vector (e.g., for
64// byte arrays it may be desirable to have the entire array aligned).
65// kNullTerminate: Whether to reserve an extra byte past the end of
66// the inline data for null termination. Not included in kStaticLength,
67// so if e.g. you want to store the string "abc" then kStaticLength can
68// be 3 and kNullTerminate can be true and the vector data will take
69// up 4 bytes of memory.
70//
71// Vector buffer memory layout:
72// * Requirements:
73// * Minimum alignment of 4 bytes (for element count).
74// * The start of the vector data must be aligned to either
75// alignof(InlineType) or a user-specified number.
76// * The element count for the vector must immediately precede the vector
77// data (and so may itself not be aligned to alignof(InlineType)).
78// * For non-inlined types, the individual types must be aligned to
79// their own alignment.
80// * In order to accommodate this, the vector buffer as a whole must
81// generally be aligned to the greatest of the above alignments. There
82// are two reasonable ways one could do this:
83// * Require that the 4th byte of the buffer provided by aligned to
84// the maximum alignment of its contents.
85// * Require that the buffer itself by aligned, and provide padding
86// ourselves. The Vector would then have to expose its own offset
87// because it would not start at the start of the buffer.
88// The former requires that the wrapping code understand the internals
89// of how vectors work; the latter generates extra padding and adds
90// extra logic around handling non-zero offsets.
91// To maintain general simplicity, we will use the second condition and eat
92// the cost of the potential extra few bytes of padding.
93// * The layout of the buffer will thus be:
94// [padding; element_count; inline_data; padding; offset_data]
95// The first padding will be of size max(0, kAlign - 4).
96// The element_count is of size 4.
97// The inline_data is of size sizeof(InlineType) * kStaticLength.
98// The second padding is of size
99// (kAlign - ((sizeof(InlineType) * kStaticLength) % kAlign)).
100// The remaining data is only present if kInline is false.
101// The offset data is of size T::kSize * kStaticLength. T::kSize % T::kAlign
102// must be zero.
103// Note that no padding is required on the end because T::kAlign will always
104// end up being equal to the alignment (this can only be violated if
105// kForceAlign is used, but we do not allow that).
106template <typename T, size_t kStaticLength, bool kInline,
107 size_t kForceAlign = 0, bool kNullTerminate = false>
108class Vector : public ResizeableObject {
109 public:
110 static_assert(kInline || !kNullTerminate,
111 "It does not make sense to null-terminate vectors of objects.");
112 // Type stored inline in the serialized vector (offsets for tables/strings; T
113 // otherwise).
114 using InlineType = typename internal::InlineWrapper<T, kInline>::Type;
115 // OUt-of-line type for out-of-line T.
116 using ObjectType = typename internal::InlineWrapper<T, kInline>::ObjectType;
117 // Type used as the template parameter to flatbuffers::Vector<>.
118 using FlatbufferType =
119 typename internal::InlineWrapper<T, kInline>::FlatbufferType;
120 using ConstFlatbufferType =
121 typename internal::InlineWrapper<T, kInline>::ConstFlatbufferType;
122 // flatbuffers::Vector type that corresponds to this Vector.
123 typedef flatbuffers::Vector<FlatbufferType> Flatbuffer;
124 typedef const flatbuffers::Vector<ConstFlatbufferType> ConstFlatbuffer;
125 // Alignment of the inline data.
126 static constexpr size_t kInlineAlign =
127 std::max(kForceAlign, alignof(InlineType));
128 // Type used for serializing the length of the vector.
129 typedef uint32_t LengthType;
130 // Overall alignment of this type, and required alignment of the buffer that
131 // must be provided to the Vector.
132 static constexpr size_t kAlign =
133 std::max({alignof(LengthType), kInlineAlign,
134 internal::InlineWrapper<T, kInline>::kDataAlign});
135 // Padding inserted prior to the length element of the vector (to manage
136 // alignment of the data properly; see class comment)
137 static constexpr size_t kPadding1 =
138 std::max<size_t>(0, kAlign - sizeof(LengthType));
139 // Size of the vector length field.
140 static constexpr size_t kLengthSize = sizeof(LengthType);
141 // Size of all the inline vector data, including null termination (prior to
142 // any dynamic increases in size).
143 static constexpr size_t kInlineSize =
144 sizeof(InlineType) * (kStaticLength + (kNullTerminate ? 1 : 0));
145 // Per-element size of any out-of-line data.
146 static constexpr size_t kDataElementSize =
147 internal::InlineWrapper<T, kInline>::kDataSize;
148 // Padding between the inline data and any out-of-line data, to manage
149 // mismatches in alignment between the two.
150 static constexpr size_t kPadding2 = kAlign - (kInlineSize % kAlign);
151 // Total statically allocated space for any out-of-line data ("offset data")
152 // (prior to any dynamic increases in size).
153 static constexpr size_t kOffsetOffsetDataSize =
154 kInline ? 0 : (kStaticLength * kDataElementSize);
155 // Total nominal size of the Vector.
156 static constexpr size_t kSize =
157 kPadding1 + kLengthSize + kInlineSize + kPadding2 + kOffsetOffsetDataSize;
158 // Offset from the start of the provided buffer to where the actual start of
159 // the vector is.
160 static constexpr size_t kOffset = kPadding1;
161 // Constructors; the provided buffer must be aligned to kAlign and be kSize in
162 // length. parent must be non-null.
163 Vector(std::span<uint8_t> buffer, ResizeableObject *parent)
164 : ResizeableObject(buffer, parent) {
165 CHECK_EQ(0u, reinterpret_cast<size_t>(buffer.data()) % kAlign);
166 CHECK_EQ(kSize, buffer.size());
167 // Set padding and length to zero.
168 internal::ClearSpan(internal::GetSubSpan(buffer, 0, kPadding1));
169 SetLength(0u);
170 internal::ClearSpan(internal::GetSubSpan(
171 buffer, kPadding1 + kLengthSize + kInlineSize, kPadding2));
172 if (!kInline) {
173 // Initialize the offsets for any sub-tables. These are used to track
174 // where each table will get serialized in memory as memory gets
175 // resized/moved around.
176 for (size_t index = 0; index < kStaticLength; ++index) {
177 object_absolute_offsets_.emplace_back(kPadding1 + kLengthSize +
178 kInlineSize + kPadding2 +
179 index * kDataElementSize);
180 }
181 }
182 }
183 Vector(const Vector &) = delete;
184 Vector &operator=(const Vector &) = delete;
185 virtual ~Vector() {}
186 // Current allocated length of this vector.
187 // Does not include null termination.
188 size_t capacity() const { return allocated_length_; }
189 // Current length of the vector.
190 // Does not include null termination.
191 size_t size() const { return length_; }
192
193 // Appends an element to the Vector. Used when kInline is false. Returns
194 // nullptr if the append failed due to insufficient capacity. If you need to
195 // increase the capacity() of the vector, call reserve().
196 [[nodiscard]] T *emplace_back();
197 // Appends an element to the Vector. Used when kInline is true. Returns false
198 // if there is insufficient capacity for a new element.
199 [[nodiscard]] bool emplace_back(T element) {
200 static_assert(kInline);
201 return AddInlineElement(element);
202 }
203
204 // Adjusts the allocated size of the vector (does not affect the actual
205 // current length as returned by size()). Returns true on success, and false
206 // if the allocation failed for some reason.
207 // Note that reductions in size will not currently result in the allocated
208 // size actually changing.
209 [[nodiscard]] bool reserve(size_t new_length) {
210 if (new_length > allocated_length_) {
211 const size_t new_elements = new_length - allocated_length_;
212 // First, we must add space for our new inline elements.
213 if (!InsertBytes(
214 inline_data() + allocated_length_ + (kNullTerminate ? 1 : 0),
215 new_elements * sizeof(InlineType), SetZero::kYes)) {
216 return false;
217 }
218 if (!kInline) {
219 // For non-inline objects, create the space required for all the new
220 // object data.
221 const size_t insertion_point = buffer_.size();
222 if (!InsertBytes(buffer_.data() + insertion_point,
223 new_elements * kDataElementSize, SetZero::kYes)) {
224 return false;
225 }
226 for (size_t index = 0; index < new_elements; ++index) {
227 // Note that the already-allocated data may be arbitrarily-sized, so
228 // we cannot use the same static calculation that we do in the
229 // constructor.
230 object_absolute_offsets_.emplace_back(insertion_point +
231 index * kDataElementSize);
232 }
233 objects_.reserve(new_length);
234 }
235 allocated_length_ = new_length;
236 }
237 return true;
238 }
239
240 // Accessors for using the Vector as a flatbuffers::Vector.
241 // Note that these pointers will be unstable if any memory allocations occur
242 // that cause memory to get shifted around.
243 Flatbuffer *AsMutableFlatbufferVector() {
244 return reinterpret_cast<Flatbuffer *>(vector_buffer().data());
245 }
246 ConstFlatbuffer *AsFlatbufferVector() const {
247 return reinterpret_cast<const Flatbuffer *>(vector_buffer().data());
248 }
249
250 // Copies the contents of the provided vector into this; returns false on
251 // failure (e.g., if the provided vector is too long for the amount of space
252 // we can allocate through reserve()).
253 [[nodiscard]] bool FromFlatbuffer(ConstFlatbuffer *vector);
254
255 // Returns the element at the provided index. index must be less than size().
256 const T &at(size_t index) const {
257 CHECK_LT(index, length_);
258 return unsafe_at(index);
259 }
260
261 // Same as at(), except that bounds checks are only performed in non-optimized
262 // builds.
263 // TODO(james): The GetInlineElement() call itself does some bounds-checking;
264 // consider down-grading that.
265 const T &unsafe_at(size_t index) const {
266 DCHECK_LT(index, length_);
267 if (kInline) {
268 // This reinterpret_cast is extremely wrong if T != InlineType (this is
269 // fine because we only do this if kInline is true).
270 // TODO(james): Get the templating improved so that we can get away with
271 // specializing at() instead of using if statements. Resolving this will
272 // also allow deduplicating the Resize() calls.
273 // This specialization is difficult because you cannot partially
274 // specialize a templated class method (online things seem to suggest e.g.
275 // using a struct as the template parameter rather than having separate
276 // parameters).
277 return reinterpret_cast<const T &>(GetInlineElement(index));
278 } else {
279 return objects_[index].t;
280 }
281 }
282
283 // Returns a mutable pointer to the element at the provided index. index must
284 // be less than size().
285 T &at(size_t index) {
286 CHECK_LT(index, length_);
287 return unsafe_at(index);
288 }
289
290 // Same as at(), except that bounds checks are only performed in non-optimized
291 // builds.
292 // TODO(james): The GetInlineElement() call itself does some bounds-checking;
293 // consider down-grading that.
294 T &unsafe_at(size_t index) {
295 DCHECK_LT(index, length_);
296 if (kInline) {
297 // This reinterpret_cast is extremely wrong if T != InlineType (this is
298 // fine because we only do this if kInline is true).
299 // TODO(james): Get the templating improved so that we can get away with
300 // specializing at() instead of using if statements. Resolving this will
301 // also allow deduplicating the Resize() calls.
302 // This specialization is difficult because you cannot partially
303 // specialize a templated class method (online things seem to suggest e.g.
304 // using a struct as the template parameter rather than having separate
305 // parameters).
306 return reinterpret_cast<T &>(GetInlineElement(index));
307 } else {
308 return objects_[index].t;
309 }
310 }
311
312 const T &operator[](size_t index) const { return at(index); }
313 T &operator[](size_t index) { return at(index); }
314
315 // Resizes the vector to the requested size.
316 // size must be less than or equal to the current capacity() of the vector.
317 // Does not allocate additional memory (call reserve() to allocate additional
318 // memory).
319 // Zero-initializes all inline element; initializes all subtable/string
320 // elements to extant but empty objects.
321 void resize(size_t size);
322
323 // Resizes an inline vector to the requested size.
324 // When changing the size of the vector, the removed/inserted elements will be
325 // set to zero if requested. Otherwise, they will be left uninitialized.
326 void resize_inline(size_t size, SetZero set_zero) {
327 CHECK_LE(size, allocated_length_);
328 static_assert(
329 kInline,
330 "Vector::resize_inline() only works for inline vector types (scalars, "
331 "enums, structs).");
332 if (size == length_) {
333 return;
334 }
335 if (set_zero == SetZero::kYes) {
336 memset(
337 reinterpret_cast<void *>(inline_data() + std::min(size, length_)), 0,
338 std::abs(static_cast<ssize_t>(length_) - static_cast<ssize_t>(size)) *
339 sizeof(InlineType));
340 }
341 length_ = size;
342 SetLength(length_);
343 }
344 // Resizes a vector of offsets to the requested size.
345 // If the size is increased, the new elements will be initialized
346 // to empty but extant objects for non-inlined types (so, zero-length
347 // vectors/strings; objects that exist but have no fields populated).
348 // Note that this is always equivalent to resize().
349 void resize_not_inline(size_t size) {
350 CHECK_LE(size, allocated_length_);
351 static_assert(!kInline,
352 "Vector::resize_not_inline() only works for offset vector "
353 "types (objects, strings).");
354 if (size == length_) {
355 return;
356 } else if (length_ > size) {
357 // TODO: Remove any excess allocated memory.
358 length_ = size;
359 SetLength(length_);
360 return;
361 } else {
362 while (length_ < size) {
363 CHECK_NOTNULL(emplace_back());
364 }
365 }
366 }
367
368 // Accessors directly to the inline data of a vector.
369 const T *data() const {
370 static_assert(kInline,
371 "If you have a use-case for directly accessing the "
372 "flatbuffer data pointer for vectors of "
373 "objects/strings, please start a discussion.");
374 return inline_data();
375 }
376
377 T *data() {
378 static_assert(kInline,
379 "If you have a use-case for directly accessing the "
380 "flatbuffer data pointer for vectors of "
381 "objects/strings, please start a discussion.");
382 return inline_data();
383 }
384
385 std::string SerializationDebugString() const {
386 std::stringstream str;
387 str << "Raw Size: " << kSize << " alignment: " << kAlign
388 << " allocated length: " << allocated_length_ << " inline alignment "
389 << kInlineAlign << " kPadding1 " << kPadding1 << "\n";
390 str << "Observed length " << GetLength() << " (expected " << length_
391 << ")\n";
392 str << "Inline Size " << kInlineSize << " Inline bytes/value:\n";
393 // TODO(james): Get pretty-printing for structs so we can provide better
394 // debug.
395 internal::DebugBytes(
396 internal::GetSubSpan(vector_buffer(), kLengthSize,
397 sizeof(InlineType) * allocated_length_),
398 str);
399 str << "kPadding2 " << kPadding2 << " offset data size "
400 << kOffsetOffsetDataSize << "\n";
401 return str.str();
402 }
403
404 protected:
405 friend struct internal::TableMover<
406 Vector<T, kStaticLength, kInline, kForceAlign, kNullTerminate>>;
407 // protected so that the String class can access the move constructor.
408 Vector(Vector &&) = default;
409
410 private:
411 // See kAlign and kOffset.
412 size_t Alignment() const final { return kAlign; }
413 size_t AbsoluteOffsetOffset() const override { return kOffset; }
414 // Returns a buffer that starts at the start of the vector itself (past any
415 // padding).
416 std::span<uint8_t> vector_buffer() {
417 return internal::GetSubSpan(buffer(), kPadding1);
418 }
419 std::span<const uint8_t> vector_buffer() const {
420 return internal::GetSubSpan(buffer(), kPadding1);
421 }
422
423 bool AddInlineElement(InlineType e) {
424 if (length_ == allocated_length_) {
425 return false;
426 }
427 SetInlineElement(length_, e);
428 ++length_;
429 SetLength(length_);
430 return true;
431 }
432
433 void SetInlineElement(size_t index, InlineType value) {
434 CHECK_LT(index, allocated_length_);
435 inline_data()[index] = value;
436 }
437
438 InlineType &GetInlineElement(size_t index) {
439 CHECK_LT(index, allocated_length_);
440 return inline_data()[index];
441 }
442
443 const InlineType &GetInlineElement(size_t index) const {
444 CHECK_LT(index, allocated_length_);
445 return inline_data()[index];
446 }
447
448 // Returns a pointer to the start of the inline data itself.
449 InlineType *inline_data() {
450 return reinterpret_cast<InlineType *>(vector_buffer().data() + kLengthSize);
451 }
452 const InlineType *inline_data() const {
453 return reinterpret_cast<const InlineType *>(vector_buffer().data() +
454 kLengthSize);
455 }
456
457 // Updates the length of the vector to match the provided length. Does not set
458 // the length_ member.
459 void SetLength(LengthType length) {
460 *reinterpret_cast<LengthType *>(vector_buffer().data()) = length;
461 if (kNullTerminate) {
462 memset(reinterpret_cast<void *>(inline_data() + length), 0,
463 sizeof(InlineType));
464 }
465 }
466 LengthType GetLength() const {
467 return *reinterpret_cast<const LengthType *>(vector_buffer().data());
468 }
469
470 // Overrides to allow ResizeableObject to manage memory adjustments.
471 size_t NumberOfSubObjects() const final {
472 return kInline ? 0 : allocated_length_;
473 }
474 using ResizeableObject::SubObject;
475 SubObject GetSubObject(size_t index) final {
476 return SubObject{
477 reinterpret_cast<uoffset_t *>(&GetInlineElement(index)),
478 // In order to let this compile regardless of whether type T is an
479 // object type or not, we just use a reinterpret_cast.
480 (index < length_)
481 ? reinterpret_cast<ResizeableObject *>(&objects_[index].t)
482 : nullptr,
483 &object_absolute_offsets_[index]};
484 }
485 // Implementation that handles copying from a flatbuffers::Vector of an inline
486 // data type.
487 [[nodiscard]] bool FromInlineFlatbuffer(ConstFlatbuffer *vector) {
488 if (!reserve(CHECK_NOTNULL(vector)->size())) {
489 return false;
490 }
491
492 // We will be overwriting the whole vector very shortly; there is no need to
493 // clear the buffer to zero.
494 resize_inline(vector->size(), SetZero::kNo);
495
496 memcpy(inline_data(), vector->Data(), size() * sizeof(InlineType));
497 return true;
498 }
499
500 // Implementation that handles copying from a flatbuffers::Vector of a
501 // not-inline data type.
502 [[nodiscard]] bool FromNotInlineFlatbuffer(const Flatbuffer *vector) {
503 if (!reserve(vector->size())) {
504 return false;
505 }
506 // "Clear" the vector.
507 resize_not_inline(0);
508
509 for (const typename T::Flatbuffer *entry : *vector) {
510 if (!CHECK_NOTNULL(emplace_back())->FromFlatbuffer(entry)) {
511 return false;
512 }
513 }
514 return true;
515 }
516
517 // In order to allow for easy partial template specialization, we use a
518 // non-member class to call FromInline/FromNotInlineFlatbuffer and
519 // resize_inline/resize_not_inline. There are not actually any great ways to
520 // do this with just our own class member functions, so instead we make these
521 // methods members of a friend of the Vector class; we then partially
522 // specialize the entire InlineWrapper class and use it to isolate anything
523 // that needs to have a common user interface while still having separate
524 // actual logic.
525 template <typename T_, bool kInline_, class Enable_>
526 friend struct internal::InlineWrapper;
527
528 // Note: The objects here really want to be owned by this object (as opposed
529 // to e.g. returning a stack-allocated object from the emplace_back() methods
530 // that the user then owns). There are two main challenges with have the user
531 // own the object on question:
532 // 1. We can't have >1 reference floating around, or else one object's state
533 // can become out of date. This forces us to do ref-counting and could
534 // make certain types of code obnoxious to write.
535 // 2. Once the user-created object goes out of scope, we lose all of its
536 // internal state. In _theory_ it should be possible to reconstruct most
537 // of the relevant state by examining the contents of the buffer, but
538 // doing so would be cumbersome.
539 aos::InlinedVector<internal::TableMover<ObjectType>,
540 kInline ? 0 : kStaticLength>
541 objects_;
542 aos::InlinedVector<size_t, kInline ? 0 : kStaticLength>
543 object_absolute_offsets_;
544 // Current actual length of the vector.
545 size_t length_ = 0;
546 // Current length that we have allocated space available for.
547 size_t allocated_length_ = kStaticLength;
548};
549
550template <typename T, size_t kStaticLength, bool kInline, size_t kForceAlign,
551 bool kNullTerminate>
552T *Vector<T, kStaticLength, kInline, kForceAlign,
553 kNullTerminate>::emplace_back() {
554 static_assert(!kInline);
555 if (length_ >= allocated_length_) {
556 return nullptr;
557 }
558 const size_t object_start = object_absolute_offsets_[length_];
559 std::span<uint8_t> object_buffer =
560 internal::GetSubSpan(buffer(), object_start, T::kSize);
561 objects_.emplace_back(object_buffer, this);
562 const uoffset_t offset =
563 object_start - (reinterpret_cast<size_t>(&GetInlineElement(length_)) -
564 reinterpret_cast<size_t>(buffer().data()));
565 CHECK(AddInlineElement(offset));
566 return &objects_[objects_.size() - 1].t;
567}
568
569// The String class is a special version of the Vector that is always
570// null-terminated, always contains 1-byte character elements, and which has a
571// few extra methods for convenient string access.
572template <size_t kStaticLength>
573class String : public Vector<char, kStaticLength, true, 0, true> {
574 public:
575 typedef Vector<char, kStaticLength, true, 0, true> VectorType;
576 typedef flatbuffers::String Flatbuffer;
577 String(std::span<uint8_t> buffer, ResizeableObject *parent)
578 : VectorType(buffer, parent) {}
579 virtual ~String() {}
580 void SetString(std::string_view string) {
581 CHECK_LT(string.size(), VectorType::capacity());
582 VectorType::resize_inline(string.size(), SetZero::kNo);
583 memcpy(VectorType::data(), string.data(), string.size());
584 }
585 std::string_view string_view() const {
586 return std::string_view(VectorType::data(), VectorType::size());
587 }
588 std::string str() const {
589 return std::string(VectorType::data(), VectorType::size());
590 }
591 const char *c_str() const { return VectorType::data(); }
592
593 private:
594 friend struct internal::TableMover<String<kStaticLength>>;
595 String(String &&) = default;
596};
597
598namespace internal {
599// Specialization for all non-inline vector types. All of these types will just
600// use offsets for their inline data and have appropriate member types/constants
601// for the remaining fields.
602template <typename T>
603struct InlineWrapper<T, false, void> {
604 typedef uoffset_t Type;
605 typedef T ObjectType;
606 typedef flatbuffers::Offset<typename T::Flatbuffer> FlatbufferType;
607 typedef flatbuffers::Offset<typename T::Flatbuffer> ConstFlatbufferType;
608 static_assert((T::kSize % T::kAlign) == 0);
609 static constexpr size_t kDataAlign = T::kAlign;
610 static constexpr size_t kDataSize = T::kSize;
611 template <typename StaticVector>
612 static bool FromFlatbuffer(
613 StaticVector *to, const typename StaticVector::ConstFlatbuffer *from) {
614 return to->FromNotInlineFlatbuffer(from);
615 }
616 template <typename StaticVector>
617 static void ResizeVector(StaticVector *target, size_t size) {
618 target->resize_not_inline(size);
619 }
620};
621// Specialization for "normal" scalar inline data (ints, floats, doubles,
622// enums).
623template <typename T>
624struct InlineWrapper<T, true,
625 typename std::enable_if_t<!std::is_class<T>::value>> {
626 typedef T Type;
627 typedef T ObjectType;
628 typedef T FlatbufferType;
629 typedef T ConstFlatbufferType;
630 static constexpr size_t kDataAlign = alignof(T);
631 static constexpr size_t kDataSize = sizeof(T);
632 template <typename StaticVector>
633 static bool FromFlatbuffer(
634 StaticVector *to, const typename StaticVector::ConstFlatbuffer *from) {
635 return to->FromInlineFlatbuffer(from);
636 }
637 template <typename StaticVector>
638 static void ResizeVector(StaticVector *target, size_t size) {
639 target->resize_inline(size, SetZero::kYes);
640 }
641};
642// Specialization for booleans, given that flatbuffers uses uint8_t's for bools.
643template <>
644struct InlineWrapper<bool, true, void> {
645 typedef uint8_t Type;
646 typedef uint8_t ObjectType;
647 typedef uint8_t FlatbufferType;
648 typedef uint8_t ConstFlatbufferType;
649 static constexpr size_t kDataAlign = 1u;
650 static constexpr size_t kDataSize = 1u;
651 template <typename StaticVector>
652 static bool FromFlatbuffer(
653 StaticVector *to, const typename StaticVector::ConstFlatbuffer *from) {
654 return to->FromInlineFlatbuffer(from);
655 }
656 template <typename StaticVector>
657 static void ResizeVector(StaticVector *target, size_t size) {
658 target->resize_inline(size, SetZero::kYes);
659 }
660};
661// Specialization for flatbuffer structs.
662// The flatbuffers codegen uses struct pointers rather than references or the
663// such, so it needs to be treated special.
664template <typename T>
665struct InlineWrapper<T, true,
666 typename std::enable_if_t<std::is_class<T>::value>> {
667 typedef T Type;
668 typedef T ObjectType;
669 typedef T *FlatbufferType;
670 typedef const T *ConstFlatbufferType;
671 static constexpr size_t kDataAlign = alignof(T);
672 static constexpr size_t kDataSize = sizeof(T);
673 template <typename StaticVector>
674 static bool FromFlatbuffer(
675 StaticVector *to, const typename StaticVector::ConstFlatbuffer *from) {
676 return to->FromInlineFlatbuffer(from);
677 }
678 template <typename StaticVector>
679 static void ResizeVector(StaticVector *target, size_t size) {
680 target->resize_inline(size, SetZero::kYes);
681 }
682};
683} // namespace internal
684 //
685template <typename T, size_t kStaticLength, bool kInline, size_t kForceAlign,
686 bool kNullTerminate>
687bool Vector<T, kStaticLength, kInline, kForceAlign,
688 kNullTerminate>::FromFlatbuffer(ConstFlatbuffer *vector) {
689 return internal::InlineWrapper<T, kInline>::FromFlatbuffer(this, vector);
690}
691
692template <typename T, size_t kStaticLength, bool kInline, size_t kForceAlign,
693 bool kNullTerminate>
694void Vector<T, kStaticLength, kInline, kForceAlign, kNullTerminate>::resize(
695 size_t size) {
696 internal::InlineWrapper<T, kInline>::ResizeVector(this, size);
697}
698
699} // namespace aos::fbs
700#endif // AOS_FLATBUFFERS_STATIC_VECTOR_H_