summaryrefslogtreecommitdiff
path: root/lobster/flatbuffers.lobster
blob: 0f1c15dd2196de1e1c453f6350d8833f15585e85 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
// 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

enum sizeof:
    sz_8 = 1
    sz_16 = 2
    sz_32 = 4
    sz_64 = 8
    sz_voffset = 2
    sz_uoffset = 4
    sz_soffset = 4
    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):
        // Finish finalizes a buffer, pointing to the given root_table
        assert not finished
        assert not nested
        var prep_size = sz_32
        if size_prefix:
            prep_size += sz_32
        Prep(minalign, prep_size)
        PrependUOffsetTRelative(root_table)
        if size_prefix:
            PrependInt32(head)
        finished = true
        return Start()

    def Finish(root_table:offset):
        return __Finish(root_table, false)

    def FinishSizePrefixed(root_table:offset):
        return __Finish(root_table, true)

    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 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 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)