Commit a360958b authored by gregoire-astruc's avatar gregoire-astruc Committed by Wouter van Oortmerssen

Implementation of a buffer release strategy.

* Tests for Release feature.
* Check vector_downward.buf_ before passing to deallocator.
* Assertions.
* Shared test between unique_ptr and GetBufferPointer()
* Unnecessary using directives.
* Reallocate vector if released on clear operation.
* Use allocator attribute.
* Renamed `Release()` to `ReleaseBufferPointer()`
* For consistency with `GetBufferPointer()`
* Updated documentation for ReleaseBuffer.

Change-Id: I108527778e56ae5127abf9e5b1be6b445ad75cb7
parent 432f3f26
...@@ -112,6 +112,12 @@ be compressed, or whatever you'd like to do with it. You can access the ...@@ -112,6 +112,12 @@ be compressed, or whatever you'd like to do with it. You can access the
start of the buffer with `fbb.GetBufferPointer()`, and it's size from start of the buffer with `fbb.GetBufferPointer()`, and it's size from
`fbb.GetSize()`. `fbb.GetSize()`.
Calling code may take ownership of the buffer with `fbb.ReleaseBufferPointer()`.
Should you do it, the `FlatBufferBuilder` will be in an invalid state,
and *must* be cleared before it can be used again.
However, it also means you are able to destroy the builder while keeping
the buffer in your application.
`samples/sample_binary.cpp` is a complete code sample similar to `samples/sample_binary.cpp` is a complete code sample similar to
the code above, that also includes the reading code below. the code above, that also includes the reading code below.
......
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
#include <type_traits> #include <type_traits>
#include <vector> #include <vector>
#include <algorithm> #include <algorithm>
#include <functional>
#include <memory>
#if __cplusplus <= 199711L && \ #if __cplusplus <= 199711L && \
(!defined(_MSC_VER) || _MSC_VER < 1600) && \ (!defined(_MSC_VER) || _MSC_VER < 1600) && \
...@@ -84,6 +86,10 @@ typedef uint16_t voffset_t; ...@@ -84,6 +86,10 @@ typedef uint16_t voffset_t;
typedef uintmax_t largest_scalar_t; typedef uintmax_t largest_scalar_t;
// Pointer to relinquished memory.
typedef std::unique_ptr<uint8_t, std::function<void(uint8_t * /* unused */)>>
unique_ptr_t;
// Wrapper for uoffset_t to allow safe template specialization. // Wrapper for uoffset_t to allow safe template specialization.
template<typename T> struct Offset { template<typename T> struct Offset {
uoffset_t o; uoffset_t o;
...@@ -358,9 +364,33 @@ class vector_downward { ...@@ -358,9 +364,33 @@ class vector_downward {
assert((initial_size & (sizeof(largest_scalar_t) - 1)) == 0); assert((initial_size & (sizeof(largest_scalar_t) - 1)) == 0);
} }
~vector_downward() { allocator_.deallocate(buf_); } ~vector_downward() {
if (buf_)
allocator_.deallocate(buf_);
}
void clear() {
if (buf_ == nullptr)
buf_ = allocator_.allocate(reserved_);
cur_ = buf_ + reserved_;
}
// Relinquish the pointer to the caller.
unique_ptr_t release() {
// Actually deallocate from the start of the allocated memory.
std::function<void(uint8_t *)> deleter(
std::bind(&simple_allocator::deallocate, allocator_, buf_));
void clear() { cur_ = buf_ + reserved_; } // Point to the desired offset.
unique_ptr_t retval(data(), deleter);
// Don't deallocate when this instance is destroyed.
buf_ = nullptr;
cur_ = nullptr;
return retval;
}
size_t growth_policy(size_t bytes) { size_t growth_policy(size_t bytes) {
return (bytes / 2) & ~(sizeof(largest_scalar_t) - 1); return (bytes / 2) & ~(sizeof(largest_scalar_t) - 1);
...@@ -385,10 +415,14 @@ class vector_downward { ...@@ -385,10 +415,14 @@ class vector_downward {
} }
uoffset_t size() const { uoffset_t size() const {
assert(cur_ != nullptr && buf_ != nullptr);
return static_cast<uoffset_t>(reserved_ - (cur_ - buf_)); return static_cast<uoffset_t>(reserved_ - (cur_ - buf_));
} }
uint8_t *data() const { return cur_; } uint8_t *data() const {
assert(cur_ != nullptr);
return cur_;
}
uint8_t *data_at(size_t offset) { return buf_ + reserved_ - offset; } uint8_t *data_at(size_t offset) { return buf_ + reserved_ - offset; }
...@@ -463,6 +497,10 @@ class FlatBufferBuilder FLATBUFFERS_FINAL_CLASS { ...@@ -463,6 +497,10 @@ class FlatBufferBuilder FLATBUFFERS_FINAL_CLASS {
// Get the serialized buffer (after you call Finish()). // Get the serialized buffer (after you call Finish()).
uint8_t *GetBufferPointer() const { return buf_.data(); } uint8_t *GetBufferPointer() const { return buf_.data(); }
// Get the released pointer to the serialized buffer.
// Don't attempt to use this FlatBufferBuilder afterwards!
unique_ptr_t ReleaseBufferPointer() { return buf_.release(); }
void ForceDefaults(bool fd) { force_defaults_ = fd; } void ForceDefaults(bool fd) { force_defaults_ = fd; }
void Pad(size_t num_bytes) { buf_.fill(num_bytes); } void Pad(size_t num_bytes) { buf_.fill(num_bytes); }
......
...@@ -60,7 +60,7 @@ uint32_t lcg_rand() { ...@@ -60,7 +60,7 @@ uint32_t lcg_rand() {
void lcg_reset() { lcg_seed = 48271; } void lcg_reset() { lcg_seed = 48271; }
// example of how to build up a serialized buffer algorithmically: // example of how to build up a serialized buffer algorithmically:
std::string CreateFlatBufferTest() { flatbuffers::unique_ptr_t CreateFlatBufferTest(std::string &buffer) {
flatbuffers::FlatBufferBuilder builder; flatbuffers::FlatBufferBuilder builder;
auto vec = Vec3(1, 2, 3, 0, Color_Red, Test(10, 20)); auto vec = Vec3(1, 2, 3, 0, Color_Red, Test(10, 20));
...@@ -119,24 +119,25 @@ std::string CreateFlatBufferTest() { ...@@ -119,24 +119,25 @@ std::string CreateFlatBufferTest() {
#endif #endif
// return the buffer for the caller to use. // return the buffer for the caller to use.
return std::string(reinterpret_cast<const char *>(builder.GetBufferPointer()), auto bufferpointer =
builder.GetSize()); reinterpret_cast<const char *>(builder.GetBufferPointer());
buffer.assign(bufferpointer, bufferpointer + builder.GetSize());
return builder.ReleaseBufferPointer();
} }
// example of accessing a buffer loaded in memory: // example of accessing a buffer loaded in memory:
void AccessFlatBufferTest(const std::string &flatbuf) { void AccessFlatBufferTest(const uint8_t *flatbuf, const std::size_t length) {
// First, verify the buffers integrity (optional) // First, verify the buffers integrity (optional)
flatbuffers::Verifier verifier( flatbuffers::Verifier verifier(flatbuf, length);
reinterpret_cast<const uint8_t *>(flatbuf.c_str()),
flatbuf.length());
TEST_EQ(VerifyMonsterBuffer(verifier), true); TEST_EQ(VerifyMonsterBuffer(verifier), true);
TEST_EQ(strcmp(MonsterIdentifier(), "MONS"), 0); TEST_EQ(strcmp(MonsterIdentifier(), "MONS"), 0);
TEST_EQ(MonsterBufferHasIdentifier(flatbuf.c_str()), true); TEST_EQ(MonsterBufferHasIdentifier(flatbuf), true);
// Access the buffer from the root. // Access the buffer from the root.
auto monster = GetMonster(flatbuf.c_str()); auto monster = GetMonster(flatbuf);
TEST_EQ(monster->hp(), 80); TEST_EQ(monster->hp(), 80);
TEST_EQ(monster->mana(), 150); // default TEST_EQ(monster->mana(), 150); // default
...@@ -593,8 +594,10 @@ void UnicodeTest() { ...@@ -593,8 +594,10 @@ void UnicodeTest() {
int main(int /*argc*/, const char * /*argv*/[]) { int main(int /*argc*/, const char * /*argv*/[]) {
// Run our various test suites: // Run our various test suites:
auto flatbuf = CreateFlatBufferTest(); std::string rawbuf;
AccessFlatBufferTest(flatbuf); auto flatbuf = CreateFlatBufferTest(rawbuf);
AccessFlatBufferTest(reinterpret_cast<const uint8_t *>(rawbuf.c_str()), rawbuf.length());
AccessFlatBufferTest(flatbuf.get(), rawbuf.length());
#ifndef __ANDROID__ // requires file access #ifndef __ANDROID__ // requires file access
ParseAndGenerateTextTest(); ParseAndGenerateTextTest();
......
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