Added support for size prefixed buffers.
These are useful for streaming FlatBuffers. The functionality
ensures proper alignment of the whole buffer.
Tested: on OS X.
Bug: 27123865
Change-Id: Ic7d75a618c1bb470ea44c4dcf202ff71f2b3f4f1
Signed-off-by: Wouter van Oortmerssen <wvo@google.com>
diff --git a/include/flatbuffers/flatbuffers.h b/include/flatbuffers/flatbuffers.h
index 8c1c7f0..39942e2 100644
--- a/include/flatbuffers/flatbuffers.h
+++ b/include/flatbuffers/flatbuffers.h
@@ -658,6 +658,16 @@
}
#endif
+ /// @brief get the minimum alignment this buffer needs to be accessed
+ /// properly. This is only known once all elements have been written (after
+ /// you call Finish()). You can use this information if you need to embed
+ /// a FlatBuffer in some other buffer, such that you can later read it
+ /// without first having to copy it into its own buffer.
+ size_t GetBufferMinAlignment() {
+ Finished();
+ return minalign_;
+ }
+
/// @cond FLATBUFFERS_INTERNAL
void Finished() const {
// If you get this assert, you're attempting to get access a buffer
@@ -1153,17 +1163,20 @@
/// will be prefixed with a standard FlatBuffers file header.
template<typename T> void Finish(Offset<T> root,
const char *file_identifier = nullptr) {
- NotNested();
- // This will cause the whole buffer to be aligned.
- PreAlign(sizeof(uoffset_t) + (file_identifier ? kFileIdentifierLength : 0),
- minalign_);
- if (file_identifier) {
- assert(strlen(file_identifier) == kFileIdentifierLength);
- buf_.push(reinterpret_cast<const uint8_t *>(file_identifier),
- kFileIdentifierLength);
- }
- PushElement(ReferTo(root.o)); // Location of root.
- finished = true;
+
+ Finish(root.o, file_identifier, false);
+ }
+
+ /// @brief Finish a buffer with a 32 bit size field pre-fixed (size of the
+ /// buffer following the size field). These buffers are NOT compatible
+ /// with standard buffers created by Finish, i.e. you can't call GetRoot
+ /// on them, you have to use GetSizePrefixedRoot instead.
+ /// All >32 bit quantities in this buffer will be aligned when the whole
+ /// size pre-fixed buffer is aligned.
+ /// These kinds of buffers are useful for creating a stream of FlatBuffers.
+ template<typename T> void FinishSizePrefixed(Offset<T> root,
+ const char *file_identifier = nullptr) {
+ Finish(root.o, file_identifier, true);
}
private:
@@ -1171,6 +1184,25 @@
FlatBufferBuilder(const FlatBufferBuilder &);
FlatBufferBuilder &operator=(const FlatBufferBuilder &);
+ void Finish(uoffset_t root, const char *file_identifier, bool size_prefix) {
+ NotNested();
+ // This will cause the whole buffer to be aligned.
+ PreAlign((size_prefix ? sizeof(uoffset_t) : 0) +
+ sizeof(uoffset_t) +
+ (file_identifier ? kFileIdentifierLength : 0),
+ minalign_);
+ if (file_identifier) {
+ assert(strlen(file_identifier) == kFileIdentifierLength);
+ buf_.push(reinterpret_cast<const uint8_t *>(file_identifier),
+ kFileIdentifierLength);
+ }
+ PushElement(ReferTo(root)); // Location of root.
+ if (size_prefix) {
+ PushElement(GetSize());
+ }
+ finished = true;
+ }
+
struct FieldLoc {
uoffset_t off;
voffset_t id;
@@ -1224,7 +1256,11 @@
return GetMutableRoot<T>(const_cast<void *>(buf));
}
-/// Helpers to get a typed pointer to objects that are currently beeing built.
+template<typename T> const T *GetSizePrefixedRoot(const void *buf) {
+ return GetRoot<T>(reinterpret_cast<const uint8_t *>(buf) + sizeof(uoffset_t));
+}
+
+/// Helpers to get a typed pointer to objects that are currently being built.
/// @warning Creating new objects will lead to reallocations and invalidates
/// the pointer!
template<typename T> T *GetMutableTemporaryPointer(FlatBufferBuilder &fbb,
@@ -1348,16 +1384,17 @@
return true;
}
- // Verify this whole buffer, starting with root type T.
- template<typename T> bool VerifyBuffer(const char *identifier) {
- if (identifier && (size_t(end_ - buf_) < 2 * sizeof(flatbuffers::uoffset_t) ||
- !BufferHasIdentifier(buf_, identifier))) {
+ template<typename T> bool VerifyBufferFromStart(const char *identifier,
+ const uint8_t *start) {
+ if (identifier &&
+ (size_t(end_ - start) < 2 * sizeof(flatbuffers::uoffset_t) ||
+ !BufferHasIdentifier(start, identifier))) {
return false;
}
// Call T::Verify, which must be in the generated code for this type.
- return Verify<uoffset_t>(buf_) &&
- reinterpret_cast<const T *>(buf_ + ReadScalar<uoffset_t>(buf_))->
+ return Verify<uoffset_t>(start) &&
+ reinterpret_cast<const T *>(start + ReadScalar<uoffset_t>(start))->
Verify(*this)
#ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE
&& GetComputedSize()
@@ -1365,6 +1402,17 @@
;
}
+ // Verify this whole buffer, starting with root type T.
+ template<typename T> bool VerifyBuffer(const char *identifier) {
+ return VerifyBufferFromStart<T>(identifier, buf_);
+ }
+
+ template<typename T> bool VerifySizePrefixedBuffer(const char *identifier) {
+ return Verify<uoffset_t>(buf_) &&
+ ReadScalar<uoffset_t>(buf_) == end_ - buf_ - sizeof(uoffset_t) &&
+ VerifyBufferFromStart<T>(identifier, buf_ + sizeof(uoffset_t));
+ }
+
// Called at the start of a table to increase counters measuring data
// structure depth and amount, and possibly bails out with false if
// limits set by the constructor have been hit. Needs to be balanced
diff --git a/tests/test.cpp b/tests/test.cpp
index 6567b91..d704fb6 100644
--- a/tests/test.cpp
+++ b/tests/test.cpp
@@ -396,6 +396,25 @@
TEST_EQ(tests[1].b(), 40);
}
+// Prefix a FlatBuffer with a size field.
+void SizePrefixedTest() {
+ // Create size prefixed buffer.
+ flatbuffers::FlatBufferBuilder fbb;
+ fbb.FinishSizePrefixed(CreateMonster(fbb, 0, 200, 300,
+ fbb.CreateString("bob")));
+
+ // Verify it.
+ flatbuffers::Verifier verifier(fbb.GetBufferPointer(), fbb.GetSize());
+ TEST_EQ(verifier.VerifySizePrefixedBuffer<Monster>(nullptr), true);
+
+ // Access it.
+ auto m = flatbuffers::GetSizePrefixedRoot<MyGame::Example::Monster>(
+ fbb.GetBufferPointer());
+ TEST_EQ(m->mana(), 200);
+ TEST_EQ(m->hp(), 300);
+ TEST_EQ_STR(m->name()->c_str(), "bob");
+}
+
// example of parsing text straight into a buffer, and generating
// text back from it:
void ParseAndGenerateTextTest() {
@@ -1242,6 +1261,8 @@
ObjectFlatBuffersTest(flatbuf.get());
+ SizePrefixedTest();
+
#ifndef FLATBUFFERS_NO_FILE_TESTS
ParseAndGenerateTextTest();
ReflectionTest(flatbuf.get(), rawbuf.length());