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