mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-08 06:05:17 +00:00
* Bfbs Nim Generator * Remove commented out tests * add missing line to idl.h * Commit python reflection changes * Commit python reflection changes and move tests * Remove default string addition * Move tests to python file * Fix element size check when element is table * remove whitespace changes * add element_type docs and commit further to namer and remove kkeep * Bfbs Nim Generator * Remove commented out tests * add missing line to idl.h * Commit python reflection changes * Commit python reflection changes and move tests * Remove default string addition * Move tests to python file * Fix element size check when element is table * remove whitespace changes * add element_type docs and commit further to namer and remove kkeep * remove unused variables * added tests to ci * added tests to ci * fixes * Added reflection type Field, Variable to namer * Moved reflection namer impl to bfbsnamer * Remove whitespace at end of line * Added nim to generated code * Revert whitespace removal Co-authored-by: Derek Bailey <derekbailey@google.com>
263 lines
7.4 KiB
Nim
263 lines
7.4 KiB
Nim
import math
|
|
import table
|
|
|
|
|
|
const MAX_BUFFER_SIZE* = 2^31
|
|
|
|
|
|
type Builder* = ref object of RootObj
|
|
bytes*: seq[byte]
|
|
minalign*: int
|
|
current_vtable*: seq[uoffset]
|
|
objectEnd*: uoffset
|
|
vtables*: seq[uoffset] #?
|
|
head*: uoffset
|
|
nested*: bool
|
|
finished*: bool
|
|
vectorNumElems*: uoffset
|
|
|
|
using this: var Builder
|
|
|
|
func newBuilder*(size: int): Builder =
|
|
result = new Builder
|
|
result.bytes.setLen(size)
|
|
result.minalign = 1
|
|
result.head = size.uoffset
|
|
result.nested = false
|
|
result.finished = false
|
|
result.vectorNumElems = 0
|
|
|
|
proc FinishedBytes*(this): seq[byte] =
|
|
if not this.finished:
|
|
quit("Builder not finished, Incorrect use of FinishedBytes(): must call 'Finish' first.")
|
|
result = this.bytes[this.head..^1]
|
|
|
|
proc Output*(this): seq[byte] =
|
|
if not this.finished:
|
|
quit("Builder not finished, Incorrect use of Output(): must call 'Finish' first.")
|
|
|
|
result = this.bytes[this.head..^1]
|
|
|
|
func Offset*(this): uoffset =
|
|
result = this.bytes.len.uoffset - this.head
|
|
|
|
proc StartObject*(this; numfields: int) =
|
|
if this.nested:
|
|
quit("builder is nested")
|
|
|
|
this.current_vtable.setLen(numfields)
|
|
for i in this.current_vtable.mitems():
|
|
i = 0
|
|
this.objectEnd = this.Offset()
|
|
this.nested = true
|
|
|
|
proc GrowByteBuffer*(this) =
|
|
if this.bytes.len == MAX_BUFFER_SIZE:
|
|
quit("flatbuffers: cannot grow buffer beyond 2 gigabytes")
|
|
let oldLen = this.bytes.len
|
|
var newLen = min(this.bytes.len * 2, MAX_BUFFER_SIZE)
|
|
if newLen == 0:
|
|
newLen = 1
|
|
this.bytes.setLen(newLen)
|
|
var j = this.bytes.len - 1
|
|
while j >= 0:
|
|
if j >= newLen - oldLen:
|
|
this.bytes[j] = this.bytes[j - (newLen - oldLen)]
|
|
else:
|
|
this.bytes[j] = 0
|
|
dec(j)
|
|
|
|
proc Place*[T](this; x: T) =
|
|
this.head -= uoffset x.sizeof
|
|
WriteVal(this.bytes, this.head, x)
|
|
|
|
func Pad*(this; n: int) =
|
|
for i in 0..<n:
|
|
this.Place(0.byte)
|
|
|
|
proc Prep*(this; size: int; additionalBytes: int) =
|
|
if size > this.minalign:
|
|
this.minalign = size
|
|
var alignsize = (not (this.bytes.len - this.head.int + additionalBytes)) + 1
|
|
alignsize = alignsize and (size - 1)
|
|
|
|
while this.head.int < alignsize + size + additionalBytes:
|
|
let oldbufSize = this.bytes.len
|
|
this.GrowByteBuffer()
|
|
this.head = (this.head.int + this.bytes.len - oldbufSize).uoffset
|
|
this.Pad(alignsize)
|
|
|
|
proc PrependOffsetRelative*[T: Offsets](this; off: T) =
|
|
when T is voffset:
|
|
this.Prep(T.sizeof, 0)
|
|
if not off.uoffset <= this.Offset:
|
|
quit("flatbuffers: Offset arithmetic error.")
|
|
this.Place(off)
|
|
else:
|
|
this.Prep(T.sizeof, 0)
|
|
if not off.uoffset <= this.Offset:
|
|
quit("flatbuffers: Offset arithmetic error.")
|
|
let off2: T = this.Offset.T - off + sizeof(T).T
|
|
this.Place(off2)
|
|
|
|
|
|
proc Prepend*[T](this; x: T) =
|
|
this.Prep(x.sizeof, 0)
|
|
this.Place(x)
|
|
|
|
proc Slot*(this; slotnum: int) =
|
|
this.current_vtable[slotnum] = this.Offset
|
|
|
|
proc PrependSlot*[T](this; o: int; x, d: T) =
|
|
if x != d:
|
|
when T is uoffset or T is soffset or T is voffset:
|
|
this.PrependOffsetRelative(x)
|
|
else:
|
|
this.Prepend(x)
|
|
this.Slot(o)
|
|
|
|
proc AssertStuctInline(this; obj: uoffset) =
|
|
if obj != this.Offset:
|
|
quit("flatbuffers: Tried to write a Struct at an Offset that is different from the current Offset of the Builder.")
|
|
|
|
proc PrependStructSlot*(this; o: int; x: uoffset; d: uoffset) =
|
|
if x != d:
|
|
this.AssertStuctInline(x)
|
|
this.Slot(o)
|
|
|
|
proc Add*[T](this; n: T) =
|
|
this.Prep(T.sizeof, 0)
|
|
WriteVal(this.bytes, this.head, n)
|
|
|
|
proc VtableEqual*(a: seq[uoffset]; objectStart: uoffset; b: seq[byte]): bool =
|
|
if a.len * voffset.sizeof != b.len:
|
|
return false
|
|
|
|
var i = 0
|
|
while i < a.len:
|
|
var seq = b[i * voffset.sizeof..<(i + 1) * voffset.sizeof]
|
|
let x = GetVal[voffset](addr seq)
|
|
|
|
if x == 0 and a[i] == 0:
|
|
inc i
|
|
continue
|
|
|
|
let y = objectStart.soffset - a[i].soffset
|
|
if x.soffset != y:
|
|
return false
|
|
inc i
|
|
return true
|
|
|
|
proc WriteVtable*(this): uoffset =
|
|
this.PrependOffsetRelative(0.soffset)
|
|
|
|
let objectOffset = this.Offset
|
|
var existingVtable = uoffset 0
|
|
|
|
var i = this.current_vtable.len - 1
|
|
while i >= 0 and this.current_vtable[i] == 0: dec i
|
|
|
|
this.current_vtable = this.current_vtable[0..i]
|
|
for i in countdown(this.vtables.len - 1, 0):
|
|
let
|
|
vt2Offset: uoffset = this.vtables[i]
|
|
vt2Start: int = this.bytes.len - int vt2Offset
|
|
|
|
var seq = this.bytes[vt2Start..<this.bytes.len]
|
|
let
|
|
vt2Len = GetVal[voffset](addr seq)
|
|
metadata = 2 * voffset.sizeof # VtableMetadataFields * SizeVOffsetT
|
|
vt2End = vt2Start + vt2Len.int
|
|
vt2 = this.bytes[this.bytes.len - vt2Offset.int + metadata..<vt2End]
|
|
|
|
if VtableEqual(this.current_vtable, objectOffset, vt2):
|
|
existingVtable = vt2Offset
|
|
break
|
|
|
|
if existingVtable == 0:
|
|
for i in countdown(this.current_vtable.len - 1, 0):
|
|
var off: uoffset
|
|
if this.current_vtable[i] != 0:
|
|
off = objectOffset - this.current_vtable[i]
|
|
|
|
this.PrependOffsetRelative(off.voffset)
|
|
|
|
let objectSize = objectOffset - this.objectEnd
|
|
this.PrependOffsetRelative(objectSize.voffset)
|
|
|
|
let vBytes = (this.current_vtable.len + 2) * voffset.sizeof
|
|
this.PrependOffsetRelative(vBytes.voffset)
|
|
|
|
let objectStart: uoffset = (this.bytes.len.uoffset - objectOffset)
|
|
WriteVal(this.bytes, objectStart, (this.Offset - objectOffset).soffset)
|
|
this.vtables.add this.Offset
|
|
else:
|
|
let objectStart: uoffset = this.bytes.len.uoffset - objectOffset
|
|
this.head = objectStart
|
|
WriteVal(this.bytes, this.head,
|
|
(existingVtable.soffset - objectOffset.soffset))
|
|
|
|
this.current_vtable = @[]
|
|
result = objectOffset
|
|
|
|
proc EndObject*(this): uoffset =
|
|
if not this.nested:
|
|
quit("builder is not nested")
|
|
result = this.WriteVtable()
|
|
this.nested = false
|
|
|
|
proc End*(this: var Builder): uoffset =
|
|
result = this.EndObject()
|
|
|
|
proc StartVector*(this; elemSize: int; numElems: uoffset;
|
|
alignment: int) =
|
|
if this.nested:
|
|
quit("builder is nested")
|
|
this.nested = true
|
|
this.vectorNumElems = numElems
|
|
this.Prep(sizeof(uint32), elemSize * numElems.int)
|
|
this.Prep(alignment, elemSize * numElems.int)
|
|
|
|
proc EndVector*(this): uoffset =
|
|
if not this.nested:
|
|
quit("builder is not nested")
|
|
this.nested = false
|
|
this.Place(this.vectorNumElems)
|
|
this.vectorNumElems = 0
|
|
result = this.Offset
|
|
|
|
proc getChars*(str: seq[byte]): string =
|
|
var bytes = str
|
|
result = GetVal[string](addr bytes)
|
|
|
|
proc getBytes*(str: string | cstring): seq[byte] =
|
|
for chr in str:
|
|
result.add byte chr
|
|
result.add byte 0
|
|
|
|
proc Create*[T](this; s: T): uoffset = # Both CreateString and CreateByteVector functionality
|
|
if this.nested:
|
|
quit("builder is nested")
|
|
this.nested = true
|
|
when T is cstring or T is string:
|
|
let x = s.getBytes()
|
|
let l = x.len.uoffset
|
|
this.vectorNumElems = l-1
|
|
else:
|
|
let x = s
|
|
let l = x.len.uoffset
|
|
this.vectorNumElems = l
|
|
this.Prep(uoffset.sizeof, l.int * byte.sizeof)
|
|
this.head -= l
|
|
this.bytes[this.head..<this.head+l] = x
|
|
result = this.EndVector()
|
|
|
|
proc Finish*(this; rootTable: uoffset) =
|
|
if this.nested:
|
|
quit("builder is nested")
|
|
this.nested = true
|
|
|
|
this.Prep(this.minalign, uoffset.sizeof)
|
|
this.PrependOffsetRelative(rootTable)
|
|
this.finished = true
|