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
@@ -255,6 +254,7 @@ def WriteVtable(self):
255254 i = len (self .current_vtable ) - 1
256255 trailing = 0
257256 trim = True
257+ vt_entries = []
258258 while i >= 0 :
259259 off = 0
260260 elem = self .current_vtable [i ]
@@ -270,18 +270,25 @@ def WriteVtable(self):
270270 off = objectOffset - elem
271271 trim = False
272272
273- self . PrependVOffsetT ( off )
273+ vt_entries . append ( VOffsetTFlags . py_type ( off ) )
274274
275275 # The two metadata fields are written last.
276276
277277 # First, store the object bytesize:
278278 objectSize = UOffsetTFlags .py_type (objectOffset - self .objectEnd )
279- self . PrependVOffsetT (VOffsetTFlags .py_type (objectSize ))
279+ vt_entries . append (VOffsetTFlags .py_type (objectSize ))
280280
281281 # Second, store the vtable bytesize:
282282 vBytes = len (self .current_vtable ) - trailing + VtableMetadataFields
283283 vBytes *= N .VOffsetTFlags .bytewidth
284- self .PrependVOffsetT (VOffsetTFlags .py_type (vBytes ))
284+ vt_entries .append (VOffsetTFlags .py_type (vBytes ))
285+
286+ field_entries = vt_entries [:- 2 ]
287+ field_entries .reverse ()
288+ ordered_entries = [vt_entries [- 1 ], vt_entries [- 2 ]]
289+ ordered_entries .extend (field_entries )
290+
291+ self .WriteVtableEntries (ordered_entries )
285292
286293 # Next, write the offset to the new vtable in the
287294 # already-allocated SOffsetT at the beginning of this object:
@@ -306,20 +313,37 @@ def WriteVtable(self):
306313 encode .Write (
307314 packer .soffset ,
308315 self .Bytes ,
309- self .Head () ,
316+ self .head ,
310317 SOffsetTFlags .py_type (vt2Offset - objectOffset ),
311318 )
312319
313320 self .current_vtable = None
314321 return objectOffset
315322
323+ def WriteVtableEntries (self , entries ):
324+ """Write a contiguous block of VOffsetT values with a single prep call."""
325+ count = len (entries )
326+ if count == 0 :
327+ return
328+ elem_size = N .VOffsetTFlags .bytewidth
329+ total_bytes = elem_size * count
330+ self .Prep (elem_size , total_bytes - elem_size )
331+ head = self .head - total_bytes
332+ self .head = UOffsetTFlags .py_type (head )
333+ pack = packer .voffset .pack_into
334+ buf = memoryview_type (self .Bytes )
335+ offset = head
336+ for value in entries :
337+ pack (buf , offset , value )
338+ offset += elem_size
339+
316340 def EndObject (self ):
317341 """EndObject writes data necessary to finish object construction."""
318342 self .assertNested ()
319343 self .nested = False
320344 return self .WriteVtable ()
321345
322- def growByteBuffer (self ):
346+ def GrowByteBuffer (self ):
323347 """Doubles the size of the byteslice, and copies the old data towards
324348
325349 the end of the new buffer (since we build the buffer backwards).
@@ -350,12 +374,15 @@ def Head(self):
350374 ## @cond FLATBUFFERS_INTERNAL
351375 def Offset (self ):
352376 """Offset relative to the end of the buffer."""
353- return UOffsetTFlags .py_type (len (self .Bytes ) - self .Head () )
377+ return UOffsetTFlags .py_type (len (self .Bytes ) - self .head )
354378
355379 def Pad (self , n ):
356380 """Pad places zeros at the current offset."""
357- for i in range_func (n ):
358- self .Place (0 , N .Uint8Flags )
381+ if n <= 0 :
382+ return
383+ new_head = self .head - n
384+ self .Bytes [new_head : self .head ] = b"\x00 " * n
385+ self .head = UOffsetTFlags .py_type (new_head )
359386
360387 def Prep (self , size , additionalBytes ):
361388 """Prep prepares to write an element of `size` after `additional_bytes`
@@ -372,15 +399,19 @@ def Prep(self, size, additionalBytes):
372399
373400 # Find the amount of alignment needed such that `size` is properly
374401 # aligned after `additionalBytes`:
375- alignSize = (~ (len (self .Bytes ) - self .Head () + additionalBytes )) + 1
402+ head = self .head
403+ buf_len = len (self .Bytes )
404+ alignSize = (~ (buf_len - head + additionalBytes )) + 1
376405 alignSize &= size - 1
377406
378407 # Reallocate the buffer if needed:
379- while self .Head () < alignSize + size + additionalBytes :
380- oldBufSize = len (self .Bytes )
381- self .growByteBuffer ()
382- updated_head = self .head + len (self .Bytes ) - oldBufSize
383- self .head = UOffsetTFlags .py_type (updated_head )
408+ needed = alignSize + size + additionalBytes
409+ while head < needed :
410+ oldBufSize = buf_len
411+ self .GrowByteBuffer ()
412+ buf_len = len (self .Bytes )
413+ head += buf_len - oldBufSize
414+ self .head = UOffsetTFlags .py_type (head )
384415 self .Pad (alignSize )
385416
386417 def PrependSOffsetTRelative (self , off ):
@@ -455,7 +486,9 @@ def CreateSharedString(self, s, encoding="utf-8", errors="strict"):
455486 before calling CreateString.
456487 """
457488
458- if s in self .sharedStrings :
489+ if not self .sharedStrings :
490+ self .sharedStrings = {}
491+ elif s in self .sharedStrings :
459492 return self .sharedStrings [s ]
460493
461494 off = self .CreateString (s , encoding , errors )
@@ -478,16 +511,17 @@ def CreateString(self, s, encoding="utf-8", errors="strict"):
478511 else :
479512 raise TypeError ("non-string passed to CreateString" )
480513
481- self .Prep (N .UOffsetTFlags .bytewidth , (len (x ) + 1 ) * N .Uint8Flags .bytewidth )
514+ payload_len = len (x )
515+ self .Prep (
516+ N .UOffsetTFlags .bytewidth , (payload_len + 1 ) * N .Uint8Flags .bytewidth
517+ )
482518 self .Place (0 , N .Uint8Flags )
483519
484- l = UOffsetTFlags .py_type (len (s ))
485- ## @cond FLATBUFFERS_INTERNAL
486- self .head = UOffsetTFlags .py_type (self .Head () - l )
487- ## @endcond
488- self .Bytes [self .Head () : self .Head () + l ] = x
520+ new_head = self .head - payload_len
521+ self .head = UOffsetTFlags .py_type (new_head )
522+ self .Bytes [new_head : new_head + payload_len ] = x
489523
490- self .vectorNumElems = len ( x )
524+ self .vectorNumElems = payload_len
491525 return self .EndVector ()
492526
493527 def CreateByteVector (self , x ):
@@ -501,15 +535,13 @@ def CreateByteVector(self, x):
501535 if not isinstance (x , compat .binary_types ):
502536 raise TypeError ("non-byte vector passed to CreateByteVector" )
503537
504- self .Prep (N .UOffsetTFlags .bytewidth , len (x ) * N .Uint8Flags .bytewidth )
538+ data_len = len (x )
539+ self .Prep (N .UOffsetTFlags .bytewidth , data_len * N .Uint8Flags .bytewidth )
540+ new_head = self .head - data_len
541+ self .head = UOffsetTFlags .py_type (new_head )
542+ self .Bytes [new_head : new_head + data_len ] = x
505543
506- l = UOffsetTFlags .py_type (len (x ))
507- ## @cond FLATBUFFERS_INTERNAL
508- self .head = UOffsetTFlags .py_type (self .Head () - l )
509- ## @endcond
510- self .Bytes [self .Head () : self .Head () + l ] = x
511-
512- self .vectorNumElems = len (x )
544+ self .vectorNumElems = data_len
513545 return self .EndVector ()
514546
515547 def CreateNumpyVector (self , x ):
@@ -536,14 +568,14 @@ def CreateNumpyVector(self, x):
536568 else :
537569 x_lend = x .byteswap (inplace = False )
538570
539- # Calculate total length
540- l = UOffsetTFlags .py_type (x_lend .itemsize * x_lend .size )
541- ## @cond FLATBUFFERS_INTERNAL
542- self .head = UOffsetTFlags .py_type (self .Head () - l )
543- ## @endcond
544-
545571 # tobytes ensures c_contiguous ordering
546- self .Bytes [self .Head () : self .Head () + l ] = x_lend .tobytes (order = "C" )
572+ payload = x_lend .tobytes (order = "C" )
573+
574+ # Calculate total length
575+ payload_len = len (payload )
576+ new_head = self .head - payload_len
577+ self .head = UOffsetTFlags .py_type (new_head )
578+ self .Bytes [new_head : new_head + payload_len ] = payload
547579
548580 self .vectorNumElems = x .size
549581 return self .EndVector ()
@@ -613,11 +645,11 @@ def __Finish(self, rootTable, sizePrefix, file_identifier=None):
613645
614646 self .PrependUOffsetTRelative (rootTable )
615647 if sizePrefix :
616- size = len (self .Bytes ) - self .Head ()
648+ size = len (self .Bytes ) - self .head
617649 N .enforce_number (size , N .Int32Flags )
618650 self .PrependInt32 (size )
619651 self .finished = True
620- return self .Head ()
652+ return self .head
621653
622654 def Finish (self , rootTable , file_identifier = None ):
623655 """Finish finalizes a buffer, pointing to the given `rootTable`."""
@@ -632,8 +664,31 @@ def FinishSizePrefixed(self, rootTable, file_identifier=None):
632664
633665 ## @cond FLATBUFFERS_INTERNAL
634666 def Prepend (self , flags , off ):
635- self .Prep (flags .bytewidth , 0 )
636- self .Place (off , flags )
667+ size = flags .bytewidth
668+ if size > self .minalign :
669+ self .minalign = size
670+
671+ head = self .head
672+ buf_len = len (self .Bytes )
673+ alignSize = (~ (buf_len - head )) + 1
674+ alignSize &= size - 1
675+
676+ needed = alignSize + size
677+ while head < needed :
678+ oldBufSize = buf_len
679+ self .GrowByteBuffer ()
680+ buf_len = len (self .Bytes )
681+ head += buf_len - oldBufSize
682+
683+ if alignSize :
684+ new_head = head - alignSize
685+ self .Bytes [new_head :head ] = b"\x00 " * alignSize
686+ head = new_head
687+
688+ N .enforce_number (off , flags )
689+ head -= size
690+ self .head = UOffsetTFlags .py_type (head )
691+ encode .Write (flags .packer_type , self .Bytes , head , off )
637692
638693 def PrependSlot (self , flags , o , x , d ):
639694 if x is not None :
@@ -801,70 +856,47 @@ def ForceDefaults(self, forceDefaults):
801856 ##############################################################
802857
803858 ## @cond FLATBUFFERS_INTERNAL
804- def PrependVOffsetT (self , x ):
805- self .Prepend (N .VOffsetTFlags , x )
806-
807859 def Place (self , x , flags ):
808860 """Place prepends a value specified by `flags` to the Builder,
809861
810862 without checking for available space.
811863 """
812864
813865 N .enforce_number (x , flags )
814- self .head = self .head - flags .bytewidth
815- encode .Write (flags .packer_type , self .Bytes , self .Head (), x )
866+ new_head = self .head - flags .bytewidth
867+ self .head = UOffsetTFlags .py_type (new_head )
868+ encode .Write (flags .packer_type , self .Bytes , new_head , x )
816869
817870 def PlaceVOffsetT (self , x ):
818871 """PlaceVOffsetT prepends a VOffsetT to the Builder, without checking
819872
820873 for space.
821874 """
822875 N .enforce_number (x , N .VOffsetTFlags )
823- self .head = self .head - N .VOffsetTFlags .bytewidth
824- encode .Write (packer .voffset , self .Bytes , self .Head (), x )
876+ new_head = self .head - N .VOffsetTFlags .bytewidth
877+ self .head = UOffsetTFlags .py_type (new_head )
878+ encode .Write (packer .voffset , self .Bytes , new_head , x )
825879
826880 def PlaceSOffsetT (self , x ):
827881 """PlaceSOffsetT prepends a SOffsetT to the Builder, without checking
828882
829883 for space.
830884 """
831885 N .enforce_number (x , N .SOffsetTFlags )
832- self .head = self .head - N .SOffsetTFlags .bytewidth
833- encode .Write (packer .soffset , self .Bytes , self .Head (), x )
886+ new_head = self .head - N .SOffsetTFlags .bytewidth
887+ self .head = UOffsetTFlags .py_type (new_head )
888+ encode .Write (packer .soffset , self .Bytes , new_head , x )
834889
835890 def PlaceUOffsetT (self , x ):
836891 """PlaceUOffsetT prepends a UOffsetT to the Builder, without checking
837892
838893 for space.
839894 """
840895 N .enforce_number (x , N .UOffsetTFlags )
841- self .head = self .head - N .UOffsetTFlags .bytewidth
842- encode .Write (packer .uoffset , self .Bytes , self .Head (), x )
896+ new_head = self .head - N .UOffsetTFlags .bytewidth
897+ self .head = UOffsetTFlags .py_type (new_head )
898+ encode .Write (packer .uoffset , self .Bytes , new_head , x )
843899
844900 ## @endcond
845901
846-
847- ## @cond FLATBUFFERS_INTERNAL
848- def vtableEqual (a , objectStart , b ):
849- """vtableEqual compares an unwritten vtable to a written vtable."""
850-
851- N .enforce_number (objectStart , N .UOffsetTFlags )
852-
853- if len (a ) * N .VOffsetTFlags .bytewidth != len (b ):
854- return False
855-
856- for i , elem in enumerate (a ):
857- x = encode .Get (packer .voffset , b , i * N .VOffsetTFlags .bytewidth )
858-
859- # Skip vtable entries that indicate a default value.
860- if x == 0 and elem == 0 :
861- pass
862- else :
863- y = objectStart - elem
864- if x != y :
865- return False
866- return True
867-
868-
869- ## @endcond
870902## @}
0 commit comments