Home | History | Annotate | Download | only in lobster
      1 // Copyright 2018 Google Inc. All rights reserved.
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 include "std.lobster"
     16 
     17 namespace flatbuffers
     18 
     19 struct handle:
     20     buf_:string
     21     pos_:int
     22 
     23 enum + sz_8 = 1,
     24        sz_16 = 2,
     25        sz_32 = 4,
     26        sz_64 = 8,
     27        sz_voffset = 2,
     28        sz_uoffset = 4,
     29        sz_soffset = 4,
     30        sz_metadata_fields = 2
     31 
     32 struct builder:
     33     buf:string = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
     34     current_vtable:[int] = []
     35     head:int = 0
     36     minalign:int = 1
     37     object_end:int = 0
     38     vtables:[int] = []
     39     nested:int = false
     40     finished:int = false
     41 
     42     // Optionally call this right after creating the builder for a larger initial buffer.
     43     def Initial(initial_size:int):
     44         buf = "\x00".repeat_string(initial_size)
     45 
     46     def Start():
     47         // Get the start of useful data in the underlying byte buffer.
     48         return buf.length - head
     49 
     50     def Offset():
     51         // Offset relative to the end of the buffer.
     52         return head
     53 
     54     // Returns a copy of the part of the buffer containing only the finished FlatBuffer
     55     def SizedCopy():
     56         assert finished
     57         return buf.substring(Start(), -1)
     58 
     59     def StartNesting():
     60         assert not nested
     61         nested = true
     62 
     63     def EndNesting():
     64         assert nested
     65         nested = false
     66 
     67     def StartObject(numfields):
     68         StartNesting()
     69         current_vtable = map(numfields): 0
     70         object_end = head
     71         minalign = 1
     72 
     73     def EndObject():
     74         EndNesting()
     75         // Prepend a zero scalar to the object. Later in this function we'll
     76         // write an offset here that points to the object's vtable:
     77         PrependInt32(0)
     78         object_offset := head
     79         // Write out new vtable speculatively.
     80         vtable_size := (current_vtable.length + sz_metadata_fields) * sz_voffset
     81         while current_vtable.length:
     82             o := current_vtable.pop()
     83             PrependVOffsetT(if o: object_offset - o else: 0)
     84         // The two metadata fields are written last.
     85         // First, store the object bytesize:
     86         PrependVOffsetT(object_offset - object_end)
     87         // Second, store the vtable bytesize:
     88         PrependVOffsetT(vtable_size)
     89         // Search backwards through existing vtables, because similar vtables
     90         // are likely to have been recently appended. See
     91         // BenchmarkVtableDeduplication for a case in which this heuristic
     92         // saves about 30% of the time used in writing objects with duplicate
     93         // tables.
     94         existing_vtable := do():
     95             reverse(vtables) vt2_offset:
     96                 // Find the other vtable:
     97                 vt2_start := buf.length - vt2_offset
     98                 vt2_len := buf.read_int16_le(vt2_start)
     99                 // Compare the other vtable to the one under consideration.
    100                 // If they are equal, return the offset:
    101                 if vtable_size == vt2_len and
    102                     not compare_substring(buf, Start(), buf, vt2_start, vtable_size):
    103                         return vt2_offset from do
    104             0
    105         if existing_vtable:
    106             // Found a duplicate vtable, remove the one we wrote.
    107             head = object_offset
    108             // Write the offset to the found vtable in the
    109             // already-allocated offset at the beginning of this object:
    110             buf.write_int32_le(Start(), existing_vtable - object_offset)
    111         else:
    112             // Did not find a vtable, so keep the one we wrote.
    113             // Next, write the offset to the new vtable in the
    114             // already-allocated offset at the beginning of this object:
    115             buf.write_int32_le(buf.length - object_offset, head - object_offset)
    116             // Finally, store this vtable in memory for future
    117             // deduplication:
    118             vtables.push(head)
    119         return object_offset
    120 
    121     def Pad(n):
    122         for(n): buf, head = buf.write_int8_le_back(head, 0)
    123 
    124     def Prep(size, additional_bytes):
    125         // Track the biggest thing we've ever aligned to.
    126         if size > minalign:
    127             minalign = size
    128         // Find the amount of alignment needed such that `size` is properly
    129         // aligned after `additionalBytes`:
    130         align_size := ((~(head + additional_bytes)) + 1) & (size - 1)
    131         Pad(align_size)
    132 
    133     def PrependUOffsetTRelative(off):
    134         // Prepends an unsigned offset into vector data, relative to where it will be written.
    135         Prep(sz_uoffset, 0)
    136         assert off <= head
    137         PlaceUOffsetT(head - off + sz_uoffset)
    138 
    139     def StartVector(elem_size, num_elems, alignment):
    140         // Initializes bookkeeping for writing a new vector.
    141         StartNesting()
    142         Prep(sz_32, elem_size * num_elems)
    143         Prep(alignment, elem_size * num_elems)  // In case alignment > int.
    144         return head
    145 
    146     def EndVector(vector_num_elems):
    147         EndNesting()
    148         // we already made space for this, so write without PrependUint32
    149         PlaceUOffsetT(vector_num_elems)
    150         return head
    151 
    152     def CreateString(s:string):
    153         // writes a null-terminated byte string.
    154         StartNesting()
    155         Prep(sz_32, s.length + 1)
    156         buf, head = buf.write_substring_back(head, s, true)
    157         return EndVector(s.length)
    158 
    159     def CreateByteVector(s:string):
    160         // writes a non-null-terminated byte string.
    161         StartNesting()
    162         Prep(sz_32, s.length)
    163         buf, head = buf.write_substring_back(head, s, false)
    164         return EndVector(s.length)
    165 
    166     def Slot(slotnum):
    167         assert nested
    168         while current_vtable.length <= slotnum: current_vtable.push(0)
    169         current_vtable[slotnum] = head
    170 
    171     def __Finish(root_table:int, size_prefix:int):
    172         // Finish finalizes a buffer, pointing to the given root_table
    173         assert not finished
    174         assert not nested
    175         prep_size := sz_32
    176         if size_prefix:
    177             prep_size += sz_32
    178         Prep(minalign, prep_size)
    179         PrependUOffsetTRelative(root_table)
    180         if size_prefix:
    181             PrependInt32(head)
    182         finished = true
    183         return Start()
    184 
    185     def Finish(root_table:int):
    186         return __Finish(root_table, false)
    187 
    188     def FinishSizePrefixed(root_table:int):
    189         return __Finish(root_table, true)
    190 
    191     def PrependBool(x):
    192         buf, head = buf.write_int8_le_back(head, x)
    193 
    194     def PrependByte(x):
    195         buf, head = buf.write_int8_le_back(head, x)
    196 
    197     def PrependUint8(x):
    198         buf, head = buf.write_int8_le_back(head, x)
    199 
    200     def PrependUint16(x):
    201         Prep(sz_16, 0)
    202         buf, head = buf.write_int16_le_back(head, x)
    203 
    204     def PrependUint32(x):
    205         Prep(sz_32, 0)
    206         buf, head = buf.write_int32_le_back(head, x)
    207 
    208     def PrependUint64(x):
    209         Prep(sz_64, 0)
    210         buf, head = buf.write_int64_le_back(head, x)
    211 
    212     def PrependInt8(x):
    213         buf, head = buf.write_int8_le_back(head, x)
    214 
    215     def PrependInt16(x):
    216         Prep(sz_16, 0)
    217         buf, head = buf.write_int16_le_back(head, x)
    218 
    219     def PrependInt32(x):
    220         Prep(sz_32, 0)
    221         buf, head = buf.write_int32_le_back(head, x)
    222 
    223     def PrependInt64(x):
    224         Prep(sz_64, 0)
    225         buf, head = buf.write_int64_le_back(head, x)
    226 
    227     def PrependFloat32(x):
    228         Prep(sz_32, 0)
    229         buf, head = buf.write_float32_le_back(head, x)
    230 
    231     def PrependFloat64(x):
    232         Prep(sz_64, 0)
    233         buf, head = buf.write_float64_le_back(head, x)
    234 
    235     def PrependVOffsetT(x):
    236         Prep(sz_voffset, 0)
    237         buf, head = buf.write_int16_le_back(head, x)
    238 
    239     def PlaceVOffsetT(x):
    240         buf, head = buf.write_int16_le_back(head, x)
    241 
    242     def PlaceSOffsetT(x):
    243         buf, head = buf.write_int32_le_back(head, x)
    244 
    245     def PlaceUOffsetT(x):
    246         buf, head = buf.write_int32_le_back(head, x)
    247 
    248     def PrependSlot(o:int, x, d, f):
    249         if x != d:
    250             f(x)
    251             Slot(o)
    252 
    253     def PrependBoolSlot(o, x, d): PrependSlot(o, x, d): PrependBool(_)
    254     def PrependByteSlot(o, x, d): PrependSlot(o, x, d): PrependByte(_)
    255     def PrependUint8Slot(o, x, d): PrependSlot(o, x, d): PrependUint8(_)
    256     def PrependUint16Slot(o, x, d): PrependSlot(o, x, d): PrependUint16(_)
    257     def PrependUint32Slot(o, x, d): PrependSlot(o, x, d): PrependUint32(_)
    258     def PrependUint64Slot(o, x, d): PrependSlot(o, x, d): PrependUint64(_)
    259     def PrependInt8Slot(o, x, d): PrependSlot(o, x, d): PrependInt8(_)
    260     def PrependInt16Slot(o, x, d): PrependSlot(o, x, d): PrependInt16(_)
    261     def PrependInt32Slot(o, x, d): PrependSlot(o, x, d): PrependInt32(_)
    262     def PrependInt64Slot(o, x, d): PrependSlot(o, x, d): PrependInt64(_)
    263     def PrependFloat32Slot(o, x, d): PrependSlot(o, x, d): PrependFloat32(_)
    264     def PrependFloat64Slot(o, x, d): PrependSlot(o, x, d): PrependFloat64(_)
    265 
    266     def PrependUOffsetTRelativeSlot(o, x, d):
    267         if x != d:
    268             PrependUOffsetTRelative(x)
    269             Slot(o)
    270 
    271     def PrependStructSlot(v, x, d):
    272         if x != d:
    273             // Structs are always stored inline, so need to be created right
    274             // where they are used. You'll get this error if you created it
    275             //elsewhere.
    276             assert x == head
    277             Slot(v)
    278 
    279