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