| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592 |
- # Lint as: python3
- # Copyright 2020 Google Inc. All rights reserved.
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- """Implementation of FlexBuffers binary format.
- For more info check https://google.github.io/flatbuffers/flexbuffers.html and
- corresponding C++ implementation at
- https://github.com/google/flatbuffers/blob/master/include/flatbuffers/flexbuffers.h
- """
- # pylint: disable=invalid-name
- # TODO(dkovalev): Add type hints everywhere, so tools like pytypes could work.
- import array
- import contextlib
- import enum
- import struct
- __all__ = ('Type', 'Builder', 'GetRoot', 'Dumps', 'Loads')
- class BitWidth(enum.IntEnum):
- """Supported bit widths of value types.
- These are used in the lower 2 bits of a type field to determine the size of
- the elements (and or size field) of the item pointed to (e.g. vector).
- """
- W8 = 0 # 2^0 = 1 byte
- W16 = 1 # 2^1 = 2 bytes
- W32 = 2 # 2^2 = 4 bytes
- W64 = 3 # 2^3 = 8 bytes
- @staticmethod
- def U(value):
- """Returns the minimum `BitWidth` to encode unsigned integer value."""
- assert value >= 0
- if value < (1 << 8):
- return BitWidth.W8
- elif value < (1 << 16):
- return BitWidth.W16
- elif value < (1 << 32):
- return BitWidth.W32
- elif value < (1 << 64):
- return BitWidth.W64
- else:
- raise ValueError('value is too big to encode: %s' % value)
- @staticmethod
- def I(value):
- """Returns the minimum `BitWidth` to encode signed integer value."""
- # -2^(n-1) <= value < 2^(n-1)
- # -2^n <= 2 * value < 2^n
- # 2 * value < 2^n, when value >= 0 or 2 * (-value) <= 2^n, when value < 0
- # 2 * value < 2^n, when value >= 0 or 2 * (-value) - 1 < 2^n, when value < 0
- #
- # if value >= 0:
- # return BitWidth.U(2 * value)
- # else:
- # return BitWidth.U(2 * (-value) - 1) # ~x = -x - 1
- value *= 2
- return BitWidth.U(value if value >= 0 else ~value)
- @staticmethod
- def F(value):
- """Returns the `BitWidth` to encode floating point value."""
- if struct.unpack('<f', struct.pack('<f', value))[0] == value:
- return BitWidth.W32
- return BitWidth.W64
- @staticmethod
- def B(byte_width):
- return {1: BitWidth.W8, 2: BitWidth.W16, 4: BitWidth.W32, 8: BitWidth.W64}[
- byte_width
- ]
- I = {1: 'b', 2: 'h', 4: 'i', 8: 'q'} # Integer formats
- U = {1: 'B', 2: 'H', 4: 'I', 8: 'Q'} # Unsigned integer formats
- F = {4: 'f', 8: 'd'} # Floating point formats
- def _Unpack(fmt, buf):
- return struct.unpack('<%s' % fmt[len(buf)], buf)[0]
- def _UnpackVector(fmt, buf, length):
- byte_width = len(buf) // length
- return struct.unpack('<%d%s' % (length, fmt[byte_width]), buf)
- def _Pack(fmt, value, byte_width):
- return struct.pack('<%s' % fmt[byte_width], value)
- def _PackVector(fmt, values, byte_width):
- return struct.pack('<%d%s' % (len(values), fmt[byte_width]), *values)
- def _Mutate(fmt, buf, value, byte_width, value_bit_width):
- if (1 << value_bit_width) <= byte_width:
- buf[:byte_width] = _Pack(fmt, value, byte_width)
- return True
- return False
- # Computes how many bytes you'd have to pad to be able to write an
- # "scalar_size" scalar if the buffer had grown to "buf_size",
- # "scalar_size" is a power of two.
- def _PaddingBytes(buf_size, scalar_size):
- # ((buf_size + (scalar_size - 1)) // scalar_size) * scalar_size - buf_size
- return -buf_size & (scalar_size - 1)
- def _ShiftSlice(s, offset, length):
- start = offset + (0 if s.start is None else s.start)
- stop = offset + (length if s.stop is None else s.stop)
- return slice(start, stop, s.step)
- # https://en.cppreference.com/w/cpp/algorithm/lower_bound
- def _LowerBound(values, value, pred):
- """Implementation of C++ std::lower_bound() algorithm."""
- first, last = 0, len(values)
- count = last - first
- while count > 0:
- i = first
- step = count // 2
- i += step
- if pred(values[i], value):
- i += 1
- first = i
- count -= step + 1
- else:
- count = step
- return first
- # https://en.cppreference.com/w/cpp/algorithm/binary_search
- def _BinarySearch(values, value, pred=lambda x, y: x < y):
- """Implementation of C++ std::binary_search() algorithm."""
- index = _LowerBound(values, value, pred)
- if index != len(values) and not pred(value, values[index]):
- return index
- return -1
- class Type(enum.IntEnum):
- """Supported types of encoded data.
- These are used as the upper 6 bits of a type field to indicate the actual
- type.
- """
- NULL = 0
- INT = 1
- UINT = 2
- FLOAT = 3
- # Types above stored inline, types below store an offset.
- KEY = 4
- STRING = 5
- INDIRECT_INT = 6
- INDIRECT_UINT = 7
- INDIRECT_FLOAT = 8
- MAP = 9
- VECTOR = 10 # Untyped.
- VECTOR_INT = 11 # Typed any size (stores no type table).
- VECTOR_UINT = 12
- VECTOR_FLOAT = 13
- VECTOR_KEY = 14
- # DEPRECATED, use VECTOR or VECTOR_KEY instead.
- # Read test.cpp/FlexBuffersDeprecatedTest() for details on why.
- VECTOR_STRING_DEPRECATED = 15
- VECTOR_INT2 = 16 # Typed tuple (no type table, no size field).
- VECTOR_UINT2 = 17
- VECTOR_FLOAT2 = 18
- VECTOR_INT3 = 19 # Typed triple (no type table, no size field).
- VECTOR_UINT3 = 20
- VECTOR_FLOAT3 = 21
- VECTOR_INT4 = 22 # Typed quad (no type table, no size field).
- VECTOR_UINT4 = 23
- VECTOR_FLOAT4 = 24
- BLOB = 25
- BOOL = 26
- VECTOR_BOOL = 36 # To do the same type of conversion of type to vector type
- @staticmethod
- def Pack(type_, bit_width):
- return (int(type_) << 2) | bit_width
- @staticmethod
- def Unpack(packed_type):
- return 1 << (packed_type & 0b11), Type(packed_type >> 2)
- @staticmethod
- def IsInline(type_):
- return type_ <= Type.FLOAT or type_ == Type.BOOL
- @staticmethod
- def IsTypedVector(type_):
- return (
- Type.VECTOR_INT <= type_ <= Type.VECTOR_STRING_DEPRECATED
- or type_ == Type.VECTOR_BOOL
- )
- @staticmethod
- def IsTypedVectorElementType(type_):
- return Type.INT <= type_ <= Type.STRING or type_ == Type.BOOL
- @staticmethod
- def ToTypedVectorElementType(type_):
- if not Type.IsTypedVector(type_):
- raise ValueError('must be typed vector type')
- return Type(type_ - Type.VECTOR_INT + Type.INT)
- @staticmethod
- def IsFixedTypedVector(type_):
- return Type.VECTOR_INT2 <= type_ <= Type.VECTOR_FLOAT4
- @staticmethod
- def IsFixedTypedVectorElementType(type_):
- return Type.INT <= type_ <= Type.FLOAT
- @staticmethod
- def ToFixedTypedVectorElementType(type_):
- if not Type.IsFixedTypedVector(type_):
- raise ValueError('must be fixed typed vector type')
- # 3 types each, starting from length 2.
- fixed_type = type_ - Type.VECTOR_INT2
- return Type(fixed_type % 3 + Type.INT), fixed_type // 3 + 2
- @staticmethod
- def ToTypedVector(element_type, fixed_len=0):
- """Converts element type to corresponding vector type.
- Args:
- element_type: vector element type
- fixed_len: number of elements: 0 for typed vector; 2, 3, or 4 for fixed
- typed vector.
- Returns:
- Typed vector type or fixed typed vector type.
- """
- if fixed_len == 0:
- if not Type.IsTypedVectorElementType(element_type):
- raise ValueError('must be typed vector element type')
- else:
- if not Type.IsFixedTypedVectorElementType(element_type):
- raise ValueError('must be fixed typed vector element type')
- offset = element_type - Type.INT
- if fixed_len == 0:
- return Type(offset + Type.VECTOR_INT) # TypedVector
- elif fixed_len == 2:
- return Type(offset + Type.VECTOR_INT2) # FixedTypedVector
- elif fixed_len == 3:
- return Type(offset + Type.VECTOR_INT3) # FixedTypedVector
- elif fixed_len == 4:
- return Type(offset + Type.VECTOR_INT4) # FixedTypedVector
- else:
- raise ValueError('unsupported fixed_len: %s' % fixed_len)
- class Buf:
- """Class to access underlying buffer object starting from the given offset."""
- def __init__(self, buf, offset):
- self._buf = buf
- self._offset = offset if offset >= 0 else len(buf) + offset
- self._length = len(buf) - self._offset
- def __getitem__(self, key):
- if isinstance(key, slice):
- return self._buf[_ShiftSlice(key, self._offset, self._length)]
- elif isinstance(key, int):
- return self._buf[self._offset + key]
- else:
- raise TypeError('invalid key type')
- def __setitem__(self, key, value):
- if isinstance(key, slice):
- self._buf[_ShiftSlice(key, self._offset, self._length)] = value
- elif isinstance(key, int):
- self._buf[self._offset + key] = key
- else:
- raise TypeError('invalid key type')
- def __repr__(self):
- return 'buf[%d:]' % self._offset
- def Find(self, sub):
- """Returns the lowest index where the sub subsequence is found."""
- return self._buf[self._offset :].find(sub)
- def Slice(self, offset):
- """Returns new `Buf` which starts from the given offset."""
- return Buf(self._buf, self._offset + offset)
- def Indirect(self, offset, byte_width):
- """Return new `Buf` based on the encoded offset (indirect encoding)."""
- return self.Slice(offset - _Unpack(U, self[offset : offset + byte_width]))
- class Object:
- """Base class for all non-trivial data accessors."""
- __slots__ = '_buf', '_byte_width'
- def __init__(self, buf, byte_width):
- self._buf = buf
- self._byte_width = byte_width
- @property
- def ByteWidth(self):
- return self._byte_width
- class Sized(Object):
- """Base class for all data accessors which need to read encoded size."""
- __slots__ = ('_size',)
- def __init__(self, buf, byte_width, size=0):
- super().__init__(buf, byte_width)
- if size == 0:
- self._size = _Unpack(U, self.SizeBytes)
- else:
- self._size = size
- @property
- def SizeBytes(self):
- return self._buf[-self._byte_width : 0]
- def __len__(self):
- return self._size
- class Blob(Sized):
- """Data accessor for the encoded blob bytes."""
- __slots__ = ()
- @property
- def Bytes(self):
- return self._buf[0 : len(self)]
- def __repr__(self):
- return 'Blob(%s, size=%d)' % (self._buf, len(self))
- class String(Sized):
- """Data accessor for the encoded string bytes."""
- __slots__ = ()
- @property
- def Bytes(self):
- return self._buf[0 : len(self)]
- def Mutate(self, value):
- """Mutates underlying string bytes in place.
- Args:
- value: New string to replace the existing one. New string must have less
- or equal UTF-8-encoded bytes than the existing one to successfully
- mutate underlying byte buffer.
- Returns:
- Whether the value was mutated or not.
- """
- encoded = value.encode('utf-8')
- n = len(encoded)
- if n <= len(self):
- self._buf[-self._byte_width : 0] = _Pack(U, n, self._byte_width)
- self._buf[0:n] = encoded
- self._buf[n : len(self)] = bytearray(len(self) - n)
- return True
- return False
- def __str__(self):
- return self.Bytes.decode('utf-8')
- def __repr__(self):
- return 'String(%s, size=%d)' % (self._buf, len(self))
- class Key(Object):
- """Data accessor for the encoded key bytes."""
- __slots__ = ()
- def __init__(self, buf, byte_width):
- assert byte_width == 1
- super().__init__(buf, byte_width)
- @property
- def Bytes(self):
- return self._buf[0 : len(self)]
- def __len__(self):
- return self._buf.Find(0)
- def __str__(self):
- return self.Bytes.decode('ascii')
- def __repr__(self):
- return 'Key(%s, size=%d)' % (self._buf, len(self))
- class Vector(Sized):
- """Data accessor for the encoded vector bytes."""
- __slots__ = ()
- def __getitem__(self, index):
- if index < 0 or index >= len(self):
- raise IndexError(
- 'vector index %s is out of [0, %d) range' % (index, len(self))
- )
- packed_type = self._buf[len(self) * self._byte_width + index]
- buf = self._buf.Slice(index * self._byte_width)
- return Ref.PackedType(buf, self._byte_width, packed_type)
- @property
- def Value(self):
- """Returns the underlying encoded data as a list object."""
- return [e.Value for e in self]
- def __repr__(self):
- return 'Vector(%s, byte_width=%d, size=%d)' % (
- self._buf,
- self._byte_width,
- self._size,
- )
- class TypedVector(Sized):
- """Data accessor for the encoded typed vector or fixed typed vector bytes."""
- __slots__ = '_element_type', '_size'
- def __init__(self, buf, byte_width, element_type, size=0):
- super().__init__(buf, byte_width, size)
- if element_type == Type.STRING:
- # These can't be accessed as strings, since we don't know the bit-width
- # of the size field, see the declaration of
- # FBT_VECTOR_STRING_DEPRECATED above for details.
- # We change the type here to be keys, which are a subtype of strings,
- # and will ignore the size field. This will truncate strings with
- # embedded nulls.
- element_type = Type.KEY
- self._element_type = element_type
- @property
- def Bytes(self):
- return self._buf[: self._byte_width * len(self)]
- @property
- def ElementType(self):
- return self._element_type
- def __getitem__(self, index):
- if index < 0 or index >= len(self):
- raise IndexError(
- 'vector index %s is out of [0, %d) range' % (index, len(self))
- )
- buf = self._buf.Slice(index * self._byte_width)
- return Ref(buf, self._byte_width, 1, self._element_type)
- @property
- def Value(self):
- """Returns underlying data as list object."""
- if not self:
- return []
- if self._element_type is Type.BOOL:
- return [bool(e) for e in _UnpackVector(U, self.Bytes, len(self))]
- elif self._element_type is Type.INT:
- return list(_UnpackVector(I, self.Bytes, len(self)))
- elif self._element_type is Type.UINT:
- return list(_UnpackVector(U, self.Bytes, len(self)))
- elif self._element_type is Type.FLOAT:
- return list(_UnpackVector(F, self.Bytes, len(self)))
- elif self._element_type is Type.KEY:
- return [e.AsKey for e in self]
- elif self._element_type is Type.STRING:
- return [e.AsString for e in self]
- else:
- raise TypeError('unsupported element_type: %s' % self._element_type)
- def __repr__(self):
- return 'TypedVector(%s, byte_width=%d, element_type=%s, size=%d)' % (
- self._buf,
- self._byte_width,
- self._element_type,
- self._size,
- )
- class Map(Vector):
- """Data accessor for the encoded map bytes."""
- @staticmethod
- def CompareKeys(a, b):
- if isinstance(a, Ref):
- a = a.AsKeyBytes
- if isinstance(b, Ref):
- b = b.AsKeyBytes
- return a < b
- def __getitem__(self, key):
- if isinstance(key, int):
- return super().__getitem__(key)
- index = _BinarySearch(self.Keys, key.encode('ascii'), self.CompareKeys)
- if index != -1:
- return super().__getitem__(index)
- raise KeyError(key)
- @property
- def Keys(self):
- byte_width = _Unpack(
- U, self._buf[-2 * self._byte_width : -self._byte_width]
- )
- buf = self._buf.Indirect(-3 * self._byte_width, self._byte_width)
- return TypedVector(buf, byte_width, Type.KEY)
- @property
- def Values(self):
- return Vector(self._buf, self._byte_width)
- @property
- def Value(self):
- return {k.Value: v.Value for k, v in zip(self.Keys, self.Values)}
- def __repr__(self):
- return 'Map(%s, size=%d)' % (self._buf, len(self))
- class Ref:
- """Data accessor for the encoded data bytes."""
- __slots__ = '_buf', '_parent_width', '_byte_width', '_type'
- @staticmethod
- def PackedType(buf, parent_width, packed_type):
- byte_width, type_ = Type.Unpack(packed_type)
- return Ref(buf, parent_width, byte_width, type_)
- def __init__(self, buf, parent_width, byte_width, type_):
- self._buf = buf
- self._parent_width = parent_width
- self._byte_width = byte_width
- self._type = type_
- def __repr__(self):
- return 'Ref(%s, parent_width=%d, byte_width=%d, type_=%s)' % (
- self._buf,
- self._parent_width,
- self._byte_width,
- self._type,
- )
- @property
- def _Bytes(self):
- return self._buf[: self._parent_width]
- def _ConvertError(self, target_type):
- raise TypeError('cannot convert %s to %s' % (self._type, target_type))
- def _Indirect(self):
- return self._buf.Indirect(0, self._parent_width)
- @property
- def IsNull(self):
- return self._type is Type.NULL
- @property
- def IsBool(self):
- return self._type is Type.BOOL
- @property
- def AsBool(self):
- if self._type is Type.BOOL:
- return bool(_Unpack(U, self._Bytes))
- else:
- return self.AsInt != 0
- def MutateBool(self, value):
- """Mutates underlying boolean value bytes in place.
- Args:
- value: New boolean value.
- Returns:
- Whether the value was mutated or not.
- """
- return self.IsBool and _Mutate(
- U, self._buf, value, self._parent_width, BitWidth.W8
- )
- @property
- def IsNumeric(self):
- return self.IsInt or self.IsFloat
- @property
- def IsInt(self):
- return self._type in (
- Type.INT,
- Type.INDIRECT_INT,
- Type.UINT,
- Type.INDIRECT_UINT,
- )
- @property
- def AsInt(self):
- """Returns current reference as integer value."""
- if self.IsNull:
- return 0
- elif self.IsBool:
- return int(self.AsBool)
- elif self._type is Type.INT:
- return _Unpack(I, self._Bytes)
- elif self._type is Type.INDIRECT_INT:
- return _Unpack(I, self._Indirect()[: self._byte_width])
- if self._type is Type.UINT:
- return _Unpack(U, self._Bytes)
- elif self._type is Type.INDIRECT_UINT:
- return _Unpack(U, self._Indirect()[: self._byte_width])
- elif self.IsString:
- return len(self.AsString)
- elif self.IsKey:
- return len(self.AsKey)
- elif self.IsBlob:
- return len(self.AsBlob)
- elif self.IsVector:
- return len(self.AsVector)
- elif self.IsTypedVector:
- return len(self.AsTypedVector)
- elif self.IsFixedTypedVector:
- return len(self.AsFixedTypedVector)
- else:
- raise self._ConvertError(Type.INT)
- def MutateInt(self, value):
- """Mutates underlying integer value bytes in place.
- Args:
- value: New integer value. It must fit to the byte size of the existing
- encoded value.
- Returns:
- Whether the value was mutated or not.
- """
- if self._type is Type.INT:
- return _Mutate(I, self._buf, value, self._parent_width, BitWidth.I(value))
- elif self._type is Type.INDIRECT_INT:
- return _Mutate(
- I, self._Indirect(), value, self._byte_width, BitWidth.I(value)
- )
- elif self._type is Type.UINT:
- return _Mutate(U, self._buf, value, self._parent_width, BitWidth.U(value))
- elif self._type is Type.INDIRECT_UINT:
- return _Mutate(
- U, self._Indirect(), value, self._byte_width, BitWidth.U(value)
- )
- else:
- return False
- @property
- def IsFloat(self):
- return self._type in (Type.FLOAT, Type.INDIRECT_FLOAT)
- @property
- def AsFloat(self):
- """Returns current reference as floating point value."""
- if self.IsNull:
- return 0.0
- elif self.IsBool:
- return float(self.AsBool)
- elif self.IsInt:
- return float(self.AsInt)
- elif self._type is Type.FLOAT:
- return _Unpack(F, self._Bytes)
- elif self._type is Type.INDIRECT_FLOAT:
- return _Unpack(F, self._Indirect()[: self._byte_width])
- elif self.IsString:
- return float(self.AsString)
- elif self.IsVector:
- return float(len(self.AsVector))
- elif self.IsTypedVector():
- return float(len(self.AsTypedVector))
- elif self.IsFixedTypedVector():
- return float(len(self.FixedTypedVector))
- else:
- raise self._ConvertError(Type.FLOAT)
- def MutateFloat(self, value):
- """Mutates underlying floating point value bytes in place.
- Args:
- value: New float value. It must fit to the byte size of the existing
- encoded value.
- Returns:
- Whether the value was mutated or not.
- """
- if self._type is Type.FLOAT:
- return _Mutate(
- F,
- self._buf,
- value,
- self._parent_width,
- BitWidth.B(self._parent_width),
- )
- elif self._type is Type.INDIRECT_FLOAT:
- return _Mutate(
- F,
- self._Indirect(),
- value,
- self._byte_width,
- BitWidth.B(self._byte_width),
- )
- else:
- return False
- @property
- def IsKey(self):
- return self._type is Type.KEY
- @property
- def AsKeyBytes(self):
- if self.IsKey:
- return Key(self._Indirect(), self._byte_width).Bytes
- else:
- raise self._ConvertError(Type.KEY)
- @property
- def AsKey(self):
- if self.IsKey:
- return str(Key(self._Indirect(), self._byte_width))
- else:
- raise self._ConvertError(Type.KEY)
- @property
- def IsString(self):
- return self._type is Type.STRING
- @property
- def AsStringBytes(self):
- if self.IsString:
- return String(self._Indirect(), self._byte_width).Bytes
- elif self.IsKey:
- return self.AsKeyBytes
- else:
- raise self._ConvertError(Type.STRING)
- @property
- def AsString(self):
- if self.IsString:
- return str(String(self._Indirect(), self._byte_width))
- elif self.IsKey:
- return self.AsKey
- else:
- raise self._ConvertError(Type.STRING)
- def MutateString(self, value):
- return String(self._Indirect(), self._byte_width).Mutate(value)
- @property
- def IsBlob(self):
- return self._type is Type.BLOB
- @property
- def AsBlob(self):
- if self.IsBlob:
- return Blob(self._Indirect(), self._byte_width).Bytes
- else:
- raise self._ConvertError(Type.BLOB)
- @property
- def IsAnyVector(self):
- return self.IsVector or self.IsTypedVector or self.IsFixedTypedVector()
- @property
- def IsVector(self):
- return self._type in (Type.VECTOR, Type.MAP)
- @property
- def AsVector(self):
- if self.IsVector:
- return Vector(self._Indirect(), self._byte_width)
- else:
- raise self._ConvertError(Type.VECTOR)
- @property
- def IsTypedVector(self):
- return Type.IsTypedVector(self._type)
- @property
- def AsTypedVector(self):
- if self.IsTypedVector:
- return TypedVector(
- self._Indirect(),
- self._byte_width,
- Type.ToTypedVectorElementType(self._type),
- )
- else:
- raise self._ConvertError('TYPED_VECTOR')
- @property
- def IsFixedTypedVector(self):
- return Type.IsFixedTypedVector(self._type)
- @property
- def AsFixedTypedVector(self):
- if self.IsFixedTypedVector:
- element_type, size = Type.ToFixedTypedVectorElementType(self._type)
- return TypedVector(self._Indirect(), self._byte_width, element_type, size)
- else:
- raise self._ConvertError('FIXED_TYPED_VECTOR')
- @property
- def IsMap(self):
- return self._type is Type.MAP
- @property
- def AsMap(self):
- if self.IsMap:
- return Map(self._Indirect(), self._byte_width)
- else:
- raise self._ConvertError(Type.MAP)
- @property
- def Value(self):
- """Converts current reference to value of corresponding type.
- This is equivalent to calling `AsInt` for integer values, `AsFloat` for
- floating point values, etc.
- Returns:
- Value of corresponding type.
- """
- if self.IsNull:
- return None
- elif self.IsBool:
- return self.AsBool
- elif self.IsInt:
- return self.AsInt
- elif self.IsFloat:
- return self.AsFloat
- elif self.IsString:
- return self.AsString
- elif self.IsKey:
- return self.AsKey
- elif self.IsBlob:
- return self.AsBlob
- elif self.IsMap:
- return self.AsMap.Value
- elif self.IsVector:
- return self.AsVector.Value
- elif self.IsTypedVector:
- return self.AsTypedVector.Value
- elif self.IsFixedTypedVector:
- return self.AsFixedTypedVector.Value
- else:
- raise TypeError('cannot convert %r to value' % self)
- def _IsIterable(obj):
- try:
- iter(obj)
- return True
- except TypeError:
- return False
- class Value:
- """Class to represent given value during the encoding process."""
- @staticmethod
- def Null():
- return Value(0, Type.NULL, BitWidth.W8)
- @staticmethod
- def Bool(value):
- return Value(value, Type.BOOL, BitWidth.W8)
- @staticmethod
- def Int(value, bit_width):
- return Value(value, Type.INT, bit_width)
- @staticmethod
- def UInt(value, bit_width):
- return Value(value, Type.UINT, bit_width)
- @staticmethod
- def Float(value, bit_width):
- return Value(value, Type.FLOAT, bit_width)
- @staticmethod
- def Key(offset):
- return Value(offset, Type.KEY, BitWidth.W8)
- def __init__(self, value, type_, min_bit_width):
- self._value = value
- self._type = type_
- # For scalars: of itself, for vector: of its elements, for string: length.
- self._min_bit_width = min_bit_width
- @property
- def Value(self):
- return self._value
- @property
- def Type(self):
- return self._type
- @property
- def MinBitWidth(self):
- return self._min_bit_width
- def StoredPackedType(self, parent_bit_width=BitWidth.W8):
- return Type.Pack(self._type, self.StoredWidth(parent_bit_width))
- # We have an absolute offset, but want to store a relative offset
- # elem_index elements beyond the current buffer end. Since whether
- # the relative offset fits in a certain byte_width depends on
- # the size of the elements before it (and their alignment), we have
- # to test for each size in turn.
- def ElemWidth(self, buf_size, elem_index=0):
- if Type.IsInline(self._type):
- return self._min_bit_width
- for byte_width in 1, 2, 4, 8:
- offset_loc = (
- buf_size
- + _PaddingBytes(buf_size, byte_width)
- + elem_index * byte_width
- )
- bit_width = BitWidth.U(offset_loc - self._value)
- if byte_width == (1 << bit_width):
- return bit_width
- raise ValueError('relative offset is too big')
- def StoredWidth(self, parent_bit_width=BitWidth.W8):
- if Type.IsInline(self._type):
- return max(self._min_bit_width, parent_bit_width)
- return self._min_bit_width
- def __repr__(self):
- return 'Value(%s, %s, %s)' % (self._value, self._type, self._min_bit_width)
- def __str__(self):
- return str(self._value)
- def InMap(func):
- def wrapper(self, *args, **kwargs):
- if isinstance(args[0], str):
- self.Key(args[0])
- func(self, *args[1:], **kwargs)
- else:
- func(self, *args, **kwargs)
- return wrapper
- def InMapForString(func):
- def wrapper(self, *args):
- if len(args) == 1:
- func(self, args[0])
- elif len(args) == 2:
- self.Key(args[0])
- func(self, args[1])
- else:
- raise ValueError('invalid number of arguments')
- return wrapper
- class Pool:
- """Collection of (data, offset) pairs sorted by data for quick access."""
- def __init__(self):
- self._pool = [] # sorted list of (data, offset) tuples
- def FindOrInsert(self, data, offset):
- do = data, offset
- index = _BinarySearch(self._pool, do, lambda a, b: a[0] < b[0])
- if index != -1:
- _, offset = self._pool[index]
- return offset
- self._pool.insert(index, do)
- return None
- def Clear(self):
- self._pool = []
- @property
- def Elements(self):
- return [data for data, _ in self._pool]
- class Builder:
- """Helper class to encode structural data into flexbuffers format."""
- def __init__(
- self,
- share_strings=False,
- share_keys=True,
- force_min_bit_width=BitWidth.W8,
- ):
- self._share_strings = share_strings
- self._share_keys = share_keys
- self._force_min_bit_width = force_min_bit_width
- self._string_pool = Pool()
- self._key_pool = Pool()
- self._finished = False
- self._buf = bytearray()
- self._stack = []
- def __len__(self):
- return len(self._buf)
- @property
- def StringPool(self):
- return self._string_pool
- @property
- def KeyPool(self):
- return self._key_pool
- def Clear(self):
- self._string_pool.Clear()
- self._key_pool.Clear()
- self._finished = False
- self._buf = bytearray()
- self._stack = []
- def Finish(self):
- """Finishes encoding process and returns underlying buffer."""
- if self._finished:
- raise RuntimeError('builder has been already finished')
- # If you hit this exception, you likely have objects that were never
- # included in a parent. You need to have exactly one root to finish a
- # buffer. Check your Start/End calls are matched, and all objects are inside
- # some other object.
- if len(self._stack) != 1:
- raise RuntimeError('internal stack size must be one')
- value = self._stack[0]
- byte_width = self._Align(value.ElemWidth(len(self._buf)))
- self._WriteAny(value, byte_width=byte_width) # Root value
- self._Write(U, value.StoredPackedType(), byte_width=1) # Root type
- self._Write(U, byte_width, byte_width=1) # Root size
- self.finished = True
- return self._buf
- def _ReadKey(self, offset):
- key = self._buf[offset:]
- return key[: key.find(0)]
- def _Align(self, alignment):
- byte_width = 1 << alignment
- self._buf.extend(b'\x00' * _PaddingBytes(len(self._buf), byte_width))
- return byte_width
- def _Write(self, fmt, value, byte_width):
- self._buf.extend(_Pack(fmt, value, byte_width))
- def _WriteVector(self, fmt, values, byte_width):
- self._buf.extend(_PackVector(fmt, values, byte_width))
- def _WriteOffset(self, offset, byte_width):
- relative_offset = len(self._buf) - offset
- assert byte_width == 8 or relative_offset < (1 << (8 * byte_width))
- self._Write(U, relative_offset, byte_width)
- def _WriteAny(self, value, byte_width):
- fmt = {
- Type.NULL: U,
- Type.BOOL: U,
- Type.INT: I,
- Type.UINT: U,
- Type.FLOAT: F,
- }.get(value.Type)
- if fmt:
- self._Write(fmt, value.Value, byte_width)
- else:
- self._WriteOffset(value.Value, byte_width)
- def _WriteBlob(self, data, append_zero, type_):
- bit_width = BitWidth.U(len(data))
- byte_width = self._Align(bit_width)
- self._Write(U, len(data), byte_width)
- loc = len(self._buf)
- self._buf.extend(data)
- if append_zero:
- self._buf.append(0)
- self._stack.append(Value(loc, type_, bit_width))
- return loc
- def _WriteScalarVector(self, element_type, byte_width, elements, fixed):
- """Writes scalar vector elements to the underlying buffer."""
- bit_width = BitWidth.B(byte_width)
- # If you get this exception, you're trying to write a vector with a size
- # field that is bigger than the scalars you're trying to write (e.g. a
- # byte vector > 255 elements). For such types, write a "blob" instead.
- if BitWidth.U(len(elements)) > bit_width:
- raise ValueError('too many elements for the given byte_width')
- self._Align(bit_width)
- if not fixed:
- self._Write(U, len(elements), byte_width)
- loc = len(self._buf)
- fmt = {Type.INT: I, Type.UINT: U, Type.FLOAT: F}.get(element_type)
- if not fmt:
- raise TypeError('unsupported element_type')
- self._WriteVector(fmt, elements, byte_width)
- type_ = Type.ToTypedVector(element_type, len(elements) if fixed else 0)
- self._stack.append(Value(loc, type_, bit_width))
- return loc
- def _CreateVector(self, elements, typed, fixed, keys=None):
- """Writes vector elements to the underlying buffer."""
- length = len(elements)
- if fixed and not typed:
- raise ValueError('fixed vector must be typed')
- # Figure out smallest bit width we can store this vector with.
- bit_width = max(self._force_min_bit_width, BitWidth.U(length))
- prefix_elems = 1 # Vector size
- if keys:
- bit_width = max(bit_width, keys.ElemWidth(len(self._buf)))
- prefix_elems += 2 # Offset to the keys vector and its byte width.
- vector_type = Type.KEY
- # Check bit widths and types for all elements.
- for i, e in enumerate(elements):
- bit_width = max(bit_width, e.ElemWidth(len(self._buf), prefix_elems + i))
- if typed:
- if i == 0:
- vector_type = e.Type
- else:
- if vector_type != e.Type:
- raise RuntimeError('typed vector elements must be of the same type')
- if fixed and not Type.IsFixedTypedVectorElementType(vector_type):
- raise RuntimeError('must be fixed typed vector element type')
- byte_width = self._Align(bit_width)
- # Write vector. First the keys width/offset if available, and size.
- if keys:
- self._WriteOffset(keys.Value, byte_width)
- self._Write(U, 1 << keys.MinBitWidth, byte_width)
- if not fixed:
- self._Write(U, length, byte_width)
- # Then the actual data.
- loc = len(self._buf)
- for e in elements:
- self._WriteAny(e, byte_width)
- # Then the types.
- if not typed:
- for e in elements:
- self._buf.append(e.StoredPackedType(bit_width))
- if keys:
- type_ = Type.MAP
- else:
- if typed:
- type_ = Type.ToTypedVector(vector_type, length if fixed else 0)
- else:
- type_ = Type.VECTOR
- return Value(loc, type_, bit_width)
- def _PushIndirect(self, value, type_, bit_width):
- byte_width = self._Align(bit_width)
- loc = len(self._buf)
- fmt = {Type.INDIRECT_INT: I, Type.INDIRECT_UINT: U, Type.INDIRECT_FLOAT: F}[
- type_
- ]
- self._Write(fmt, value, byte_width)
- self._stack.append(Value(loc, type_, bit_width))
- @InMapForString
- def String(self, value):
- """Encodes string value."""
- reset_to = len(self._buf)
- encoded = value.encode('utf-8')
- loc = self._WriteBlob(encoded, append_zero=True, type_=Type.STRING)
- if self._share_strings:
- prev_loc = self._string_pool.FindOrInsert(encoded, loc)
- if prev_loc is not None:
- del self._buf[reset_to:]
- self._stack[-1]._value = loc = prev_loc # pylint: disable=protected-access
- return loc
- @InMap
- def Blob(self, value):
- """Encodes binary blob value.
- Args:
- value: A byte/bytearray value to encode
- Returns:
- Offset of the encoded value in underlying the byte buffer.
- """
- return self._WriteBlob(value, append_zero=False, type_=Type.BLOB)
- def Key(self, value):
- """Encodes key value.
- Args:
- value: A byte/bytearray/str value to encode. Byte object must not contain
- zero bytes. String object must be convertible to ASCII.
- Returns:
- Offset of the encoded value in the underlying byte buffer.
- """
- if isinstance(value, (bytes, bytearray)):
- encoded = value
- else:
- encoded = value.encode('ascii')
- if 0 in encoded:
- raise ValueError('key contains zero byte')
- loc = len(self._buf)
- self._buf.extend(encoded)
- self._buf.append(0)
- if self._share_keys:
- prev_loc = self._key_pool.FindOrInsert(encoded, loc)
- if prev_loc is not None:
- del self._buf[loc:]
- loc = prev_loc
- self._stack.append(Value.Key(loc))
- return loc
- def Null(self, key=None):
- """Encodes None value."""
- if key:
- self.Key(key)
- self._stack.append(Value.Null())
- @InMap
- def Bool(self, value):
- """Encodes boolean value.
- Args:
- value: A boolean value.
- """
- self._stack.append(Value.Bool(value))
- @InMap
- def Int(self, value, byte_width=0):
- """Encodes signed integer value.
- Args:
- value: A signed integer value.
- byte_width: Number of bytes to use: 1, 2, 4, or 8.
- """
- bit_width = BitWidth.I(value) if byte_width == 0 else BitWidth.B(byte_width)
- self._stack.append(Value.Int(value, bit_width))
- @InMap
- def IndirectInt(self, value, byte_width=0):
- """Encodes signed integer value indirectly.
- Args:
- value: A signed integer value.
- byte_width: Number of bytes to use: 1, 2, 4, or 8.
- """
- bit_width = BitWidth.I(value) if byte_width == 0 else BitWidth.B(byte_width)
- self._PushIndirect(value, Type.INDIRECT_INT, bit_width)
- @InMap
- def UInt(self, value, byte_width=0):
- """Encodes unsigned integer value.
- Args:
- value: An unsigned integer value.
- byte_width: Number of bytes to use: 1, 2, 4, or 8.
- """
- bit_width = BitWidth.U(value) if byte_width == 0 else BitWidth.B(byte_width)
- self._stack.append(Value.UInt(value, bit_width))
- @InMap
- def IndirectUInt(self, value, byte_width=0):
- """Encodes unsigned integer value indirectly.
- Args:
- value: An unsigned integer value.
- byte_width: Number of bytes to use: 1, 2, 4, or 8.
- """
- bit_width = BitWidth.U(value) if byte_width == 0 else BitWidth.B(byte_width)
- self._PushIndirect(value, Type.INDIRECT_UINT, bit_width)
- @InMap
- def Float(self, value, byte_width=0):
- """Encodes floating point value.
- Args:
- value: A floating point value.
- byte_width: Number of bytes to use: 4 or 8.
- """
- bit_width = BitWidth.F(value) if byte_width == 0 else BitWidth.B(byte_width)
- self._stack.append(Value.Float(value, bit_width))
- @InMap
- def IndirectFloat(self, value, byte_width=0):
- """Encodes floating point value indirectly.
- Args:
- value: A floating point value.
- byte_width: Number of bytes to use: 4 or 8.
- """
- bit_width = BitWidth.F(value) if byte_width == 0 else BitWidth.B(byte_width)
- self._PushIndirect(value, Type.INDIRECT_FLOAT, bit_width)
- def _StartVector(self):
- """Starts vector construction."""
- return len(self._stack)
- def _EndVector(self, start, typed, fixed):
- """Finishes vector construction by encodung its elements."""
- vec = self._CreateVector(self._stack[start:], typed, fixed)
- del self._stack[start:]
- self._stack.append(vec)
- return vec.Value
- @contextlib.contextmanager
- def Vector(self, key=None):
- if key:
- self.Key(key)
- try:
- start = self._StartVector()
- yield self
- finally:
- self._EndVector(start, typed=False, fixed=False)
- @InMap
- def VectorFromElements(self, elements):
- """Encodes sequence of any elements as a vector.
- Args:
- elements: sequence of elements, they may have different types.
- """
- with self.Vector():
- for e in elements:
- self.Add(e)
- @contextlib.contextmanager
- def TypedVector(self, key=None):
- if key:
- self.Key(key)
- try:
- start = self._StartVector()
- yield self
- finally:
- self._EndVector(start, typed=True, fixed=False)
- @InMap
- def TypedVectorFromElements(self, elements, element_type=None):
- """Encodes sequence of elements of the same type as typed vector.
- Args:
- elements: Sequence of elements, they must be of the same type.
- element_type: Suggested element type. Setting it to None means determining
- correct value automatically based on the given elements.
- """
- if isinstance(elements, array.array):
- if elements.typecode == 'f':
- self._WriteScalarVector(Type.FLOAT, 4, elements, fixed=False)
- elif elements.typecode == 'd':
- self._WriteScalarVector(Type.FLOAT, 8, elements, fixed=False)
- elif elements.typecode in ('b', 'h', 'i', 'l', 'q'):
- self._WriteScalarVector(
- Type.INT, elements.itemsize, elements, fixed=False
- )
- elif elements.typecode in ('B', 'H', 'I', 'L', 'Q'):
- self._WriteScalarVector(
- Type.UINT, elements.itemsize, elements, fixed=False
- )
- else:
- raise ValueError('unsupported array typecode: %s' % elements.typecode)
- else:
- add = self.Add if element_type is None else self.Adder(element_type)
- with self.TypedVector():
- for e in elements:
- add(e)
- @InMap
- def FixedTypedVectorFromElements(
- self, elements, element_type=None, byte_width=0
- ):
- """Encodes sequence of elements of the same type as fixed typed vector.
- Args:
- elements: Sequence of elements, they must be of the same type. Allowed
- types are `Type.INT`, `Type.UINT`, `Type.FLOAT`. Allowed number of
- elements are 2, 3, or 4.
- element_type: Suggested element type. Setting it to None means determining
- correct value automatically based on the given elements.
- byte_width: Number of bytes to use per element. For `Type.INT` and
- `Type.UINT`: 1, 2, 4, or 8. For `Type.FLOAT`: 4 or 8. Setting it to 0
- means determining correct value automatically based on the given
- elements.
- """
- if not 2 <= len(elements) <= 4:
- raise ValueError('only 2, 3, or 4 elements are supported')
- types = {type(e) for e in elements}
- if len(types) != 1:
- raise TypeError('all elements must be of the same type')
- (type_,) = types
- if element_type is None:
- element_type = {int: Type.INT, float: Type.FLOAT}.get(type_)
- if not element_type:
- raise TypeError('unsupported element_type: %s' % type_)
- if byte_width == 0:
- width = {
- Type.UINT: BitWidth.U,
- Type.INT: BitWidth.I,
- Type.FLOAT: BitWidth.F,
- }[element_type]
- byte_width = 1 << max(width(e) for e in elements)
- self._WriteScalarVector(element_type, byte_width, elements, fixed=True)
- def _StartMap(self):
- """Starts map construction."""
- return len(self._stack)
- def _EndMap(self, start):
- """Finishes map construction by encodung its elements."""
- # Interleaved keys and values on the stack.
- stack = self._stack[start:]
- if len(stack) % 2 != 0:
- raise RuntimeError('must be even number of keys and values')
- for key in stack[::2]:
- if key.Type is not Type.KEY:
- raise RuntimeError('all map keys must be of %s type' % Type.KEY)
- pairs = zip(stack[::2], stack[1::2]) # [(key, value), ...]
- pairs = sorted(pairs, key=lambda pair: self._ReadKey(pair[0].Value))
- del self._stack[start:]
- for pair in pairs:
- self._stack.extend(pair)
- keys = self._CreateVector(self._stack[start::2], typed=True, fixed=False)
- values = self._CreateVector(
- self._stack[start + 1 :: 2], typed=False, fixed=False, keys=keys
- )
- del self._stack[start:]
- self._stack.append(values)
- return values.Value
- @contextlib.contextmanager
- def Map(self, key=None):
- if key:
- self.Key(key)
- try:
- start = self._StartMap()
- yield self
- finally:
- self._EndMap(start)
- def MapFromElements(self, elements):
- start = self._StartMap()
- for k, v in elements.items():
- self.Key(k)
- self.Add(v)
- self._EndMap(start)
- def Adder(self, type_):
- return {
- Type.BOOL: self.Bool,
- Type.INT: self.Int,
- Type.INDIRECT_INT: self.IndirectInt,
- Type.UINT: self.UInt,
- Type.INDIRECT_UINT: self.IndirectUInt,
- Type.FLOAT: self.Float,
- Type.INDIRECT_FLOAT: self.IndirectFloat,
- Type.KEY: self.Key,
- Type.BLOB: self.Blob,
- Type.STRING: self.String,
- }[type_]
- @InMapForString
- def Add(self, value):
- """Encodes value of any supported type."""
- if value is None:
- self.Null()
- elif isinstance(value, bool):
- self.Bool(value)
- elif isinstance(value, int):
- self.Int(value)
- elif isinstance(value, float):
- self.Float(value)
- elif isinstance(value, str):
- self.String(value)
- elif isinstance(value, (bytes, bytearray)):
- self.Blob(value)
- elif isinstance(value, dict):
- with self.Map():
- for k, v in value.items():
- self.Key(k)
- self.Add(v)
- elif isinstance(value, array.array):
- self.TypedVectorFromElements(value)
- elif _IsIterable(value):
- self.VectorFromElements(value)
- else:
- raise TypeError('unsupported python type: %s' % type(value))
- @property
- def LastValue(self):
- return self._stack[-1]
- @InMap
- def ReuseValue(self, value):
- self._stack.append(value)
- def GetRoot(buf):
- """Returns root `Ref` object for the given buffer."""
- if len(buf) < 3:
- raise ValueError('buffer is too small')
- byte_width = buf[-1]
- return Ref.PackedType(
- Buf(buf, -(2 + byte_width)), byte_width, packed_type=buf[-2]
- )
- def Dumps(obj):
- """Returns bytearray with the encoded python object."""
- fbb = Builder()
- fbb.Add(obj)
- return fbb.Finish()
- def Loads(buf):
- """Returns python object decoded from the buffer."""
- return GetRoot(buf).Value
|