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
View as plain text