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.. 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..