mirror of
https://github.com/google/flatbuffers.git
synced 2026-11-07 20:59:10 +00:00
This is because newer versions of the Lobster compiler reject enums with the same values, and older compilers are fine with the let versions also.
312 lines
11 KiB
Plaintext
312 lines
11 KiB
Plaintext
// Copyright 2018 Google Inc. All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
import std
|
|
|
|
namespace flatbuffers
|
|
|
|
class handle:
|
|
buf_:string
|
|
pos_:int
|
|
|
|
// More strongly typed than a naked int, at no cost.
|
|
struct offset:
|
|
o:int
|
|
|
|
let sz_8 = 1
|
|
let sz_16 = 2
|
|
let sz_32 = 4
|
|
let sz_64 = 8
|
|
let sz_voffset = 2
|
|
let sz_uoffset = 4
|
|
let sz_soffset = 4
|
|
let sz_metadata_fields = 2
|
|
|
|
class builder:
|
|
buf = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
|
current_vtable:[int] = []
|
|
head = 0
|
|
minalign = 1
|
|
object_end = 0
|
|
vtables:[int] = []
|
|
nested = false
|
|
finished = false
|
|
|
|
// Optionally call this right after creating the builder for a larger initial buffer.
|
|
def Initial(initial_size:int):
|
|
buf = "\x00".repeat_string(initial_size)
|
|
|
|
def Start():
|
|
// Get the start of useful data in the underlying byte buffer.
|
|
return buf.length - head
|
|
|
|
def Offset():
|
|
// Offset relative to the end of the buffer.
|
|
return offset { head }
|
|
|
|
// Returns a copy of the part of the buffer containing only the finished FlatBuffer
|
|
def SizedCopy():
|
|
assert finished
|
|
return buf.substring(Start(), -1)
|
|
|
|
def StartNesting():
|
|
assert not nested
|
|
nested = true
|
|
|
|
def EndNesting():
|
|
assert nested
|
|
nested = false
|
|
|
|
def StartObject(numfields):
|
|
StartNesting()
|
|
current_vtable = map(numfields): 0
|
|
object_end = head
|
|
minalign = 1
|
|
|
|
def EndObject():
|
|
EndNesting()
|
|
// Prepend a zero scalar to the object. Later in this function we'll
|
|
// write an offset here that points to the object's vtable:
|
|
PrependInt32(0)
|
|
let object_offset = head
|
|
// Write out new vtable speculatively.
|
|
let vtable_size = (current_vtable.length + sz_metadata_fields) * sz_voffset
|
|
while current_vtable.length:
|
|
let o = current_vtable.pop()
|
|
PrependVOffsetT(if o: object_offset - o else: 0)
|
|
// The two metadata fields are written last.
|
|
// First, store the object bytesize:
|
|
PrependVOffsetT(object_offset - object_end)
|
|
// Second, store the vtable bytesize:
|
|
PrependVOffsetT(vtable_size)
|
|
// Search backwards through existing vtables, because similar vtables
|
|
// are likely to have been recently appended. See
|
|
// BenchmarkVtableDeduplication for a case in which this heuristic
|
|
// saves about 30% of the time used in writing objects with duplicate
|
|
// tables.
|
|
def find_existing_table():
|
|
reverse(vtables) vt2_offset:
|
|
// Find the other vtable:
|
|
let vt2_start = buf.length - vt2_offset
|
|
let vt2_len = buf.read_int16_le(vt2_start)
|
|
// Compare the other vtable to the one under consideration.
|
|
// If they are equal, return the offset:
|
|
if vtable_size == vt2_len and
|
|
not compare_substring(buf, Start(), buf, vt2_start, vtable_size):
|
|
return vt2_offset
|
|
return 0
|
|
let existing_vtable = find_existing_table()
|
|
if existing_vtable:
|
|
// Found a duplicate vtable, remove the one we wrote.
|
|
head = object_offset
|
|
// Write the offset to the found vtable in the
|
|
// already-allocated offset at the beginning of this object:
|
|
buf.write_int32_le(Start(), existing_vtable - object_offset)
|
|
else:
|
|
// Did not find a vtable, so keep the one we wrote.
|
|
// Next, write the offset to the new vtable in the
|
|
// already-allocated offset at the beginning of this object:
|
|
buf.write_int32_le(buf.length - object_offset, head - object_offset)
|
|
// Finally, store this vtable in memory for future
|
|
// deduplication:
|
|
vtables.push(head)
|
|
return offset { object_offset }
|
|
|
|
def Pad(n):
|
|
for(n):
|
|
buf, head = buf.write_int8_le_back(head, 0)
|
|
|
|
def Prep(size, additional_bytes):
|
|
// Track the biggest thing we've ever aligned to.
|
|
if size > minalign:
|
|
minalign = size
|
|
// Find the amount of alignment needed such that `size` is properly
|
|
// aligned after `additionalBytes`:
|
|
let align_size = ((~(head + additional_bytes)) + 1) & (size - 1)
|
|
Pad(align_size)
|
|
|
|
def PrependUOffsetTRelative(off:offset):
|
|
// Prepends an unsigned offset into vector data, relative to where it will be written.
|
|
Prep(sz_uoffset, 0)
|
|
assert off.o <= head
|
|
PlaceUOffsetT(head - off.o + sz_uoffset)
|
|
|
|
def StartVector(elem_size, num_elems, alignment):
|
|
// Initializes bookkeeping for writing a new vector.
|
|
StartNesting()
|
|
Prep(sz_32, elem_size * num_elems)
|
|
Prep(alignment, elem_size * num_elems) // In case alignment > int.
|
|
return Offset()
|
|
|
|
def EndVector(vector_num_elems):
|
|
EndNesting()
|
|
// we already made space for this, so write without PrependUint32
|
|
PlaceUOffsetT(vector_num_elems)
|
|
return Offset()
|
|
|
|
def CreateString(s:string):
|
|
// writes a null-terminated byte string.
|
|
StartNesting()
|
|
Prep(sz_32, s.length + 1)
|
|
buf, head = buf.write_substring_back(head, s, true)
|
|
return EndVector(s.length)
|
|
|
|
def CreateByteVector(s:string):
|
|
// writes a non-null-terminated byte string.
|
|
StartNesting()
|
|
Prep(sz_32, s.length)
|
|
buf, head = buf.write_substring_back(head, s, false)
|
|
return EndVector(s.length)
|
|
|
|
def Slot(slotnum):
|
|
assert nested
|
|
while current_vtable.length <= slotnum: current_vtable.push(0)
|
|
current_vtable[slotnum] = head
|
|
|
|
def __Finish(root_table:offset, size_prefix:int, file_identifier:string?):
|
|
// Finish finalizes a buffer, pointing to the given root_table
|
|
assert not finished
|
|
assert not nested
|
|
var prep_size = sz_32
|
|
if file_identifier:
|
|
prep_size += sz_32
|
|
if size_prefix:
|
|
prep_size += sz_32
|
|
Prep(minalign, prep_size)
|
|
if file_identifier:
|
|
assert file_identifier.length == 4
|
|
buf, head = buf.write_substring_back(head, file_identifier, false)
|
|
PrependUOffsetTRelative(root_table)
|
|
if size_prefix:
|
|
PrependInt32(head)
|
|
finished = true
|
|
return Start()
|
|
|
|
def Finish(root_table:offset, file_identifier:string? = nil):
|
|
return __Finish(root_table, false, file_identifier)
|
|
|
|
def FinishSizePrefixed(root_table:offset, file_identifier:string? = nil):
|
|
return __Finish(root_table, true, file_identifier)
|
|
|
|
def PrependBool(x):
|
|
buf, head = buf.write_int8_le_back(head, x)
|
|
|
|
def PrependByte(x):
|
|
buf, head = buf.write_int8_le_back(head, x)
|
|
|
|
def PrependUint8(x):
|
|
buf, head = buf.write_int8_le_back(head, x)
|
|
|
|
def PrependUint16(x):
|
|
Prep(sz_16, 0)
|
|
buf, head = buf.write_int16_le_back(head, x)
|
|
|
|
def PrependUint32(x):
|
|
Prep(sz_32, 0)
|
|
buf, head = buf.write_int32_le_back(head, x)
|
|
|
|
def PrependUint64(x):
|
|
Prep(sz_64, 0)
|
|
buf, head = buf.write_int64_le_back(head, x)
|
|
|
|
def PrependInt8(x):
|
|
buf, head = buf.write_int8_le_back(head, x)
|
|
|
|
def PrependInt16(x):
|
|
Prep(sz_16, 0)
|
|
buf, head = buf.write_int16_le_back(head, x)
|
|
|
|
def PrependInt32(x):
|
|
Prep(sz_32, 0)
|
|
buf, head = buf.write_int32_le_back(head, x)
|
|
|
|
def PrependInt64(x):
|
|
Prep(sz_64, 0)
|
|
buf, head = buf.write_int64_le_back(head, x)
|
|
|
|
def PrependFloat32(x):
|
|
Prep(sz_32, 0)
|
|
buf, head = buf.write_float32_le_back(head, x)
|
|
|
|
def PrependFloat64(x):
|
|
Prep(sz_64, 0)
|
|
buf, head = buf.write_float64_le_back(head, x)
|
|
|
|
def PrependVOffsetT(x):
|
|
Prep(sz_voffset, 0)
|
|
buf, head = buf.write_int16_le_back(head, x)
|
|
|
|
def PlaceVOffsetT(x):
|
|
buf, head = buf.write_int16_le_back(head, x)
|
|
|
|
def PlaceSOffsetT(x):
|
|
buf, head = buf.write_int32_le_back(head, x)
|
|
|
|
def PlaceUOffsetT(x):
|
|
buf, head = buf.write_int32_le_back(head, x)
|
|
|
|
def PrependSlot(o:int, x, d, f):
|
|
if x != d:
|
|
f(x)
|
|
Slot(o)
|
|
|
|
def PrependSlot(o:int, x, f):
|
|
f(x)
|
|
Slot(o)
|
|
|
|
def PrependBoolSlot(o, x, d): PrependSlot(o, x, d): PrependBool(_)
|
|
def PrependByteSlot(o, x, d): PrependSlot(o, x, d): PrependByte(_)
|
|
def PrependUint8Slot(o, x, d): PrependSlot(o, x, d): PrependUint8(_)
|
|
def PrependUint16Slot(o, x, d): PrependSlot(o, x, d): PrependUint16(_)
|
|
def PrependUint32Slot(o, x, d): PrependSlot(o, x, d): PrependUint32(_)
|
|
def PrependUint64Slot(o, x, d): PrependSlot(o, x, d): PrependUint64(_)
|
|
def PrependInt8Slot(o, x, d): PrependSlot(o, x, d): PrependInt8(_)
|
|
def PrependInt16Slot(o, x, d): PrependSlot(o, x, d): PrependInt16(_)
|
|
def PrependInt32Slot(o, x, d): PrependSlot(o, x, d): PrependInt32(_)
|
|
def PrependInt64Slot(o, x, d): PrependSlot(o, x, d): PrependInt64(_)
|
|
def PrependFloat32Slot(o, x, d): PrependSlot(o, x, d): PrependFloat32(_)
|
|
def PrependFloat64Slot(o, x, d): PrependSlot(o, x, d): PrependFloat64(_)
|
|
|
|
def PrependBoolSlot(o, x): PrependSlot(o, x): PrependBool(_)
|
|
def PrependByteSlot(o, x): PrependSlot(o, x): PrependByte(_)
|
|
def PrependUint8Slot(o, x): PrependSlot(o, x): PrependUint8(_)
|
|
def PrependUint16Slot(o, x): PrependSlot(o, x): PrependUint16(_)
|
|
def PrependUint32Slot(o, x): PrependSlot(o, x): PrependUint32(_)
|
|
def PrependUint64Slot(o, x): PrependSlot(o, x): PrependUint64(_)
|
|
def PrependInt8Slot(o, x): PrependSlot(o, x): PrependInt8(_)
|
|
def PrependInt16Slot(o, x): PrependSlot(o, x): PrependInt16(_)
|
|
def PrependInt32Slot(o, x): PrependSlot(o, x): PrependInt32(_)
|
|
def PrependInt64Slot(o, x): PrependSlot(o, x): PrependInt64(_)
|
|
def PrependFloat32Slot(o, x): PrependSlot(o, x): PrependFloat32(_)
|
|
def PrependFloat64Slot(o, x): PrependSlot(o, x): PrependFloat64(_)
|
|
|
|
def PrependUOffsetTRelativeSlot(o:int, x:offset):
|
|
if x.o:
|
|
PrependUOffsetTRelative(x)
|
|
Slot(o)
|
|
|
|
def PrependStructSlot(v:int, x:offset):
|
|
if x.o:
|
|
// Structs are always stored inline, so need to be created right
|
|
// where they are used. You'll get this error if you created it
|
|
// elsewhere.
|
|
assert x.o == head
|
|
Slot(v)
|
|
|
|
def has_identifier(buf:string, file_identifier:string):
|
|
assert file_identifier.length == 4
|
|
return buf.length >= 8 and buf.substring(4, 4) == file_identifier
|
|
|
|
|