diff --git a/lua/flatbuffers/builder.lua b/lua/flatbuffers/builder.lua index 2fb22204d..19f214de5 100644 --- a/lua/flatbuffers/builder.lua +++ b/lua/flatbuffers/builder.lua @@ -60,6 +60,23 @@ function m.New(initialSize) return o end +-- Clears the builder and resets the state. It does not actually clear the backing binary array, it just reuses it as +-- needed. This is a performant way to use the builder for multiple constructions without the overhead of multiple +-- builder allocations. +function mt:Clear() + self.finished = false + self.nested = false + self.minalign = 1 + self.currentVTable = nil + self.objectEnd = nil + self.head = #self.bytes -- place the head at the end of the binary array + + -- clear vtables instead of making a new table + local vtable = self.vtables + local vtableCount = #vtable + for i=1,vtableCount do vtable[i] = nil end +end + function mt:Output(full) assert(self.finished, "Builder Not Finished") if full then diff --git a/tests/luatest.lua b/tests/luatest.lua index c85a4ec99..04f185ec6 100644 --- a/tests/luatest.lua +++ b/tests/luatest.lua @@ -81,8 +81,8 @@ local function checkReadBuffer(buf, offset, sizePrefix) assert(mon:Testempty() == nil) end -local function generateMonster(sizePrefix) - local b = flatbuffers.Builder(0) +local function generateMonster(sizePrefix, b) + b = b or flatbuffers.Builder(0) local str = b:CreateString("MyMonster") local test1 = b:CreateString("test1") local test2 = b:CreateString("test2") @@ -156,6 +156,51 @@ local function sizePrefix(sizePrefix) checkReadBuffer(buf, offset, sizePrefix) end +local function fbbClear() + -- Generate a builder that will be 'cleared' and reused to create two different objects. + local fbb = flatbuffers.Builder(0) + + -- First use the builder to read the normal monster data and verify it works + local buf, offset = generateMonster(false, fbb) + checkReadBuffer(buf, offset, false) + + -- Then clear the builder to be used again + fbb:Clear() + + -- Storage for the built monsters + local monsters = {} + local lastBuf + + -- Make another builder that will be use identically to the 'cleared' one so outputs can be compared. Build both the + -- Cleared builder and new builder in the exact same way, so we can compare their results + for i, builder in ipairs({fbb, flatbuffers.Builder(0)}) do + local strOffset = builder:CreateString("Hi there") + monster.Start(builder) + monster.AddPos(builder, vec3.CreateVec3(builder, 3.0, 2.0, 1.0, 17.0, 3, 100, 123)) + monster.AddName(builder, strOffset) + monster.AddMana(builder, 123) + builder:Finish(monster.End(builder)) + local buf = builder:Output(false) + if not lastBuf then + lastBuf = buf + else + -- the output, sized-buffer should be identical + assert(lastBuf == buf, "Monster output buffers are not identical") + end + monsters[i] = monster.GetRootAsMonster(flatbuffers.binaryArray.New(buf), 0) + end + + -- Check that all the fields for the generated monsters are as we expect + for i, monster in ipairs(monsters) do + assert(monster:Name() == "Hi there", "Monster Name is not 'Hi There' for monster "..i) + -- HP is default to 100 in the schema, but we change it in generateMonster to 80, so this is a good test to + -- see if the cleared builder really clears the data. + assert(monster:Hp() == 100, "HP doesn't equal the default value for monster "..i) + assert(monster:Mana() == 123, "Monster Mana is not '123' for monster "..i) + assert(monster:Pos():X() == 3.0, "Monster vec3.X is not '3' for monster "..i) + end +end + local function testCanonicalData() local f = assert(io.open('monsterdata_test.mon', 'rb')) local wireData = f:read("*a") @@ -219,6 +264,10 @@ local tests = d = "Test size prefix", args = {{true}, {false}} }, + { + f = fbbClear, + d = "FlatBufferBuilder Clear", + }, { f = testCanonicalData, d = "Tests Canonical flatbuffer file included in repo"