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