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: 's avatarWouter van Oortmerssen <wvo@google.com>
parent ab51b030
......@@ -658,6 +658,16 @@ FLATBUFFERS_FINAL_CLASS
}
#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,24 +1163,46 @@ FLATBUFFERS_FINAL_CLASS
/// will be prefixed with a standard FlatBuffers file header.
template<typename T> void Finish(Offset<T> root,
const char *file_identifier = nullptr) {
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:
// You shouldn't really be copying instances of this class.
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(sizeof(uoffset_t) + (file_identifier ? kFileIdentifierLength : 0),
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.o)); // Location of root.
PushElement(ReferTo(root)); // Location of root.
if (size_prefix) {
PushElement(GetSize());
}
finished = true;
}
private:
// You shouldn't really be copying instances of this class.
FlatBufferBuilder(const FlatBufferBuilder &);
FlatBufferBuilder &operator=(const FlatBufferBuilder &);
struct FieldLoc {
uoffset_t off;
voffset_t id;
......@@ -1224,7 +1256,11 @@ template<typename T> const T *GetRoot(const void *buf) {
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 @@ class Verifier FLATBUFFERS_FINAL_CLASS {
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 @@ class Verifier FLATBUFFERS_FINAL_CLASS {
;
}
// 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
......
......@@ -396,6 +396,25 @@ void ObjectFlatBuffersTest(uint8_t *flatbuf) {
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 @@ int main(int /*argc*/, const char * /*argv*/[]) {
ObjectFlatBuffersTest(flatbuf.get());
SizePrefixedTest();
#ifndef FLATBUFFERS_NO_FILE_TESTS
ParseAndGenerateTextTest();
ReflectionTest(flatbuf.get(), rawbuf.length());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment