mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-03 20:31:23 +00:00
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>
This commit is contained in:
@@ -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,17 +1163,20 @@ 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) {
|
||||
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 @@ FLATBUFFERS_FINAL_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((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 @@ 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());
|
||||
|
||||
Reference in New Issue
Block a user