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