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