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 list of vtable entries (i.e. a list of list of integers). 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", "finished") 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): 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 ## @endcond 135 self.finished = False 136 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 existingVtable = None 195 196 # Trim trailing 0 offsets. 197 while self.current_vtable and self.current_vtable[-1] == 0: 198 self.current_vtable.pop() 199 200 # Search backwards through existing vtables, because similar vtables 201 # are likely to have been recently appended. See 202 # BenchmarkVtableDeduplication for a case in which this heuristic 203 # saves about 30% of the time used in writing objects with duplicate 204 # tables. 205 206 i = len(self.vtables) - 1 207 while i >= 0: 208 # Find the other vtable, which is associated with `i`: 209 vt2Offset = self.vtables[i] 210 vt2Start = len(self.Bytes) - vt2Offset 211 vt2Len = encode.Get(packer.voffset, self.Bytes, vt2Start) 212 213 metadata = VtableMetadataFields * N.VOffsetTFlags.bytewidth 214 vt2End = vt2Start + vt2Len 215 vt2 = self.Bytes[vt2Start+metadata:vt2End] 216 217 # Compare the other vtable to the one under consideration. 218 # If they are equal, store the offset and break: 219 if vtableEqual(self.current_vtable, objectOffset, vt2): 220 existingVtable = vt2Offset 221 break 222 223 i -= 1 224 225 if existingVtable is None: 226 # Did not find a vtable, so write this one to the buffer. 227 228 # Write out the current vtable in reverse , because 229 # serialization occurs in last-first order: 230 i = len(self.current_vtable) - 1 231 while i >= 0: 232 off = 0 233 if self.current_vtable[i] != 0: 234 # Forward reference to field; 235 # use 32bit number to ensure no overflow: 236 off = objectOffset - self.current_vtable[i] 237 238 self.PrependVOffsetT(off) 239 i -= 1 240 241 # The two metadata fields are written last. 242 243 # First, store the object bytesize: 244 objectSize = UOffsetTFlags.py_type(objectOffset - self.objectEnd) 245 self.PrependVOffsetT(VOffsetTFlags.py_type(objectSize)) 246 247 # Second, store the vtable bytesize: 248 vBytes = len(self.current_vtable) + VtableMetadataFields 249 vBytes *= N.VOffsetTFlags.bytewidth 250 self.PrependVOffsetT(VOffsetTFlags.py_type(vBytes)) 251 252 # Next, write the offset to the new vtable in the 253 # already-allocated SOffsetT at the beginning of this object: 254 objectStart = SOffsetTFlags.py_type(len(self.Bytes) - objectOffset) 255 encode.Write(packer.soffset, self.Bytes, objectStart, 256 SOffsetTFlags.py_type(self.Offset() - objectOffset)) 257 258 # Finally, store this vtable in memory for future 259 # deduplication: 260 self.vtables.append(self.Offset()) 261 else: 262 # Found a duplicate vtable. 263 264 objectStart = SOffsetTFlags.py_type(len(self.Bytes) - objectOffset) 265 self.head = UOffsetTFlags.py_type(objectStart) 266 267 # Write the offset to the found vtable in the 268 # already-allocated SOffsetT at the beginning of this object: 269 encode.Write(packer.soffset, self.Bytes, self.Head(), 270 SOffsetTFlags.py_type(existingVtable - objectOffset)) 271 272 self.current_vtable = None 273 return objectOffset 274 275 def EndObject(self): 276 """EndObject writes data necessary to finish object construction.""" 277 self.assertNested() 278 self.nested = False 279 return self.WriteVtable() 280 281 def growByteBuffer(self): 282 """Doubles the size of the byteslice, and copies the old data towards 283 the end of the new buffer (since we build the buffer backwards).""" 284 if len(self.Bytes) == Builder.MAX_BUFFER_SIZE: 285 msg = "flatbuffers: cannot grow buffer beyond 2 gigabytes" 286 raise BuilderSizeError(msg) 287 288 newSize = min(len(self.Bytes) * 2, Builder.MAX_BUFFER_SIZE) 289 if newSize == 0: 290 newSize = 1 291 bytes2 = bytearray(newSize) 292 bytes2[newSize-len(self.Bytes):] = self.Bytes 293 self.Bytes = bytes2 294 ## @endcond 295 296 def Head(self): 297 """Get the start of useful data in the underlying byte buffer. 298 299 Note: unlike other functions, this value is interpreted as from the 300 left. 301 """ 302 ## @cond FLATBUFFERS_INTERNAL 303 return self.head 304 ## @endcond 305 306 ## @cond FLATBUFFERS_INTERNAL 307 def Offset(self): 308 """Offset relative to the end of the buffer.""" 309 return UOffsetTFlags.py_type(len(self.Bytes) - self.Head()) 310 311 def Pad(self, n): 312 """Pad places zeros at the current offset.""" 313 for i in range_func(n): 314 self.Place(0, N.Uint8Flags) 315 316 def Prep(self, size, additionalBytes): 317 """ 318 Prep prepares to write an element of `size` after `additional_bytes` 319 have been written, e.g. if you write a string, you need to align 320 such the int length field is aligned to SizeInt32, and the string 321 data follows it directly. 322 If all you need to do is align, `additionalBytes` will be 0. 323 """ 324 325 # Track the biggest thing we've ever aligned to. 326 if size > self.minalign: 327 self.minalign = size 328 329 # Find the amount of alignment needed such that `size` is properly 330 # aligned after `additionalBytes`: 331 alignSize = (~(len(self.Bytes) - self.Head() + additionalBytes)) + 1 332 alignSize &= (size - 1) 333 334 # Reallocate the buffer if needed: 335 while self.Head() < alignSize+size+additionalBytes: 336 oldBufSize = len(self.Bytes) 337 self.growByteBuffer() 338 updated_head = self.head + len(self.Bytes) - oldBufSize 339 self.head = UOffsetTFlags.py_type(updated_head) 340 self.Pad(alignSize) 341 342 def PrependSOffsetTRelative(self, off): 343 """ 344 PrependSOffsetTRelative prepends an SOffsetT, relative to where it 345 will be written. 346 """ 347 348 # Ensure alignment is already done: 349 self.Prep(N.SOffsetTFlags.bytewidth, 0) 350 if not (off <= self.Offset()): 351 msg = "flatbuffers: Offset arithmetic error." 352 raise OffsetArithmeticError(msg) 353 off2 = self.Offset() - off + N.SOffsetTFlags.bytewidth 354 self.PlaceSOffsetT(off2) 355 ## @endcond 356 357 def PrependUOffsetTRelative(self, off): 358 """Prepends an unsigned offset into vector data, relative to where it 359 will be written. 360 """ 361 362 # Ensure alignment is already done: 363 self.Prep(N.UOffsetTFlags.bytewidth, 0) 364 if not (off <= self.Offset()): 365 msg = "flatbuffers: Offset arithmetic error." 366 raise OffsetArithmeticError(msg) 367 off2 = self.Offset() - off + N.UOffsetTFlags.bytewidth 368 self.PlaceUOffsetT(off2) 369 370 ## @cond FLATBUFFERS_INTERNAL 371 def StartVector(self, elemSize, numElems, alignment): 372 """ 373 StartVector initializes bookkeeping for writing a new vector. 374 375 A vector has the following format: 376 - <UOffsetT: number of elements in this vector> 377 - <T: data>+, where T is the type of elements of this vector. 378 """ 379 380 self.assertNotNested() 381 self.nested = True 382 self.Prep(N.Uint32Flags.bytewidth, elemSize*numElems) 383 self.Prep(alignment, elemSize*numElems) # In case alignment > int. 384 return self.Offset() 385 ## @endcond 386 387 def EndVector(self, vectorNumElems): 388 """EndVector writes data necessary to finish vector construction.""" 389 390 self.assertNested() 391 ## @cond FLATBUFFERS_INTERNAL 392 self.nested = False 393 ## @endcond 394 # we already made space for this, so write without PrependUint32 395 self.PlaceUOffsetT(vectorNumElems) 396 return self.Offset() 397 398 def CreateString(self, s, encoding='utf-8', errors='strict'): 399 """CreateString writes a null-terminated byte string as a vector.""" 400 401 self.assertNotNested() 402 ## @cond FLATBUFFERS_INTERNAL 403 self.nested = True 404 ## @endcond 405 406 if isinstance(s, compat.string_types): 407 x = s.encode(encoding, errors) 408 elif isinstance(s, compat.binary_types): 409 x = s 410 else: 411 raise TypeError("non-string passed to CreateString") 412 413 self.Prep(N.UOffsetTFlags.bytewidth, (len(x)+1)*N.Uint8Flags.bytewidth) 414 self.Place(0, N.Uint8Flags) 415 416 l = UOffsetTFlags.py_type(len(s)) 417 ## @cond FLATBUFFERS_INTERNAL 418 self.head = UOffsetTFlags.py_type(self.Head() - l) 419 ## @endcond 420 self.Bytes[self.Head():self.Head()+l] = x 421 422 return self.EndVector(len(x)) 423 424 def CreateByteVector(self, x): 425 """CreateString writes a byte vector.""" 426 427 self.assertNotNested() 428 ## @cond FLATBUFFERS_INTERNAL 429 self.nested = True 430 ## @endcond 431 432 if not isinstance(x, compat.binary_types): 433 raise TypeError("non-byte vector passed to CreateByteVector") 434 435 self.Prep(N.UOffsetTFlags.bytewidth, len(x)*N.Uint8Flags.bytewidth) 436 437 l = UOffsetTFlags.py_type(len(x)) 438 ## @cond FLATBUFFERS_INTERNAL 439 self.head = UOffsetTFlags.py_type(self.Head() - l) 440 ## @endcond 441 self.Bytes[self.Head():self.Head()+l] = x 442 443 return self.EndVector(len(x)) 444 445 def CreateNumpyVector(self, x): 446 """CreateNumpyVector writes a numpy array into the buffer.""" 447 448 if np is None: 449 # Numpy is required for this feature 450 raise NumpyRequiredForThisFeature("Numpy was not found.") 451 452 if not isinstance(x, np.ndarray): 453 raise TypeError("non-numpy-ndarray passed to CreateNumpyVector") 454 455 if x.dtype.kind not in ['b', 'i', 'u', 'f']: 456 raise TypeError("numpy-ndarray holds elements of unsupported datatype") 457 458 if x.ndim > 1: 459 raise TypeError("multidimensional-ndarray passed to CreateNumpyVector") 460 461 self.StartVector(x.itemsize, x.size, x.dtype.alignment) 462 463 # Ensure little endian byte ordering 464 if x.dtype.str[0] == "<": 465 x_lend = x 466 else: 467 x_lend = x.byteswap(inplace=False) 468 469 # Calculate total length 470 l = UOffsetTFlags.py_type(x_lend.itemsize * x_lend.size) 471 ## @cond FLATBUFFERS_INTERNAL 472 self.head = UOffsetTFlags.py_type(self.Head() - l) 473 ## @endcond 474 475 # tobytes ensures c_contiguous ordering 476 self.Bytes[self.Head():self.Head()+l] = x_lend.tobytes(order='C') 477 478 return self.EndVector(x.size) 479 480 ## @cond FLATBUFFERS_INTERNAL 481 def assertNested(self): 482 """ 483 Check that we are in the process of building an object. 484 """ 485 486 if not self.nested: 487 raise IsNotNestedError() 488 489 def assertNotNested(self): 490 """ 491 Check that no other objects are being built while making this 492 object. If not, raise an exception. 493 """ 494 495 if self.nested: 496 raise IsNestedError() 497 498 def assertStructIsInline(self, obj): 499 """ 500 Structs are always stored inline, so need to be created right 501 where they are used. You'll get this error if you created it 502 elsewhere. 503 """ 504 505 N.enforce_number(obj, N.UOffsetTFlags) 506 if obj != self.Offset(): 507 msg = ("flatbuffers: Tried to write a Struct at an Offset that " 508 "is different from the current Offset of the Builder.") 509 raise StructIsNotInlineError(msg) 510 511 def Slot(self, slotnum): 512 """ 513 Slot sets the vtable key `voffset` to the current location in the 514 buffer. 515 516 """ 517 self.assertNested() 518 self.current_vtable[slotnum] = self.Offset() 519 ## @endcond 520 521 def __Finish(self, rootTable, sizePrefix): 522 """Finish finalizes a buffer, pointing to the given `rootTable`.""" 523 N.enforce_number(rootTable, N.UOffsetTFlags) 524 prepSize = N.UOffsetTFlags.bytewidth 525 if sizePrefix: 526 prepSize += N.Int32Flags.bytewidth 527 self.Prep(self.minalign, prepSize) 528 self.PrependUOffsetTRelative(rootTable) 529 if sizePrefix: 530 size = len(self.Bytes) - self.Head() 531 N.enforce_number(size, N.Int32Flags) 532 self.PrependInt32(size) 533 self.finished = True 534 return self.Head() 535 536 def Finish(self, rootTable): 537 """Finish finalizes a buffer, pointing to the given `rootTable`.""" 538 return self.__Finish(rootTable, False) 539 540 def FinishSizePrefixed(self, rootTable): 541 """ 542 Finish finalizes a buffer, pointing to the given `rootTable`, 543 with the size prefixed. 544 """ 545 return self.__Finish(rootTable, True) 546 547 ## @cond FLATBUFFERS_INTERNAL 548 def Prepend(self, flags, off): 549 self.Prep(flags.bytewidth, 0) 550 self.Place(off, flags) 551 552 def PrependSlot(self, flags, o, x, d): 553 N.enforce_number(x, flags) 554 N.enforce_number(d, flags) 555 if x != d: 556 self.Prepend(flags, x) 557 self.Slot(o) 558 559 def PrependBoolSlot(self, *args): self.PrependSlot(N.BoolFlags, *args) 560 561 def PrependByteSlot(self, *args): self.PrependSlot(N.Uint8Flags, *args) 562 563 def PrependUint8Slot(self, *args): self.PrependSlot(N.Uint8Flags, *args) 564 565 def PrependUint16Slot(self, *args): self.PrependSlot(N.Uint16Flags, *args) 566 567 def PrependUint32Slot(self, *args): self.PrependSlot(N.Uint32Flags, *args) 568 569 def PrependUint64Slot(self, *args): self.PrependSlot(N.Uint64Flags, *args) 570 571 def PrependInt8Slot(self, *args): self.PrependSlot(N.Int8Flags, *args) 572 573 def PrependInt16Slot(self, *args): self.PrependSlot(N.Int16Flags, *args) 574 575 def PrependInt32Slot(self, *args): self.PrependSlot(N.Int32Flags, *args) 576 577 def PrependInt64Slot(self, *args): self.PrependSlot(N.Int64Flags, *args) 578 579 def PrependFloat32Slot(self, *args): self.PrependSlot(N.Float32Flags, 580 *args) 581 582 def PrependFloat64Slot(self, *args): self.PrependSlot(N.Float64Flags, 583 *args) 584 585 def PrependUOffsetTRelativeSlot(self, o, x, d): 586 """ 587 PrependUOffsetTRelativeSlot prepends an UOffsetT onto the object at 588 vtable slot `o`. If value `x` equals default `d`, then the slot will 589 be set to zero and no other data will be written. 590 """ 591 592 if x != d: 593 self.PrependUOffsetTRelative(x) 594 self.Slot(o) 595 596 def PrependStructSlot(self, v, x, d): 597 """ 598 PrependStructSlot prepends a struct onto the object at vtable slot `o`. 599 Structs are stored inline, so nothing additional is being added. 600 In generated code, `d` is always 0. 601 """ 602 603 N.enforce_number(d, N.UOffsetTFlags) 604 if x != d: 605 self.assertStructIsInline(x) 606 self.Slot(v) 607 608 ## @endcond 609 610 def PrependBool(self, x): 611 """Prepend a `bool` to the Builder buffer. 612 613 Note: aligns and checks for space. 614 """ 615 self.Prepend(N.BoolFlags, x) 616 617 def PrependByte(self, x): 618 """Prepend a `byte` to the Builder buffer. 619 620 Note: aligns and checks for space. 621 """ 622 self.Prepend(N.Uint8Flags, x) 623 624 def PrependUint8(self, x): 625 """Prepend an `uint8` to the Builder buffer. 626 627 Note: aligns and checks for space. 628 """ 629 self.Prepend(N.Uint8Flags, x) 630 631 def PrependUint16(self, x): 632 """Prepend an `uint16` to the Builder buffer. 633 634 Note: aligns and checks for space. 635 """ 636 self.Prepend(N.Uint16Flags, x) 637 638 def PrependUint32(self, x): 639 """Prepend an `uint32` to the Builder buffer. 640 641 Note: aligns and checks for space. 642 """ 643 self.Prepend(N.Uint32Flags, x) 644 645 def PrependUint64(self, x): 646 """Prepend an `uint64` to the Builder buffer. 647 648 Note: aligns and checks for space. 649 """ 650 self.Prepend(N.Uint64Flags, x) 651 652 def PrependInt8(self, x): 653 """Prepend an `int8` to the Builder buffer. 654 655 Note: aligns and checks for space. 656 """ 657 self.Prepend(N.Int8Flags, x) 658 659 def PrependInt16(self, x): 660 """Prepend an `int16` to the Builder buffer. 661 662 Note: aligns and checks for space. 663 """ 664 self.Prepend(N.Int16Flags, x) 665 666 def PrependInt32(self, x): 667 """Prepend an `int32` to the Builder buffer. 668 669 Note: aligns and checks for space. 670 """ 671 self.Prepend(N.Int32Flags, x) 672 673 def PrependInt64(self, x): 674 """Prepend an `int64` to the Builder buffer. 675 676 Note: aligns and checks for space. 677 """ 678 self.Prepend(N.Int64Flags, x) 679 680 def PrependFloat32(self, x): 681 """Prepend a `float32` to the Builder buffer. 682 683 Note: aligns and checks for space. 684 """ 685 self.Prepend(N.Float32Flags, x) 686 687 def PrependFloat64(self, x): 688 """Prepend a `float64` to the Builder buffer. 689 690 Note: aligns and checks for space. 691 """ 692 self.Prepend(N.Float64Flags, x) 693 694############################################################## 695 696 ## @cond FLATBUFFERS_INTERNAL 697 def PrependVOffsetT(self, x): self.Prepend(N.VOffsetTFlags, x) 698 699 def Place(self, x, flags): 700 """ 701 Place prepends a value specified by `flags` to the Builder, 702 without checking for available space. 703 """ 704 705 N.enforce_number(x, flags) 706 self.head = self.head - flags.bytewidth 707 encode.Write(flags.packer_type, self.Bytes, self.Head(), x) 708 709 def PlaceVOffsetT(self, x): 710 """PlaceVOffsetT prepends a VOffsetT to the Builder, without checking 711 for space. 712 """ 713 N.enforce_number(x, N.VOffsetTFlags) 714 self.head = self.head - N.VOffsetTFlags.bytewidth 715 encode.Write(packer.voffset, self.Bytes, self.Head(), x) 716 717 def PlaceSOffsetT(self, x): 718 """PlaceSOffsetT prepends a SOffsetT to the Builder, without checking 719 for space. 720 """ 721 N.enforce_number(x, N.SOffsetTFlags) 722 self.head = self.head - N.SOffsetTFlags.bytewidth 723 encode.Write(packer.soffset, self.Bytes, self.Head(), x) 724 725 def PlaceUOffsetT(self, x): 726 """PlaceUOffsetT prepends a UOffsetT to the Builder, without checking 727 for space. 728 """ 729 N.enforce_number(x, N.UOffsetTFlags) 730 self.head = self.head - N.UOffsetTFlags.bytewidth 731 encode.Write(packer.uoffset, self.Bytes, self.Head(), x) 732 ## @endcond 733 734## @cond FLATBUFFERS_INTERNAL 735def vtableEqual(a, objectStart, b): 736 """vtableEqual compares an unwritten vtable to a written vtable.""" 737 738 N.enforce_number(objectStart, N.UOffsetTFlags) 739 740 if len(a) * N.VOffsetTFlags.bytewidth != len(b): 741 return False 742 743 for i, elem in enumerate(a): 744 x = encode.Get(packer.voffset, b, i * N.VOffsetTFlags.bytewidth) 745 746 # Skip vtable entries that indicate a default value. 747 if x == 0 and elem == 0: 748 pass 749 else: 750 y = objectStart - elem 751 if x != y: 752 return False 753 return True 754## @endcond 755## @} 756