2020from . import packer
2121from .compat import memoryview_type
2222from .compat import NumpyRequiredForThisFeature , import_numpy
23- from .compat import range_func
2423from .number_types import (SOffsetTFlags , UOffsetTFlags , VOffsetTFlags )
2524
2625np = import_numpy ()
@@ -159,7 +158,7 @@ def __init__(self, initialSize=1024):
159158 self .vtables = {}
160159 self .nested = False
161160 self .forceDefaults = False
162- self .sharedStrings = {}
161+ self .sharedStrings = None
163162 ## @endcond
164163 self .finished = False
165164
@@ -172,7 +171,7 @@ def Clear(self) -> None:
172171 self .vtables = {}
173172 self .nested = False
174173 self .forceDefaults = False
175- self .sharedStrings = {}
174+ self .sharedStrings = None
176175 self .vectorNumElems = None
177176 ## @endcond
178177 self .finished = False
@@ -192,7 +191,7 @@ def Output(self):
192191 if not self .finished :
193192 raise BuilderNotFinishedError ()
194193
195- return self .Bytes [self .Head () :]
194+ return self .Bytes [self .head :]
196195
197196 ## @cond FLATBUFFERS_INTERNAL
198197 def StartObject (self , numfields ):
@@ -201,7 +200,7 @@ def StartObject(self, numfields):
201200 self .assertNotNested ()
202201
203202 # use 32-bit offsets so that arithmetic doesn't overflow.
204- self .current_vtable = [0 for _ in range_func ( numfields )]
203+ self .current_vtable = [0 ] * numfields
205204 self .objectEnd = self .Offset ()
206205 self .nested = True
207206
@@ -258,6 +257,7 @@ def WriteVtable(self):
258257 i = len (self .current_vtable ) - 1
259258 trailing = 0
260259 trim = True
260+ vt_entries = []
261261 while i >= 0 :
262262 off = 0
263263 elem = self .current_vtable [i ]
@@ -273,17 +273,24 @@ def WriteVtable(self):
273273 off = objectOffset - elem
274274 trim = False
275275
276- self . PrependVOffsetT ( off )
276+ vt_entries . append ( VOffsetTFlags . py_type ( off ) )
277277
278278 # The two metadata fields are written last.
279279
280280 # First, store the object bytesize:
281- self . PrependVOffsetT (VOffsetTFlags .py_type (objectSize ))
281+ vt_entries . append (VOffsetTFlags .py_type (objectSize ))
282282
283283 # Second, store the vtable bytesize:
284284 vBytes = len (self .current_vtable ) - trailing + VtableMetadataFields
285285 vBytes *= N .VOffsetTFlags .bytewidth
286- self .PrependVOffsetT (VOffsetTFlags .py_type (vBytes ))
286+ vt_entries .append (VOffsetTFlags .py_type (vBytes ))
287+
288+ field_entries = vt_entries [:- 2 ]
289+ field_entries .reverse ()
290+ ordered_entries = [vt_entries [- 1 ], vt_entries [- 2 ]]
291+ ordered_entries .extend (field_entries )
292+
293+ self .WriteVtableEntries (ordered_entries )
287294
288295 # Next, write the offset to the new vtable in the
289296 # already-allocated SOffsetT at the beginning of this object:
@@ -308,20 +315,37 @@ def WriteVtable(self):
308315 encode .Write (
309316 packer .soffset ,
310317 self .Bytes ,
311- self .Head () ,
318+ self .head ,
312319 SOffsetTFlags .py_type (vt2Offset - objectOffset ),
313320 )
314321
315322 self .current_vtable = None
316323 return objectOffset
317324
325+ def WriteVtableEntries (self , entries ):
326+ """Write a contiguous block of VOffsetT values with a single prep call."""
327+ count = len (entries )
328+ if count == 0 :
329+ return
330+ elem_size = N .VOffsetTFlags .bytewidth
331+ total_bytes = elem_size * count
332+ self .Prep (elem_size , total_bytes - elem_size )
333+ head = self .head - total_bytes
334+ self .head = UOffsetTFlags .py_type (head )
335+ pack = packer .voffset .pack_into
336+ buf = memoryview_type (self .Bytes )
337+ offset = head
338+ for value in entries :
339+ pack (buf , offset , value )
340+ offset += elem_size
341+
318342 def EndObject (self ):
319343 """EndObject writes data necessary to finish object construction."""
320344 self .assertNested ()
321345 self .nested = False
322346 return self .WriteVtable ()
323347
324- def growByteBuffer (self ):
348+ def GrowByteBuffer (self ):
325349 """Doubles the size of the byteslice, and copies the old data towards
326350
327351 the end of the new buffer (since we build the buffer backwards).
@@ -352,12 +376,15 @@ def Head(self):
352376 ## @cond FLATBUFFERS_INTERNAL
353377 def Offset (self ):
354378 """Offset relative to the end of the buffer."""
355- return UOffsetTFlags .py_type (len (self .Bytes ) - self .Head () )
379+ return UOffsetTFlags .py_type (len (self .Bytes ) - self .head )
356380
357381 def Pad (self , n ):
358382 """Pad places zeros at the current offset."""
359- for i in range_func (n ):
360- self .Place (0 , N .Uint8Flags )
383+ if n <= 0 :
384+ return
385+ new_head = self .head - n
386+ self .Bytes [new_head : self .head ] = b"\x00 " * n
387+ self .head = UOffsetTFlags .py_type (new_head )
361388
362389 def Prep (self , size , additionalBytes ):
363390 """Prep prepares to write an element of `size` after `additional_bytes`
@@ -374,15 +401,19 @@ def Prep(self, size, additionalBytes):
374401
375402 # Find the amount of alignment needed such that `size` is properly
376403 # aligned after `additionalBytes`:
377- alignSize = (~ (len (self .Bytes ) - self .Head () + additionalBytes )) + 1
404+ head = self .head
405+ buf_len = len (self .Bytes )
406+ alignSize = (~ (buf_len - head + additionalBytes )) + 1
378407 alignSize &= size - 1
379408
380409 # Reallocate the buffer if needed:
381- while self .Head () < alignSize + size + additionalBytes :
382- oldBufSize = len (self .Bytes )
383- self .growByteBuffer ()
384- updated_head = self .head + len (self .Bytes ) - oldBufSize
385- self .head = UOffsetTFlags .py_type (updated_head )
410+ needed = alignSize + size + additionalBytes
411+ while head < needed :
412+ oldBufSize = buf_len
413+ self .GrowByteBuffer ()
414+ buf_len = len (self .Bytes )
415+ head += buf_len - oldBufSize
416+ self .head = UOffsetTFlags .py_type (head )
386417 self .Pad (alignSize )
387418
388419 def PrependSOffsetTRelative (self , off ):
@@ -457,7 +488,9 @@ def CreateSharedString(self, s, encoding="utf-8", errors="strict"):
457488 before calling CreateString.
458489 """
459490
460- if s in self .sharedStrings :
491+ if not self .sharedStrings :
492+ self .sharedStrings = {}
493+ elif s in self .sharedStrings :
461494 return self .sharedStrings [s ]
462495
463496 off = self .CreateString (s , encoding , errors )
@@ -480,16 +513,17 @@ def CreateString(self, s, encoding="utf-8", errors="strict"):
480513 else :
481514 raise TypeError ("non-string passed to CreateString" )
482515
483- self .Prep (N .UOffsetTFlags .bytewidth , (len (x ) + 1 ) * N .Uint8Flags .bytewidth )
516+ payload_len = len (x )
517+ self .Prep (
518+ N .UOffsetTFlags .bytewidth , (payload_len + 1 ) * N .Uint8Flags .bytewidth
519+ )
484520 self .Place (0 , N .Uint8Flags )
485521
486- l = UOffsetTFlags .py_type (len (s ))
487- ## @cond FLATBUFFERS_INTERNAL
488- self .head = UOffsetTFlags .py_type (self .Head () - l )
489- ## @endcond
490- self .Bytes [self .Head () : self .Head () + l ] = x
522+ new_head = self .head - payload_len
523+ self .head = UOffsetTFlags .py_type (new_head )
524+ self .Bytes [new_head : new_head + payload_len ] = x
491525
492- self .vectorNumElems = len ( x )
526+ self .vectorNumElems = payload_len
493527 return self .EndVector ()
494528
495529 def CreateByteVector (self , x ):
@@ -503,15 +537,13 @@ def CreateByteVector(self, x):
503537 if not isinstance (x , compat .binary_types ):
504538 raise TypeError ("non-byte vector passed to CreateByteVector" )
505539
506- self .Prep (N .UOffsetTFlags .bytewidth , len (x ) * N .Uint8Flags .bytewidth )
540+ data_len = len (x )
541+ self .Prep (N .UOffsetTFlags .bytewidth , data_len * N .Uint8Flags .bytewidth )
542+ new_head = self .head - data_len
543+ self .head = UOffsetTFlags .py_type (new_head )
544+ self .Bytes [new_head : new_head + data_len ] = x
507545
508- l = UOffsetTFlags .py_type (len (x ))
509- ## @cond FLATBUFFERS_INTERNAL
510- self .head = UOffsetTFlags .py_type (self .Head () - l )
511- ## @endcond
512- self .Bytes [self .Head () : self .Head () + l ] = x
513-
514- self .vectorNumElems = len (x )
546+ self .vectorNumElems = data_len
515547 return self .EndVector ()
516548
517549 def CreateNumpyVector (self , x ):
@@ -538,14 +570,14 @@ def CreateNumpyVector(self, x):
538570 else :
539571 x_lend = x .byteswap (inplace = False )
540572
541- # Calculate total length
542- l = UOffsetTFlags .py_type (x_lend .itemsize * x_lend .size )
543- ## @cond FLATBUFFERS_INTERNAL
544- self .head = UOffsetTFlags .py_type (self .Head () - l )
545- ## @endcond
546-
547573 # tobytes ensures c_contiguous ordering
548- self .Bytes [self .Head () : self .Head () + l ] = x_lend .tobytes (order = "C" )
574+ payload = x_lend .tobytes (order = "C" )
575+
576+ # Calculate total length
577+ payload_len = len (payload )
578+ new_head = self .head - payload_len
579+ self .head = UOffsetTFlags .py_type (new_head )
580+ self .Bytes [new_head : new_head + payload_len ] = payload
549581
550582 self .vectorNumElems = x .size
551583 return self .EndVector ()
@@ -615,11 +647,11 @@ def __Finish(self, rootTable, sizePrefix, file_identifier=None):
615647
616648 self .PrependUOffsetTRelative (rootTable )
617649 if sizePrefix :
618- size = len (self .Bytes ) - self .Head ()
650+ size = len (self .Bytes ) - self .head
619651 N .enforce_number (size , N .Int32Flags )
620652 self .PrependInt32 (size )
621653 self .finished = True
622- return self .Head ()
654+ return self .head
623655
624656 def Finish (self , rootTable , file_identifier = None ):
625657 """Finish finalizes a buffer, pointing to the given `rootTable`."""
@@ -634,8 +666,31 @@ def FinishSizePrefixed(self, rootTable, file_identifier=None):
634666
635667 ## @cond FLATBUFFERS_INTERNAL
636668 def Prepend (self , flags , off ):
637- self .Prep (flags .bytewidth , 0 )
638- self .Place (off , flags )
669+ size = flags .bytewidth
670+ if size > self .minalign :
671+ self .minalign = size
672+
673+ head = self .head
674+ buf_len = len (self .Bytes )
675+ alignSize = (~ (buf_len - head )) + 1
676+ alignSize &= size - 1
677+
678+ needed = alignSize + size
679+ while head < needed :
680+ oldBufSize = buf_len
681+ self .GrowByteBuffer ()
682+ buf_len = len (self .Bytes )
683+ head += buf_len - oldBufSize
684+
685+ if alignSize :
686+ new_head = head - alignSize
687+ self .Bytes [new_head :head ] = b"\x00 " * alignSize
688+ head = new_head
689+
690+ N .enforce_number (off , flags )
691+ head -= size
692+ self .head = UOffsetTFlags .py_type (head )
693+ encode .Write (flags .packer_type , self .Bytes , head , off )
639694
640695 def PrependSlot (self , flags , o , x , d ):
641696 if x is not None :
@@ -803,70 +858,47 @@ def ForceDefaults(self, forceDefaults):
803858 ##############################################################
804859
805860 ## @cond FLATBUFFERS_INTERNAL
806- def PrependVOffsetT (self , x ):
807- self .Prepend (N .VOffsetTFlags , x )
808-
809861 def Place (self , x , flags ):
810862 """Place prepends a value specified by `flags` to the Builder,
811863
812864 without checking for available space.
813865 """
814866
815867 N .enforce_number (x , flags )
816- self .head = self .head - flags .bytewidth
817- encode .Write (flags .packer_type , self .Bytes , self .Head (), x )
868+ new_head = self .head - flags .bytewidth
869+ self .head = UOffsetTFlags .py_type (new_head )
870+ encode .Write (flags .packer_type , self .Bytes , new_head , x )
818871
819872 def PlaceVOffsetT (self , x ):
820873 """PlaceVOffsetT prepends a VOffsetT to the Builder, without checking
821874
822875 for space.
823876 """
824877 N .enforce_number (x , N .VOffsetTFlags )
825- self .head = self .head - N .VOffsetTFlags .bytewidth
826- encode .Write (packer .voffset , self .Bytes , self .Head (), x )
878+ new_head = self .head - N .VOffsetTFlags .bytewidth
879+ self .head = UOffsetTFlags .py_type (new_head )
880+ encode .Write (packer .voffset , self .Bytes , new_head , x )
827881
828882 def PlaceSOffsetT (self , x ):
829883 """PlaceSOffsetT prepends a SOffsetT to the Builder, without checking
830884
831885 for space.
832886 """
833887 N .enforce_number (x , N .SOffsetTFlags )
834- self .head = self .head - N .SOffsetTFlags .bytewidth
835- encode .Write (packer .soffset , self .Bytes , self .Head (), x )
888+ new_head = self .head - N .SOffsetTFlags .bytewidth
889+ self .head = UOffsetTFlags .py_type (new_head )
890+ encode .Write (packer .soffset , self .Bytes , new_head , x )
836891
837892 def PlaceUOffsetT (self , x ):
838893 """PlaceUOffsetT prepends a UOffsetT to the Builder, without checking
839894
840895 for space.
841896 """
842897 N .enforce_number (x , N .UOffsetTFlags )
843- self .head = self .head - N .UOffsetTFlags .bytewidth
844- encode .Write (packer .uoffset , self .Bytes , self .Head (), x )
898+ new_head = self .head - N .UOffsetTFlags .bytewidth
899+ self .head = UOffsetTFlags .py_type (new_head )
900+ encode .Write (packer .uoffset , self .Bytes , new_head , x )
845901
846902 ## @endcond
847903
848-
849- ## @cond FLATBUFFERS_INTERNAL
850- def vtableEqual (a , objectStart , b ):
851- """vtableEqual compares an unwritten vtable to a written vtable."""
852-
853- N .enforce_number (objectStart , N .UOffsetTFlags )
854-
855- if len (a ) * N .VOffsetTFlags .bytewidth != len (b ):
856- return False
857-
858- for i , elem in enumerate (a ):
859- x = encode .Get (packer .voffset , b , i * N .VOffsetTFlags .bytewidth )
860-
861- # Skip vtable entries that indicate a default value.
862- if x == 0 and elem == 0 :
863- pass
864- else :
865- y = objectStart - elem
866- if x != y :
867- return False
868- return True
869-
870-
871- ## @endcond
872904## @}
0 commit comments