• 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
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
125    def __init__(self, initialSize=1024):
126        """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
143        self.forceDefaults = False
144        ## @endcond
145        self.finished = False
146
147    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
383        self.vectorNumElems = numElems
384        self.Prep(N.Uint32Flags.bytewidth, elemSize*numElems)
385        self.Prep(alignment, elemSize*numElems)  # In case alignment > int.
386        return self.Offset()
387    ## @endcond
388
389    def EndVector(self, numElems = None):
390        """EndVector writes data necessary to finish vector construction."""
391
392        self.assertNested()
393        ## @cond FLATBUFFERS_INTERNAL
394        self.nested = False
395        ## @endcond
396
397        if numElems:
398            warnings.warn("numElems is deprecated.",
399                          DeprecationWarning, stacklevel=2)
400            if numElems != self.vectorNumElems:
401                raise EndVectorLengthMismatched();
402
403        # we already made space for this, so write without PrependUint32
404        self.PlaceUOffsetT(self.vectorNumElems)
405        self.vectorNumElems = None
406        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
432        self.vectorNumElems = len(x)
433        return self.EndVector()
434
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
454        self.vectorNumElems = len(x)
455        return self.EndVector()
456
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')
489
490        self.vectorNumElems = x.size
491        return self.EndVector()
492
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
538        prepSize = N.UOffsetTFlags.bytewidth
539        if file_identifier is not None:
540            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
548            # 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:
554                self.Place(file_identifier[i], N.Uint8Flags)
555
556        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):
581        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):
586            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
622        if x != d or self.forceDefaults:
623            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
724    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
733##############################################################
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## @}
795