blob: 0f1c15dd2196de1e1c453f6350d8833f15585e85 [file] [log] [blame]
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001// 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 PrependBoolSlot(o, x, d): PrependSlot(o, x, d): PrependBool(_)
261 def PrependByteSlot(o, x, d): PrependSlot(o, x, d): PrependByte(_)
262 def PrependUint8Slot(o, x, d): PrependSlot(o, x, d): PrependUint8(_)
263 def PrependUint16Slot(o, x, d): PrependSlot(o, x, d): PrependUint16(_)
264 def PrependUint32Slot(o, x, d): PrependSlot(o, x, d): PrependUint32(_)
265 def PrependUint64Slot(o, x, d): PrependSlot(o, x, d): PrependUint64(_)
266 def PrependInt8Slot(o, x, d): PrependSlot(o, x, d): PrependInt8(_)
267 def PrependInt16Slot(o, x, d): PrependSlot(o, x, d): PrependInt16(_)
268 def PrependInt32Slot(o, x, d): PrependSlot(o, x, d): PrependInt32(_)
269 def PrependInt64Slot(o, x, d): PrependSlot(o, x, d): PrependInt64(_)
270 def PrependFloat32Slot(o, x, d): PrependSlot(o, x, d): PrependFloat32(_)
271 def PrependFloat64Slot(o, x, d): PrependSlot(o, x, d): PrependFloat64(_)
272
273 def PrependUOffsetTRelativeSlot(o:int, x:offset):
274 if x.o:
275 PrependUOffsetTRelative(x)
276 Slot(o)
277
278 def PrependStructSlot(v:int, x:offset):
279 if x.o:
280 // Structs are always stored inline, so need to be created right
281 // where they are used. You'll get this error if you created it
282 // elsewhere.
283 assert x.o == head
284 Slot(v)