blob: 25e0032a1dfb361cf6e8f19ade3e689e3ce34821 [file] [log] [blame]
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001local N = require("flatbuffers.numTypes")
2local ba = require("flatbuffers.binaryarray")
3local compat = require("flatbuffers.compat")
James Kuszmaul8e62b022022-03-22 09:33:25 -07004local string_unpack = compat.string_unpack
Austin Schuhe89fa2d2019-08-14 20:24:23 -07005
6local m = {}
7
8local mt = {}
9
10-- get locals for faster access
11local VOffsetT = N.VOffsetT
12local UOffsetT = N.UOffsetT
13local SOffsetT = N.SOffsetT
14local Bool = N.Bool
15local Uint8 = N.Uint8
16local Uint16 = N.Uint16
17local Uint32 = N.Uint32
18local Uint64 = N.Uint64
19local Int8 = N.Int8
20local Int16 = N.Int16
21local Int32 = N.Int32
22local Int64 = N.Int64
23local Float32 = N.Float32
24local Float64 = N.Float64
25
26local MAX_BUFFER_SIZE = 0x80000000 -- 2 GB
27local VtableMetadataFields = 2
28
29local getAlignSize = compat.GetAlignSize
30
31local function vtableEqual(a, objectStart, b)
32 UOffsetT:EnforceNumber(objectStart)
Austin Schuh272c6132020-11-14 16:37:52 -080033 if (#a * 2) ~= #b then
Austin Schuhe89fa2d2019-08-14 20:24:23 -070034 return false
35 end
36
37 for i, elem in ipairs(a) do
James Kuszmaul8e62b022022-03-22 09:33:25 -070038 local x = string_unpack(VOffsetT.packFmt, b, 1 + (i - 1) * 2)
Austin Schuhe89fa2d2019-08-14 20:24:23 -070039 if x ~= 0 or elem ~= 0 then
40 local y = objectStart - elem
41 if x ~= y then
42 return false
43 end
44 end
45 end
46 return true
47end
48
49function m.New(initialSize)
50 assert(0 <= initialSize and initialSize < MAX_BUFFER_SIZE)
51 local o =
52 {
53 finished = false,
54 bytes = ba.New(initialSize),
55 nested = false,
56 head = initialSize,
57 minalign = 1,
58 vtables = {}
59 }
60 setmetatable(o, {__index = mt})
61 return o
62end
63
Austin Schuh272c6132020-11-14 16:37:52 -080064-- Clears the builder and resets the state. It does not actually clear the backing binary array, it just reuses it as
65-- needed. This is a performant way to use the builder for multiple constructions without the overhead of multiple
66-- builder allocations.
67function mt:Clear()
68 self.finished = false
69 self.nested = false
70 self.minalign = 1
71 self.currentVTable = nil
72 self.objectEnd = nil
James Kuszmaul8e62b022022-03-22 09:33:25 -070073 self.head = self.bytes.size -- place the head at the end of the binary array
Austin Schuh272c6132020-11-14 16:37:52 -080074
75 -- clear vtables instead of making a new table
76 local vtable = self.vtables
77 local vtableCount = #vtable
78 for i=1,vtableCount do vtable[i] = nil end
79end
80
Austin Schuhe89fa2d2019-08-14 20:24:23 -070081function mt:Output(full)
82 assert(self.finished, "Builder Not Finished")
83 if full then
84 return self.bytes:Slice()
85 else
86 return self.bytes:Slice(self.head)
87 end
88end
89
90function mt:StartObject(numFields)
91 assert(not self.nested)
92
93 local vtable = {}
94
95 for _=1,numFields do
96 table.insert(vtable, 0)
97 end
98
99 self.currentVTable = vtable
100 self.objectEnd = self:Offset()
101 self.nested = true
102end
103
104function mt:WriteVtable()
105 self:PrependSOffsetTRelative(0)
106 local objectOffset = self:Offset()
107
108 local exisitingVTable
109 local i = #self.vtables
110 while i >= 1 do
111 if self.vtables[i] == 0 then
112 table.remove(self.vtables,i)
113 end
114 i = i - 1
115 end
116
117 i = #self.vtables
118 while i >= 1 do
119
120 local vt2Offset = self.vtables[i]
James Kuszmaul8e62b022022-03-22 09:33:25 -0700121 local vt2Start = self.bytes.size - vt2Offset
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700122 local vt2lenstr = self.bytes:Slice(vt2Start, vt2Start+1)
James Kuszmaul8e62b022022-03-22 09:33:25 -0700123 local vt2Len = string_unpack(VOffsetT.packFmt, vt2lenstr, 1)
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700124
Austin Schuh272c6132020-11-14 16:37:52 -0800125 local metadata = VtableMetadataFields * 2
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700126 local vt2End = vt2Start + vt2Len
127 local vt2 = self.bytes:Slice(vt2Start+metadata,vt2End)
128
129 if vtableEqual(self.currentVTable, objectOffset, vt2) then
130 exisitingVTable = vt2Offset
131 break
132 end
133
134 i = i - 1
135 end
136
137 if not exisitingVTable then
138 i = #self.currentVTable
139 while i >= 1 do
140 local off = 0
141 local a = self.currentVTable[i]
142 if a and a ~= 0 then
143 off = objectOffset - a
144 end
145 self:PrependVOffsetT(off)
146
147 i = i - 1
148 end
149
150 local objectSize = objectOffset - self.objectEnd
151 self:PrependVOffsetT(objectSize)
152
153 local vBytes = #self.currentVTable + VtableMetadataFields
Austin Schuh272c6132020-11-14 16:37:52 -0800154 vBytes = vBytes * 2
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700155 self:PrependVOffsetT(vBytes)
156
James Kuszmaul8e62b022022-03-22 09:33:25 -0700157 local objectStart = self.bytes.size - objectOffset
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700158 self.bytes:Set(SOffsetT:Pack(self:Offset() - objectOffset),objectStart)
159
160 table.insert(self.vtables, self:Offset())
161 else
James Kuszmaul8e62b022022-03-22 09:33:25 -0700162 local objectStart = self.bytes.size - objectOffset
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700163 self.head = objectStart
164 self.bytes:Set(SOffsetT:Pack(exisitingVTable - objectOffset),self.head)
165 end
166
167 self.currentVTable = nil
168 return objectOffset
169end
170
171function mt:EndObject()
172 assert(self.nested)
173 self.nested = false
174 return self:WriteVtable()
175end
176
177local function growByteBuffer(self, desiredSize)
James Kuszmaul8e62b022022-03-22 09:33:25 -0700178 local s = self.bytes.size
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700179 assert(s < MAX_BUFFER_SIZE, "Flat Buffers cannot grow buffer beyond 2 gigabytes")
180 local newsize = s
181 repeat
182 newsize = math.min(newsize * 2, MAX_BUFFER_SIZE)
183 if newsize == 0 then newsize = 1 end
184 until newsize > desiredSize
185
186 self.bytes:Grow(newsize)
187end
188
189function mt:Head()
190 return self.head
191end
192
193function mt:Offset()
James Kuszmaul8e62b022022-03-22 09:33:25 -0700194 return self.bytes.size - self.head
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700195end
196
197function mt:Pad(n)
198 if n > 0 then
199 -- pads are 8-bit, so skip the bytewidth lookup
200 local h = self.head - n -- UInt8
201 self.head = h
202 self.bytes:Pad(n, h)
203 end
204end
205
206function mt:Prep(size, additionalBytes)
207 if size > self.minalign then
208 self.minalign = size
209 end
210
211 local h = self.head
212
James Kuszmaul8e62b022022-03-22 09:33:25 -0700213 local k = self.bytes.size - h + additionalBytes
214 local alignsize = getAlignSize(k, size)
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700215
216 local desiredSize = alignsize + size + additionalBytes
217
218 while self.head < desiredSize do
James Kuszmaul8e62b022022-03-22 09:33:25 -0700219 local oldBufSize = self.bytes.size
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700220 growByteBuffer(self, desiredSize)
James Kuszmaul8e62b022022-03-22 09:33:25 -0700221 local updatedHead = self.head + self.bytes.size - oldBufSize
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700222 self.head = updatedHead
223 end
224
225 self:Pad(alignsize)
226end
227
228function mt:PrependSOffsetTRelative(off)
Austin Schuh272c6132020-11-14 16:37:52 -0800229 self:Prep(4, 0)
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700230 assert(off <= self:Offset(), "Offset arithmetic error")
Austin Schuh272c6132020-11-14 16:37:52 -0800231 local off2 = self:Offset() - off + 4
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700232 self:Place(off2, SOffsetT)
233end
234
235function mt:PrependUOffsetTRelative(off)
Austin Schuh272c6132020-11-14 16:37:52 -0800236 self:Prep(4, 0)
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700237 local soffset = self:Offset()
238 if off <= soffset then
Austin Schuh272c6132020-11-14 16:37:52 -0800239 local off2 = soffset - off + 4
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700240 self:Place(off2, UOffsetT)
241 else
242 error("Offset arithmetic error")
243 end
244end
245
246function mt:StartVector(elemSize, numElements, alignment)
247 assert(not self.nested)
248 self.nested = true
Austin Schuh272c6132020-11-14 16:37:52 -0800249 local elementSize = elemSize * numElements
250 self:Prep(4, elementSize) -- Uint32 length
251 self:Prep(alignment, elementSize)
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700252 return self:Offset()
253end
254
255function mt:EndVector(vectorNumElements)
256 assert(self.nested)
257 self.nested = false
258 self:Place(vectorNumElements, UOffsetT)
259 return self:Offset()
260end
261
262function mt:CreateString(s)
263 assert(not self.nested)
264 self.nested = true
265
266 assert(type(s) == "string")
267
Austin Schuh272c6132020-11-14 16:37:52 -0800268 self:Prep(4, #s + 1)
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700269 self:Place(0, Uint8)
270
271 local l = #s
272 self.head = self.head - l
273
274 self.bytes:Set(s, self.head, self.head + l)
275
Austin Schuh272c6132020-11-14 16:37:52 -0800276 return self:EndVector(l)
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700277end
278
279function mt:CreateByteVector(x)
280 assert(not self.nested)
281 self.nested = true
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700282
283 local l = #x
Austin Schuh272c6132020-11-14 16:37:52 -0800284 self:Prep(4, l)
285
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700286 self.head = self.head - l
287
288 self.bytes:Set(x, self.head, self.head + l)
289
Austin Schuh272c6132020-11-14 16:37:52 -0800290 return self:EndVector(l)
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700291end
292
293function mt:Slot(slotnum)
294 assert(self.nested)
295 -- n.b. slot number is 0-based
296 self.currentVTable[slotnum + 1] = self:Offset()
297end
298
299local function finish(self, rootTable, sizePrefix)
300 UOffsetT:EnforceNumber(rootTable)
Austin Schuh272c6132020-11-14 16:37:52 -0800301 self:Prep(self.minalign, sizePrefix and 8 or 4)
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700302 self:PrependUOffsetTRelative(rootTable)
303 if sizePrefix then
James Kuszmaul8e62b022022-03-22 09:33:25 -0700304 local size = self.bytes.size - self.head
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700305 Int32:EnforceNumber(size)
306 self:PrependInt32(size)
307 end
308 self.finished = true
309 return self.head
310end
311
312function mt:Finish(rootTable)
313 return finish(self, rootTable, false)
314end
315
316function mt:FinishSizePrefixed(rootTable)
317 return finish(self, rootTable, true)
318end
319
320function mt:Prepend(flags, off)
321 self:Prep(flags.bytewidth, 0)
322 self:Place(off, flags)
323end
324
325function mt:PrependSlot(flags, o, x, d)
Austin Schuh272c6132020-11-14 16:37:52 -0800326 flags:EnforceNumbers(x,d)
327-- flags:EnforceNumber(x)
328-- flags:EnforceNumber(d)
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700329 if x ~= d then
330 self:Prepend(flags, x)
331 self:Slot(o)
332 end
333end
334
335function mt:PrependBoolSlot(...) self:PrependSlot(Bool, ...) end
336function mt:PrependByteSlot(...) self:PrependSlot(Uint8, ...) end
337function mt:PrependUint8Slot(...) self:PrependSlot(Uint8, ...) end
338function mt:PrependUint16Slot(...) self:PrependSlot(Uint16, ...) end
339function mt:PrependUint32Slot(...) self:PrependSlot(Uint32, ...) end
340function mt:PrependUint64Slot(...) self:PrependSlot(Uint64, ...) end
341function mt:PrependInt8Slot(...) self:PrependSlot(Int8, ...) end
342function mt:PrependInt16Slot(...) self:PrependSlot(Int16, ...) end
343function mt:PrependInt32Slot(...) self:PrependSlot(Int32, ...) end
344function mt:PrependInt64Slot(...) self:PrependSlot(Int64, ...) end
345function mt:PrependFloat32Slot(...) self:PrependSlot(Float32, ...) end
346function mt:PrependFloat64Slot(...) self:PrependSlot(Float64, ...) end
347
348function mt:PrependUOffsetTRelativeSlot(o,x,d)
349 if x~=d then
350 self:PrependUOffsetTRelative(x)
351 self:Slot(o)
352 end
353end
354
355function mt:PrependStructSlot(v,x,d)
356 UOffsetT:EnforceNumber(d)
357 if x~=d then
358 UOffsetT:EnforceNumber(x)
359 assert(x == self:Offset(), "Tried to write a Struct at an Offset that is different from the current Offset of the Builder.")
360 self:Slot(v)
361 end
362end
363
364function mt:PrependBool(x) self:Prepend(Bool, x) end
365function mt:PrependByte(x) self:Prepend(Uint8, x) end
366function mt:PrependUint8(x) self:Prepend(Uint8, x) end
367function mt:PrependUint16(x) self:Prepend(Uint16, x) end
368function mt:PrependUint32(x) self:Prepend(Uint32, x) end
369function mt:PrependUint64(x) self:Prepend(Uint64, x) end
370function mt:PrependInt8(x) self:Prepend(Int8, x) end
371function mt:PrependInt16(x) self:Prepend(Int16, x) end
372function mt:PrependInt32(x) self:Prepend(Int32, x) end
373function mt:PrependInt64(x) self:Prepend(Int64, x) end
374function mt:PrependFloat32(x) self:Prepend(Float32, x) end
375function mt:PrependFloat64(x) self:Prepend(Float64, x) end
376function mt:PrependVOffsetT(x) self:Prepend(VOffsetT, x) end
377
378function mt:Place(x, flags)
379 local d = flags:EnforceNumberAndPack(x)
380 local h = self.head - flags.bytewidth
381 self.head = h
382 self.bytes:Set(d, h)
383end
384
385return m