blob: f3b901b1e4853796fa676e48b046d6d816d15f23 [file] [log] [blame]
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001# Copyright 2014 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
15from . import number_types as N
16from .number_types import (UOffsetTFlags, SOffsetTFlags, VOffsetTFlags)
17
18from . import encode
19from . import packer
20
21from . import compat
22from .compat import range_func
23from .compat import memoryview_type
24from .compat import import_numpy, NumpyRequiredForThisFeature
25
Austin Schuh2dd86a92022-09-14 21:19:23 -070026import warnings
27
Austin Schuhe89fa2d2019-08-14 20:24:23 -070028np = import_numpy()
29## @file
30## @addtogroup flatbuffers_python_api
31## @{
32
33## @cond FLATBUFFERS_INTERNAL
34class OffsetArithmeticError(RuntimeError):
35 """
36 Error caused by an Offset arithmetic error. Probably caused by bad
37 writing of fields. This is considered an unreachable situation in
38 normal circumstances.
39 """
40 pass
41
42
43class IsNotNestedError(RuntimeError):
44 """
45 Error caused by using a Builder to write Object data when not inside
46 an Object.
47 """
48 pass
49
50
51class IsNestedError(RuntimeError):
52 """
53 Error caused by using a Builder to begin an Object when an Object is
54 already being built.
55 """
56 pass
57
58
59class StructIsNotInlineError(RuntimeError):
60 """
61 Error caused by using a Builder to write a Struct at a location that
62 is not the current Offset.
63 """
64 pass
65
66
67class BuilderSizeError(RuntimeError):
68 """
69 Error caused by causing a Builder to exceed the hardcoded limit of 2
70 gigabytes.
71 """
72 pass
73
74class BuilderNotFinishedError(RuntimeError):
75 """
76 Error caused by not calling `Finish` before calling `Output`.
77 """
78 pass
79
Austin Schuh2dd86a92022-09-14 21:19:23 -070080class EndVectorLengthMismatched(RuntimeError):
81 """
82 The number of elements passed to EndVector does not match the number
83 specified in StartVector.
84 """
85 pass
86
Austin Schuhe89fa2d2019-08-14 20:24:23 -070087
88# VtableMetadataFields is the count of metadata fields in each vtable.
89VtableMetadataFields = 2
90## @endcond
91
92class Builder(object):
93 """ A Builder is used to construct one or more FlatBuffers.
94
95 Typically, Builder objects will be used from code generated by the `flatc`
96 compiler.
97
98 A Builder constructs byte buffers in a last-first manner for simplicity and
99 performance during reading.
100
101 Internally, a Builder is a state machine for creating FlatBuffer objects.
102
103 It holds the following internal state:
104 - Bytes: an array of bytes.
105 - current_vtable: a list of integers.
106 - vtables: a hash of vtable entries.
107
108 Attributes:
109 Bytes: The internal `bytearray` for the Builder.
110 finished: A boolean determining if the Builder has been finalized.
111 """
112
113 ## @cond FLATBUFFERS_INTENRAL
114 __slots__ = ("Bytes", "current_vtable", "head", "minalign", "objectEnd",
James Kuszmaul3b15b0c2022-11-08 14:03:16 -0800115 "vtables", "nested", "forceDefaults", "finished", "vectorNumElems",
116 "sharedStrings")
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700117
118 """Maximum buffer size constant, in bytes.
119
120 Builder will never allow it's buffer grow over this size.
121 Currently equals 2Gb.
122 """
123 MAX_BUFFER_SIZE = 2**31
124 ## @endcond
125
James Kuszmaul8e62b022022-03-22 09:33:25 -0700126 def __init__(self, initialSize=1024):
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700127 """Initializes a Builder of size `initial_size`.
128
129 The internal buffer is grown as needed.
130 """
131
132 if not (0 <= initialSize <= Builder.MAX_BUFFER_SIZE):
133 msg = "flatbuffers: Cannot create Builder larger than 2 gigabytes."
134 raise BuilderSizeError(msg)
135
136 self.Bytes = bytearray(initialSize)
137 ## @cond FLATBUFFERS_INTERNAL
138 self.current_vtable = None
139 self.head = UOffsetTFlags.py_type(initialSize)
140 self.minalign = 1
141 self.objectEnd = None
142 self.vtables = {}
143 self.nested = False
Austin Schuh272c6132020-11-14 16:37:52 -0800144 self.forceDefaults = False
James Kuszmaul3b15b0c2022-11-08 14:03:16 -0800145 self.sharedStrings = {}
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700146 ## @endcond
147 self.finished = False
148
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700149 def Output(self):
150 """Return the portion of the buffer that has been used for writing data.
151
152 This is the typical way to access the FlatBuffer data inside the
153 builder. If you try to access `Builder.Bytes` directly, you would need
154 to manually index it with `Head()`, since the buffer is constructed
155 backwards.
156
157 It raises BuilderNotFinishedError if the buffer has not been finished
158 with `Finish`.
159 """
160
161 if not self.finished:
162 raise BuilderNotFinishedError()
163
164 return self.Bytes[self.Head():]
165
166 ## @cond FLATBUFFERS_INTERNAL
167 def StartObject(self, numfields):
168 """StartObject initializes bookkeeping for writing a new object."""
169
170 self.assertNotNested()
171
172 # use 32-bit offsets so that arithmetic doesn't overflow.
173 self.current_vtable = [0 for _ in range_func(numfields)]
174 self.objectEnd = self.Offset()
175 self.nested = True
176
177 def WriteVtable(self):
178 """
179 WriteVtable serializes the vtable for the current object, if needed.
180
181 Before writing out the vtable, this checks pre-existing vtables for
182 equality to this one. If an equal vtable is found, point the object to
183 the existing vtable and return.
184
185 Because vtable values are sensitive to alignment of object data, not
186 all logically-equal vtables will be deduplicated.
187
188 A vtable has the following format:
189 <VOffsetT: size of the vtable in bytes, including this value>
190 <VOffsetT: size of the object in bytes, including the vtable offset>
191 <VOffsetT: offset for a field> * N, where N is the number of fields
192 in the schema for this type. Includes deprecated fields.
193 Thus, a vtable is made of 2 + N elements, each VOffsetT bytes wide.
194
195 An object has the following format:
196 <SOffsetT: offset to this object's vtable (may be negative)>
197 <byte: data>+
198 """
199
200 # Prepend a zero scalar to the object. Later in this function we'll
201 # write an offset here that points to the object's vtable:
202 self.PrependSOffsetTRelative(0)
203
204 objectOffset = self.Offset()
205
206 vtKey = []
207 trim = True
208 for elem in reversed(self.current_vtable):
209 if elem == 0:
210 if trim:
211 continue
212 else:
213 elem = objectOffset - elem
214 trim = False
215
216 vtKey.append(elem)
217
218 vtKey = tuple(vtKey)
219 vt2Offset = self.vtables.get(vtKey)
220 if vt2Offset is None:
221 # Did not find a vtable, so write this one to the buffer.
222
223 # Write out the current vtable in reverse , because
224 # serialization occurs in last-first order:
225 i = len(self.current_vtable) - 1
226 trailing = 0
227 trim = True
228 while i >= 0:
229 off = 0
230 elem = self.current_vtable[i]
231 i -= 1
232
233 if elem == 0:
234 if trim:
235 trailing += 1
236 continue
237 else:
238 # Forward reference to field;
239 # use 32bit number to ensure no overflow:
240 off = objectOffset - elem
241 trim = False
242
243 self.PrependVOffsetT(off)
244
245 # The two metadata fields are written last.
246
247 # First, store the object bytesize:
248 objectSize = UOffsetTFlags.py_type(objectOffset - self.objectEnd)
249 self.PrependVOffsetT(VOffsetTFlags.py_type(objectSize))
250
251 # Second, store the vtable bytesize:
252 vBytes = len(self.current_vtable) - trailing + VtableMetadataFields
253 vBytes *= N.VOffsetTFlags.bytewidth
254 self.PrependVOffsetT(VOffsetTFlags.py_type(vBytes))
255
256 # Next, write the offset to the new vtable in the
257 # already-allocated SOffsetT at the beginning of this object:
258 objectStart = SOffsetTFlags.py_type(len(self.Bytes) - objectOffset)
259 encode.Write(packer.soffset, self.Bytes, objectStart,
260 SOffsetTFlags.py_type(self.Offset() - objectOffset))
261
262 # Finally, store this vtable in memory for future
263 # deduplication:
264 self.vtables[vtKey] = self.Offset()
265 else:
266 # Found a duplicate vtable.
267 objectStart = SOffsetTFlags.py_type(len(self.Bytes) - objectOffset)
268 self.head = UOffsetTFlags.py_type(objectStart)
269
270 # Write the offset to the found vtable in the
271 # already-allocated SOffsetT at the beginning of this object:
272 encode.Write(packer.soffset, self.Bytes, self.Head(),
273 SOffsetTFlags.py_type(vt2Offset - objectOffset))
274
275 self.current_vtable = None
276 return objectOffset
277
278 def EndObject(self):
279 """EndObject writes data necessary to finish object construction."""
280 self.assertNested()
281 self.nested = False
282 return self.WriteVtable()
283
284 def growByteBuffer(self):
285 """Doubles the size of the byteslice, and copies the old data towards
286 the end of the new buffer (since we build the buffer backwards)."""
287 if len(self.Bytes) == Builder.MAX_BUFFER_SIZE:
288 msg = "flatbuffers: cannot grow buffer beyond 2 gigabytes"
289 raise BuilderSizeError(msg)
290
291 newSize = min(len(self.Bytes) * 2, Builder.MAX_BUFFER_SIZE)
292 if newSize == 0:
293 newSize = 1
294 bytes2 = bytearray(newSize)
295 bytes2[newSize-len(self.Bytes):] = self.Bytes
296 self.Bytes = bytes2
297 ## @endcond
298
299 def Head(self):
300 """Get the start of useful data in the underlying byte buffer.
301
302 Note: unlike other functions, this value is interpreted as from the
303 left.
304 """
305 ## @cond FLATBUFFERS_INTERNAL
306 return self.head
307 ## @endcond
308
309 ## @cond FLATBUFFERS_INTERNAL
310 def Offset(self):
311 """Offset relative to the end of the buffer."""
312 return UOffsetTFlags.py_type(len(self.Bytes) - self.Head())
313
314 def Pad(self, n):
315 """Pad places zeros at the current offset."""
316 for i in range_func(n):
317 self.Place(0, N.Uint8Flags)
318
319 def Prep(self, size, additionalBytes):
320 """
321 Prep prepares to write an element of `size` after `additional_bytes`
322 have been written, e.g. if you write a string, you need to align
323 such the int length field is aligned to SizeInt32, and the string
324 data follows it directly.
325 If all you need to do is align, `additionalBytes` will be 0.
326 """
327
328 # Track the biggest thing we've ever aligned to.
329 if size > self.minalign:
330 self.minalign = size
331
332 # Find the amount of alignment needed such that `size` is properly
333 # aligned after `additionalBytes`:
334 alignSize = (~(len(self.Bytes) - self.Head() + additionalBytes)) + 1
335 alignSize &= (size - 1)
336
337 # Reallocate the buffer if needed:
338 while self.Head() < alignSize+size+additionalBytes:
339 oldBufSize = len(self.Bytes)
340 self.growByteBuffer()
341 updated_head = self.head + len(self.Bytes) - oldBufSize
342 self.head = UOffsetTFlags.py_type(updated_head)
343 self.Pad(alignSize)
344
345 def PrependSOffsetTRelative(self, off):
346 """
347 PrependSOffsetTRelative prepends an SOffsetT, relative to where it
348 will be written.
349 """
350
351 # Ensure alignment is already done:
352 self.Prep(N.SOffsetTFlags.bytewidth, 0)
353 if not (off <= self.Offset()):
354 msg = "flatbuffers: Offset arithmetic error."
355 raise OffsetArithmeticError(msg)
356 off2 = self.Offset() - off + N.SOffsetTFlags.bytewidth
357 self.PlaceSOffsetT(off2)
358 ## @endcond
359
360 def PrependUOffsetTRelative(self, off):
361 """Prepends an unsigned offset into vector data, relative to where it
362 will be written.
363 """
364
365 # Ensure alignment is already done:
366 self.Prep(N.UOffsetTFlags.bytewidth, 0)
367 if not (off <= self.Offset()):
368 msg = "flatbuffers: Offset arithmetic error."
369 raise OffsetArithmeticError(msg)
370 off2 = self.Offset() - off + N.UOffsetTFlags.bytewidth
371 self.PlaceUOffsetT(off2)
372
373 ## @cond FLATBUFFERS_INTERNAL
374 def StartVector(self, elemSize, numElems, alignment):
375 """
376 StartVector initializes bookkeeping for writing a new vector.
377
378 A vector has the following format:
379 - <UOffsetT: number of elements in this vector>
380 - <T: data>+, where T is the type of elements of this vector.
381 """
382
383 self.assertNotNested()
384 self.nested = True
James Kuszmaul8e62b022022-03-22 09:33:25 -0700385 self.vectorNumElems = numElems
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700386 self.Prep(N.Uint32Flags.bytewidth, elemSize*numElems)
387 self.Prep(alignment, elemSize*numElems) # In case alignment > int.
388 return self.Offset()
389 ## @endcond
390
Austin Schuh2dd86a92022-09-14 21:19:23 -0700391 def EndVector(self, numElems = None):
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700392 """EndVector writes data necessary to finish vector construction."""
393
394 self.assertNested()
395 ## @cond FLATBUFFERS_INTERNAL
396 self.nested = False
397 ## @endcond
Austin Schuh2dd86a92022-09-14 21:19:23 -0700398
399 if numElems:
400 warnings.warn("numElems is deprecated.",
401 DeprecationWarning, stacklevel=2)
402 if numElems != self.vectorNumElems:
403 raise EndVectorLengthMismatched();
404
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700405 # we already made space for this, so write without PrependUint32
James Kuszmaul8e62b022022-03-22 09:33:25 -0700406 self.PlaceUOffsetT(self.vectorNumElems)
407 self.vectorNumElems = None
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700408 return self.Offset()
409
James Kuszmaul3b15b0c2022-11-08 14:03:16 -0800410 def CreateSharedString(self, s, encoding='utf-8', errors='strict'):
411 """
412 CreateSharedString checks if the string is already written to the buffer
413 before calling CreateString.
414 """
415
416 if s in self.sharedStrings:
417 return self.sharedStrings[s]
418
419 off = self.CreateString(s, encoding, errors)
420 self.sharedStrings[s] = off
421
422 return off
423
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700424 def CreateString(self, s, encoding='utf-8', errors='strict'):
425 """CreateString writes a null-terminated byte string as a vector."""
426
427 self.assertNotNested()
428 ## @cond FLATBUFFERS_INTERNAL
429 self.nested = True
430 ## @endcond
431
432 if isinstance(s, compat.string_types):
433 x = s.encode(encoding, errors)
434 elif isinstance(s, compat.binary_types):
435 x = s
436 else:
437 raise TypeError("non-string passed to CreateString")
438
439 self.Prep(N.UOffsetTFlags.bytewidth, (len(x)+1)*N.Uint8Flags.bytewidth)
440 self.Place(0, N.Uint8Flags)
441
442 l = UOffsetTFlags.py_type(len(s))
443 ## @cond FLATBUFFERS_INTERNAL
444 self.head = UOffsetTFlags.py_type(self.Head() - l)
445 ## @endcond
446 self.Bytes[self.Head():self.Head()+l] = x
447
James Kuszmaul8e62b022022-03-22 09:33:25 -0700448 self.vectorNumElems = len(x)
449 return self.EndVector()
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700450
451 def CreateByteVector(self, x):
452 """CreateString writes a byte vector."""
453
454 self.assertNotNested()
455 ## @cond FLATBUFFERS_INTERNAL
456 self.nested = True
457 ## @endcond
458
459 if not isinstance(x, compat.binary_types):
460 raise TypeError("non-byte vector passed to CreateByteVector")
461
462 self.Prep(N.UOffsetTFlags.bytewidth, len(x)*N.Uint8Flags.bytewidth)
463
464 l = UOffsetTFlags.py_type(len(x))
465 ## @cond FLATBUFFERS_INTERNAL
466 self.head = UOffsetTFlags.py_type(self.Head() - l)
467 ## @endcond
468 self.Bytes[self.Head():self.Head()+l] = x
469
James Kuszmaul8e62b022022-03-22 09:33:25 -0700470 self.vectorNumElems = len(x)
471 return self.EndVector()
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700472
473 def CreateNumpyVector(self, x):
474 """CreateNumpyVector writes a numpy array into the buffer."""
475
476 if np is None:
477 # Numpy is required for this feature
478 raise NumpyRequiredForThisFeature("Numpy was not found.")
479
480 if not isinstance(x, np.ndarray):
481 raise TypeError("non-numpy-ndarray passed to CreateNumpyVector")
482
483 if x.dtype.kind not in ['b', 'i', 'u', 'f']:
484 raise TypeError("numpy-ndarray holds elements of unsupported datatype")
485
486 if x.ndim > 1:
487 raise TypeError("multidimensional-ndarray passed to CreateNumpyVector")
488
489 self.StartVector(x.itemsize, x.size, x.dtype.alignment)
490
491 # Ensure little endian byte ordering
492 if x.dtype.str[0] == "<":
493 x_lend = x
494 else:
495 x_lend = x.byteswap(inplace=False)
496
497 # Calculate total length
498 l = UOffsetTFlags.py_type(x_lend.itemsize * x_lend.size)
499 ## @cond FLATBUFFERS_INTERNAL
500 self.head = UOffsetTFlags.py_type(self.Head() - l)
501 ## @endcond
502
503 # tobytes ensures c_contiguous ordering
504 self.Bytes[self.Head():self.Head()+l] = x_lend.tobytes(order='C')
Austin Schuh272c6132020-11-14 16:37:52 -0800505
James Kuszmaul8e62b022022-03-22 09:33:25 -0700506 self.vectorNumElems = x.size
507 return self.EndVector()
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700508
509 ## @cond FLATBUFFERS_INTERNAL
510 def assertNested(self):
511 """
512 Check that we are in the process of building an object.
513 """
514
515 if not self.nested:
516 raise IsNotNestedError()
517
518 def assertNotNested(self):
519 """
520 Check that no other objects are being built while making this
521 object. If not, raise an exception.
522 """
523
524 if self.nested:
525 raise IsNestedError()
526
527 def assertStructIsInline(self, obj):
528 """
529 Structs are always stored inline, so need to be created right
530 where they are used. You'll get this error if you created it
531 elsewhere.
532 """
533
534 N.enforce_number(obj, N.UOffsetTFlags)
535 if obj != self.Offset():
536 msg = ("flatbuffers: Tried to write a Struct at an Offset that "
537 "is different from the current Offset of the Builder.")
538 raise StructIsNotInlineError(msg)
539
540 def Slot(self, slotnum):
541 """
542 Slot sets the vtable key `voffset` to the current location in the
543 buffer.
544
545 """
546 self.assertNested()
547 self.current_vtable[slotnum] = self.Offset()
548 ## @endcond
549
550 def __Finish(self, rootTable, sizePrefix, file_identifier=None):
551 """Finish finalizes a buffer, pointing to the given `rootTable`."""
552 N.enforce_number(rootTable, N.UOffsetTFlags)
553
Austin Schuh272c6132020-11-14 16:37:52 -0800554 prepSize = N.UOffsetTFlags.bytewidth
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700555 if file_identifier is not None:
Austin Schuh272c6132020-11-14 16:37:52 -0800556 prepSize += N.Int32Flags.bytewidth
557 if sizePrefix:
558 prepSize += N.Int32Flags.bytewidth
559 self.Prep(self.minalign, prepSize)
560
561 if file_identifier is not None:
562 self.Prep(N.UOffsetTFlags.bytewidth, encode.FILE_IDENTIFIER_LENGTH)
563
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700564 # Convert bytes object file_identifier to an array of 4 8-bit integers,
565 # and use big-endian to enforce size compliance.
566 # https://docs.python.org/2/library/struct.html#format-characters
567 file_identifier = N.struct.unpack(">BBBB", file_identifier)
568 for i in range(encode.FILE_IDENTIFIER_LENGTH-1, -1, -1):
569 # Place the bytes of the file_identifer in reverse order:
Austin Schuh272c6132020-11-14 16:37:52 -0800570 self.Place(file_identifier[i], N.Uint8Flags)
571
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700572 self.PrependUOffsetTRelative(rootTable)
573 if sizePrefix:
574 size = len(self.Bytes) - self.Head()
575 N.enforce_number(size, N.Int32Flags)
576 self.PrependInt32(size)
577 self.finished = True
578 return self.Head()
579
580 def Finish(self, rootTable, file_identifier=None):
581 """Finish finalizes a buffer, pointing to the given `rootTable`."""
582 return self.__Finish(rootTable, False, file_identifier=file_identifier)
583
584 def FinishSizePrefixed(self, rootTable, file_identifier=None):
585 """
586 Finish finalizes a buffer, pointing to the given `rootTable`,
587 with the size prefixed.
588 """
589 return self.__Finish(rootTable, True, file_identifier=file_identifier)
590
591 ## @cond FLATBUFFERS_INTERNAL
592 def Prepend(self, flags, off):
593 self.Prep(flags.bytewidth, 0)
594 self.Place(off, flags)
595
596 def PrependSlot(self, flags, o, x, d):
Austin Schuh2dd86a92022-09-14 21:19:23 -0700597 if x is not None:
598 N.enforce_number(x, flags)
599 if d is not None:
600 N.enforce_number(d, flags)
601 if x != d or (self.forceDefaults and d is not None):
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700602 self.Prepend(flags, x)
603 self.Slot(o)
604
605 def PrependBoolSlot(self, *args): self.PrependSlot(N.BoolFlags, *args)
606
607 def PrependByteSlot(self, *args): self.PrependSlot(N.Uint8Flags, *args)
608
609 def PrependUint8Slot(self, *args): self.PrependSlot(N.Uint8Flags, *args)
610
611 def PrependUint16Slot(self, *args): self.PrependSlot(N.Uint16Flags, *args)
612
613 def PrependUint32Slot(self, *args): self.PrependSlot(N.Uint32Flags, *args)
614
615 def PrependUint64Slot(self, *args): self.PrependSlot(N.Uint64Flags, *args)
616
617 def PrependInt8Slot(self, *args): self.PrependSlot(N.Int8Flags, *args)
618
619 def PrependInt16Slot(self, *args): self.PrependSlot(N.Int16Flags, *args)
620
621 def PrependInt32Slot(self, *args): self.PrependSlot(N.Int32Flags, *args)
622
623 def PrependInt64Slot(self, *args): self.PrependSlot(N.Int64Flags, *args)
624
625 def PrependFloat32Slot(self, *args): self.PrependSlot(N.Float32Flags,
626 *args)
627
628 def PrependFloat64Slot(self, *args): self.PrependSlot(N.Float64Flags,
629 *args)
630
631 def PrependUOffsetTRelativeSlot(self, o, x, d):
632 """
633 PrependUOffsetTRelativeSlot prepends an UOffsetT onto the object at
634 vtable slot `o`. If value `x` equals default `d`, then the slot will
635 be set to zero and no other data will be written.
636 """
637
Austin Schuh272c6132020-11-14 16:37:52 -0800638 if x != d or self.forceDefaults:
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700639 self.PrependUOffsetTRelative(x)
640 self.Slot(o)
641
642 def PrependStructSlot(self, v, x, d):
643 """
644 PrependStructSlot prepends a struct onto the object at vtable slot `o`.
645 Structs are stored inline, so nothing additional is being added.
646 In generated code, `d` is always 0.
647 """
648
649 N.enforce_number(d, N.UOffsetTFlags)
650 if x != d:
651 self.assertStructIsInline(x)
652 self.Slot(v)
653
654 ## @endcond
655
656 def PrependBool(self, x):
657 """Prepend a `bool` to the Builder buffer.
658
659 Note: aligns and checks for space.
660 """
661 self.Prepend(N.BoolFlags, x)
662
663 def PrependByte(self, x):
664 """Prepend a `byte` to the Builder buffer.
665
666 Note: aligns and checks for space.
667 """
668 self.Prepend(N.Uint8Flags, x)
669
670 def PrependUint8(self, x):
671 """Prepend an `uint8` to the Builder buffer.
672
673 Note: aligns and checks for space.
674 """
675 self.Prepend(N.Uint8Flags, x)
676
677 def PrependUint16(self, x):
678 """Prepend an `uint16` to the Builder buffer.
679
680 Note: aligns and checks for space.
681 """
682 self.Prepend(N.Uint16Flags, x)
683
684 def PrependUint32(self, x):
685 """Prepend an `uint32` to the Builder buffer.
686
687 Note: aligns and checks for space.
688 """
689 self.Prepend(N.Uint32Flags, x)
690
691 def PrependUint64(self, x):
692 """Prepend an `uint64` to the Builder buffer.
693
694 Note: aligns and checks for space.
695 """
696 self.Prepend(N.Uint64Flags, x)
697
698 def PrependInt8(self, x):
699 """Prepend an `int8` to the Builder buffer.
700
701 Note: aligns and checks for space.
702 """
703 self.Prepend(N.Int8Flags, x)
704
705 def PrependInt16(self, x):
706 """Prepend an `int16` to the Builder buffer.
707
708 Note: aligns and checks for space.
709 """
710 self.Prepend(N.Int16Flags, x)
711
712 def PrependInt32(self, x):
713 """Prepend an `int32` to the Builder buffer.
714
715 Note: aligns and checks for space.
716 """
717 self.Prepend(N.Int32Flags, x)
718
719 def PrependInt64(self, x):
720 """Prepend an `int64` to the Builder buffer.
721
722 Note: aligns and checks for space.
723 """
724 self.Prepend(N.Int64Flags, x)
725
726 def PrependFloat32(self, x):
727 """Prepend a `float32` to the Builder buffer.
728
729 Note: aligns and checks for space.
730 """
731 self.Prepend(N.Float32Flags, x)
732
733 def PrependFloat64(self, x):
734 """Prepend a `float64` to the Builder buffer.
735
736 Note: aligns and checks for space.
737 """
738 self.Prepend(N.Float64Flags, x)
739
Austin Schuh272c6132020-11-14 16:37:52 -0800740 def ForceDefaults(self, forceDefaults):
741 """
742 In order to save space, fields that are set to their default value
743 don't get serialized into the buffer. Forcing defaults provides a
744 way to manually disable this optimization. When set to `True`, will
745 always serialize default values.
746 """
747 self.forceDefaults = forceDefaults
748
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700749##############################################################
750
751 ## @cond FLATBUFFERS_INTERNAL
752 def PrependVOffsetT(self, x): self.Prepend(N.VOffsetTFlags, x)
753
754 def Place(self, x, flags):
755 """
756 Place prepends a value specified by `flags` to the Builder,
757 without checking for available space.
758 """
759
760 N.enforce_number(x, flags)
761 self.head = self.head - flags.bytewidth
762 encode.Write(flags.packer_type, self.Bytes, self.Head(), x)
763
764 def PlaceVOffsetT(self, x):
765 """PlaceVOffsetT prepends a VOffsetT to the Builder, without checking
766 for space.
767 """
768 N.enforce_number(x, N.VOffsetTFlags)
769 self.head = self.head - N.VOffsetTFlags.bytewidth
770 encode.Write(packer.voffset, self.Bytes, self.Head(), x)
771
772 def PlaceSOffsetT(self, x):
773 """PlaceSOffsetT prepends a SOffsetT to the Builder, without checking
774 for space.
775 """
776 N.enforce_number(x, N.SOffsetTFlags)
777 self.head = self.head - N.SOffsetTFlags.bytewidth
778 encode.Write(packer.soffset, self.Bytes, self.Head(), x)
779
780 def PlaceUOffsetT(self, x):
781 """PlaceUOffsetT prepends a UOffsetT to the Builder, without checking
782 for space.
783 """
784 N.enforce_number(x, N.UOffsetTFlags)
785 self.head = self.head - N.UOffsetTFlags.bytewidth
786 encode.Write(packer.uoffset, self.Bytes, self.Head(), x)
787 ## @endcond
788
789## @cond FLATBUFFERS_INTERNAL
790def vtableEqual(a, objectStart, b):
791 """vtableEqual compares an unwritten vtable to a written vtable."""
792
793 N.enforce_number(objectStart, N.UOffsetTFlags)
794
795 if len(a) * N.VOffsetTFlags.bytewidth != len(b):
796 return False
797
798 for i, elem in enumerate(a):
799 x = encode.Get(packer.voffset, b, i * N.VOffsetTFlags.bytewidth)
800
801 # Skip vtable entries that indicate a default value.
802 if x == 0 and elem == 0:
803 pass
804 else:
805 y = objectStart - elem
806 if x != y:
807 return False
808 return True
809## @endcond
810## @}