xref: /aosp_15_r20/external/flatbuffers/lobster/flatbuffers.lobster (revision 890232f25432b36107d06881e0a25aaa6b473652)
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, file_identifier:string?):
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 file_identifier:
184            prep_size += sz_32
185        if size_prefix:
186            prep_size += sz_32
187        Prep(minalign, prep_size)
188        if file_identifier:
189            assert file_identifier.length == 4
190            buf, head = buf.write_substring_back(head, file_identifier, false)
191        PrependUOffsetTRelative(root_table)
192        if size_prefix:
193            PrependInt32(head)
194        finished = true
195        return Start()
196
197    def Finish(root_table:offset, file_identifier:string? = nil):
198        return __Finish(root_table, false, file_identifier)
199
200    def FinishSizePrefixed(root_table:offset, file_identifier:string? = nil):
201        return __Finish(root_table, true, file_identifier)
202
203    def PrependBool(x):
204        buf, head = buf.write_int8_le_back(head, x)
205
206    def PrependByte(x):
207        buf, head = buf.write_int8_le_back(head, x)
208
209    def PrependUint8(x):
210        buf, head = buf.write_int8_le_back(head, x)
211
212    def PrependUint16(x):
213        Prep(sz_16, 0)
214        buf, head = buf.write_int16_le_back(head, x)
215
216    def PrependUint32(x):
217        Prep(sz_32, 0)
218        buf, head = buf.write_int32_le_back(head, x)
219
220    def PrependUint64(x):
221        Prep(sz_64, 0)
222        buf, head = buf.write_int64_le_back(head, x)
223
224    def PrependInt8(x):
225        buf, head = buf.write_int8_le_back(head, x)
226
227    def PrependInt16(x):
228        Prep(sz_16, 0)
229        buf, head = buf.write_int16_le_back(head, x)
230
231    def PrependInt32(x):
232        Prep(sz_32, 0)
233        buf, head = buf.write_int32_le_back(head, x)
234
235    def PrependInt64(x):
236        Prep(sz_64, 0)
237        buf, head = buf.write_int64_le_back(head, x)
238
239    def PrependFloat32(x):
240        Prep(sz_32, 0)
241        buf, head = buf.write_float32_le_back(head, x)
242
243    def PrependFloat64(x):
244        Prep(sz_64, 0)
245        buf, head = buf.write_float64_le_back(head, x)
246
247    def PrependVOffsetT(x):
248        Prep(sz_voffset, 0)
249        buf, head = buf.write_int16_le_back(head, x)
250
251    def PlaceVOffsetT(x):
252        buf, head = buf.write_int16_le_back(head, x)
253
254    def PlaceSOffsetT(x):
255        buf, head = buf.write_int32_le_back(head, x)
256
257    def PlaceUOffsetT(x):
258        buf, head = buf.write_int32_le_back(head, x)
259
260    def PrependSlot(o:int, x, d, f):
261        if x != d:
262            f(x)
263            Slot(o)
264
265    def PrependSlot(o:int, x, f):
266        f(x)
267        Slot(o)
268
269    def PrependBoolSlot(o, x, d): PrependSlot(o, x, d): PrependBool(_)
270    def PrependByteSlot(o, x, d): PrependSlot(o, x, d): PrependByte(_)
271    def PrependUint8Slot(o, x, d): PrependSlot(o, x, d): PrependUint8(_)
272    def PrependUint16Slot(o, x, d): PrependSlot(o, x, d): PrependUint16(_)
273    def PrependUint32Slot(o, x, d): PrependSlot(o, x, d): PrependUint32(_)
274    def PrependUint64Slot(o, x, d): PrependSlot(o, x, d): PrependUint64(_)
275    def PrependInt8Slot(o, x, d): PrependSlot(o, x, d): PrependInt8(_)
276    def PrependInt16Slot(o, x, d): PrependSlot(o, x, d): PrependInt16(_)
277    def PrependInt32Slot(o, x, d): PrependSlot(o, x, d): PrependInt32(_)
278    def PrependInt64Slot(o, x, d): PrependSlot(o, x, d): PrependInt64(_)
279    def PrependFloat32Slot(o, x, d): PrependSlot(o, x, d): PrependFloat32(_)
280    def PrependFloat64Slot(o, x, d): PrependSlot(o, x, d): PrependFloat64(_)
281
282    def PrependBoolSlot(o, x): PrependSlot(o, x): PrependBool(_)
283    def PrependByteSlot(o, x): PrependSlot(o, x): PrependByte(_)
284    def PrependUint8Slot(o, x): PrependSlot(o, x): PrependUint8(_)
285    def PrependUint16Slot(o, x): PrependSlot(o, x): PrependUint16(_)
286    def PrependUint32Slot(o, x): PrependSlot(o, x): PrependUint32(_)
287    def PrependUint64Slot(o, x): PrependSlot(o, x): PrependUint64(_)
288    def PrependInt8Slot(o, x): PrependSlot(o, x): PrependInt8(_)
289    def PrependInt16Slot(o, x): PrependSlot(o, x): PrependInt16(_)
290    def PrependInt32Slot(o, x): PrependSlot(o, x): PrependInt32(_)
291    def PrependInt64Slot(o, x): PrependSlot(o, x): PrependInt64(_)
292    def PrependFloat32Slot(o, x): PrependSlot(o, x): PrependFloat32(_)
293    def PrependFloat64Slot(o, x): PrependSlot(o, x): PrependFloat64(_)
294
295    def PrependUOffsetTRelativeSlot(o:int, x:offset):
296        if x.o:
297            PrependUOffsetTRelative(x)
298            Slot(o)
299
300    def PrependStructSlot(v:int, x:offset):
301        if x.o:
302            // Structs are always stored inline, so need to be created right
303            // where they are used. You'll get this error if you created it
304            // elsewhere.
305            assert x.o == head
306            Slot(v)
307
308def has_identifier(buf:string, file_identifier:string):
309    assert file_identifier.length == 4
310    return buf.length >= 8 and buf.substring(4, 4) == file_identifier
311
312
313