1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
|
local N = require("flatbuffers.numTypes")
local ba = require("flatbuffers.binaryarray")
local compat = require("flatbuffers.compat")
local string_unpack = compat.string_unpack
local m = {}
local mt = {}
-- get locals for faster access
local VOffsetT = N.VOffsetT
local UOffsetT = N.UOffsetT
local SOffsetT = N.SOffsetT
local Bool = N.Bool
local Uint8 = N.Uint8
local Uint16 = N.Uint16
local Uint32 = N.Uint32
local Uint64 = N.Uint64
local Int8 = N.Int8
local Int16 = N.Int16
local Int32 = N.Int32
local Int64 = N.Int64
local Float32 = N.Float32
local Float64 = N.Float64
local MAX_BUFFER_SIZE = 0x80000000 -- 2 GB
local VtableMetadataFields = 2
local getAlignSize = compat.GetAlignSize
local function vtableEqual(a, objectStart, b)
UOffsetT:EnforceNumber(objectStart)
if (#a * 2) ~= #b then
return false
end
for i, elem in ipairs(a) do
local x = string_unpack(VOffsetT.packFmt, b, 1 + (i - 1) * 2)
if x ~= 0 or elem ~= 0 then
local y = objectStart - elem
if x ~= y then
return false
end
end
end
return true
end
function m.New(initialSize)
assert(0 <= initialSize and initialSize < MAX_BUFFER_SIZE)
local o =
{
finished = false,
bytes = ba.New(initialSize),
nested = false,
head = initialSize,
minalign = 1,
vtables = {}
}
setmetatable(o, {__index = mt})
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.size -- 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
return self.bytes:Slice()
else
return self.bytes:Slice(self.head)
end
end
function mt:StartObject(numFields)
assert(not self.nested)
local vtable = {}
for _=1,numFields do
table.insert(vtable, 0)
end
self.currentVTable = vtable
self.objectEnd = self:Offset()
self.nested = true
end
function mt:WriteVtable()
self:PrependSOffsetTRelative(0)
local objectOffset = self:Offset()
local exisitingVTable
local i = #self.vtables
while i >= 1 do
if self.vtables[i] == 0 then
table.remove(self.vtables,i)
end
i = i - 1
end
i = #self.vtables
while i >= 1 do
local vt2Offset = self.vtables[i]
local vt2Start = self.bytes.size - vt2Offset
local vt2lenstr = self.bytes:Slice(vt2Start, vt2Start+1)
local vt2Len = string_unpack(VOffsetT.packFmt, vt2lenstr, 1)
local metadata = VtableMetadataFields * 2
local vt2End = vt2Start + vt2Len
local vt2 = self.bytes:Slice(vt2Start+metadata,vt2End)
if vtableEqual(self.currentVTable, objectOffset, vt2) then
exisitingVTable = vt2Offset
break
end
i = i - 1
end
if not exisitingVTable then
i = #self.currentVTable
while i >= 1 do
local off = 0
local a = self.currentVTable[i]
if a and a ~= 0 then
off = objectOffset - a
end
self:PrependVOffsetT(off)
i = i - 1
end
local objectSize = objectOffset - self.objectEnd
self:PrependVOffsetT(objectSize)
local vBytes = #self.currentVTable + VtableMetadataFields
vBytes = vBytes * 2
self:PrependVOffsetT(vBytes)
local objectStart = self.bytes.size - objectOffset
self.bytes:Set(SOffsetT:Pack(self:Offset() - objectOffset),objectStart)
table.insert(self.vtables, self:Offset())
else
local objectStart = self.bytes.size - objectOffset
self.head = objectStart
self.bytes:Set(SOffsetT:Pack(exisitingVTable - objectOffset),self.head)
end
self.currentVTable = nil
return objectOffset
end
function mt:EndObject()
assert(self.nested)
self.nested = false
return self:WriteVtable()
end
local function growByteBuffer(self, desiredSize)
local s = self.bytes.size
assert(s < MAX_BUFFER_SIZE, "Flat Buffers cannot grow buffer beyond 2 gigabytes")
local newsize = s
repeat
newsize = math.min(newsize * 2, MAX_BUFFER_SIZE)
if newsize == 0 then newsize = 1 end
until newsize > desiredSize
self.bytes:Grow(newsize)
end
function mt:Head()
return self.head
end
function mt:Offset()
return self.bytes.size - self.head
end
function mt:Pad(n)
if n > 0 then
-- pads are 8-bit, so skip the bytewidth lookup
local h = self.head - n -- UInt8
self.head = h
self.bytes:Pad(n, h)
end
end
function mt:Prep(size, additionalBytes)
if size > self.minalign then
self.minalign = size
end
local h = self.head
local k = self.bytes.size - h + additionalBytes
local alignsize = getAlignSize(k, size)
local desiredSize = alignsize + size + additionalBytes
while self.head < desiredSize do
local oldBufSize = self.bytes.size
growByteBuffer(self, desiredSize)
local updatedHead = self.head + self.bytes.size - oldBufSize
self.head = updatedHead
end
self:Pad(alignsize)
end
function mt:PrependSOffsetTRelative(off)
self:Prep(4, 0)
assert(off <= self:Offset(), "Offset arithmetic error")
local off2 = self:Offset() - off + 4
self:Place(off2, SOffsetT)
end
function mt:PrependUOffsetTRelative(off)
self:Prep(4, 0)
local soffset = self:Offset()
if off <= soffset then
local off2 = soffset - off + 4
self:Place(off2, UOffsetT)
else
error("Offset arithmetic error")
end
end
function mt:StartVector(elemSize, numElements, alignment)
assert(not self.nested)
self.nested = true
local elementSize = elemSize * numElements
self:Prep(4, elementSize) -- Uint32 length
self:Prep(alignment, elementSize)
return self:Offset()
end
function mt:EndVector(vectorNumElements)
assert(self.nested)
self.nested = false
self:Place(vectorNumElements, UOffsetT)
return self:Offset()
end
function mt:CreateString(s)
assert(not self.nested)
self.nested = true
assert(type(s) == "string")
self:Prep(4, #s + 1)
self:Place(0, Uint8)
local l = #s
self.head = self.head - l
self.bytes:Set(s, self.head, self.head + l)
return self:EndVector(l)
end
function mt:CreateByteVector(x)
assert(not self.nested)
self.nested = true
local l = #x
self:Prep(4, l)
self.head = self.head - l
self.bytes:Set(x, self.head, self.head + l)
return self:EndVector(l)
end
function mt:Slot(slotnum)
assert(self.nested)
-- n.b. slot number is 0-based
self.currentVTable[slotnum + 1] = self:Offset()
end
local function finish(self, rootTable, sizePrefix)
UOffsetT:EnforceNumber(rootTable)
self:Prep(self.minalign, sizePrefix and 8 or 4)
self:PrependUOffsetTRelative(rootTable)
if sizePrefix then
local size = self.bytes.size - self.head
Int32:EnforceNumber(size)
self:PrependInt32(size)
end
self.finished = true
return self.head
end
function mt:Finish(rootTable)
return finish(self, rootTable, false)
end
function mt:FinishSizePrefixed(rootTable)
return finish(self, rootTable, true)
end
function mt:Prepend(flags, off)
self:Prep(flags.bytewidth, 0)
self:Place(off, flags)
end
function mt:PrependSlot(flags, o, x, d)
flags:EnforceNumbers(x,d)
-- flags:EnforceNumber(x)
-- flags:EnforceNumber(d)
if x ~= d then
self:Prepend(flags, x)
self:Slot(o)
end
end
function mt:PrependBoolSlot(...) self:PrependSlot(Bool, ...) end
function mt:PrependByteSlot(...) self:PrependSlot(Uint8, ...) end
function mt:PrependUint8Slot(...) self:PrependSlot(Uint8, ...) end
function mt:PrependUint16Slot(...) self:PrependSlot(Uint16, ...) end
function mt:PrependUint32Slot(...) self:PrependSlot(Uint32, ...) end
function mt:PrependUint64Slot(...) self:PrependSlot(Uint64, ...) end
function mt:PrependInt8Slot(...) self:PrependSlot(Int8, ...) end
function mt:PrependInt16Slot(...) self:PrependSlot(Int16, ...) end
function mt:PrependInt32Slot(...) self:PrependSlot(Int32, ...) end
function mt:PrependInt64Slot(...) self:PrependSlot(Int64, ...) end
function mt:PrependFloat32Slot(...) self:PrependSlot(Float32, ...) end
function mt:PrependFloat64Slot(...) self:PrependSlot(Float64, ...) end
function mt:PrependUOffsetTRelativeSlot(o,x,d)
if x~=d then
self:PrependUOffsetTRelative(x)
self:Slot(o)
end
end
function mt:PrependStructSlot(v,x,d)
UOffsetT:EnforceNumber(d)
if x~=d then
UOffsetT:EnforceNumber(x)
assert(x == self:Offset(), "Tried to write a Struct at an Offset that is different from the current Offset of the Builder.")
self:Slot(v)
end
end
function mt:PrependBool(x) self:Prepend(Bool, x) end
function mt:PrependByte(x) self:Prepend(Uint8, x) end
function mt:PrependUint8(x) self:Prepend(Uint8, x) end
function mt:PrependUint16(x) self:Prepend(Uint16, x) end
function mt:PrependUint32(x) self:Prepend(Uint32, x) end
function mt:PrependUint64(x) self:Prepend(Uint64, x) end
function mt:PrependInt8(x) self:Prepend(Int8, x) end
function mt:PrependInt16(x) self:Prepend(Int16, x) end
function mt:PrependInt32(x) self:Prepend(Int32, x) end
function mt:PrependInt64(x) self:Prepend(Int64, x) end
function mt:PrependFloat32(x) self:Prepend(Float32, x) end
function mt:PrependFloat64(x) self:Prepend(Float64, x) end
function mt:PrependVOffsetT(x) self:Prepend(VOffsetT, x) end
function mt:Place(x, flags)
local d = flags:EnforceNumberAndPack(x)
local h = self.head - flags.bytewidth
self.head = h
self.bytes:Set(d, h)
end
return m
|