• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
15include "std.lobster"
16
17namespace flatbuffers
18
19struct handle:
20    buf_:string
21    pos_:int
22
23enum + 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
32struct 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