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