Files
wehub-resource-sync 26446540fa
Lint / lint (push) Has been cancelled
CI / MacOS (push) Has been cancelled
CI / Windows (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:36:25 +08:00

162 lines
6.6 KiB
Python

# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
# pylint: disable=invalid-name, unused-argument, too-many-lines, import-outside-toplevel
# pylint: disable=broad-exception-raised, use-list-literal
"""Tensorflow lite frontend helper to parse custom options in Flexbuffer format."""
import struct
from enum import IntEnum
class BitWidth(IntEnum):
"""Flexbuffer bit width schema from flexbuffers.h"""
BIT_WIDTH_8 = 0
BIT_WIDTH_16 = 1
BIT_WIDTH_32 = 2
BIT_WIDTH_64 = 3
class FlexBufferType(IntEnum):
"""Flexbuffer type schema from flexbuffers.h"""
FBT_NULL = 0
FBT_INT = 1
FBT_UINT = 2
FBT_FLOAT = 3
# Types above stored inline, types below store an offset.
FBT_KEY = 4
FBT_STRING = 5
FBT_INDIRECT_INT = 6
FBT_INDIRECT_UINT = 7
FBT_INDIRECT_FLOAT = 8
FBT_MAP = 9
FBT_VECTOR = 10 # Untyped.
FBT_VECTOR_INT = 11 # Typed any size (stores no type table).
FBT_VECTOR_UINT = 12
FBT_VECTOR_FLOAT = 13
FBT_VECTOR_KEY = 14
FBT_VECTOR_STRING = 15
FBT_VECTOR_INT2 = 16 # Typed tuple (no type table, no size field).
FBT_VECTOR_UINT2 = 17
FBT_VECTOR_FLOAT2 = 18
FBT_VECTOR_INT3 = 19 # Typed triple (no type table, no size field).
FBT_VECTOR_UINT3 = 20
FBT_VECTOR_FLOAT3 = 21
FBT_VECTOR_INT4 = 22 # Typed quad (no type table, no size field).
FBT_VECTOR_UINT4 = 23
FBT_VECTOR_FLOAT4 = 24
FBT_BLOB = 25
FBT_BOOL = 26
FBT_VECTOR_BOOL = 36 # To Allow the same type of conversion of type to vector type
class FlexBufferDecoder:
"""
This implements partial flexbuffer deserialization to be able
to read custom options. It is not intended to be a general
purpose flexbuffer deserializer and as such only supports a
limited number of types and assumes the data is a flat map.
"""
def __init__(self, buffer):
self.buffer = buffer
def indirect_jump(self, offset, byte_width):
"""Helper function to read the offset value and jump"""
unpack_str = {1: "<B", 2: "<H", 4: "<I", 8: "<Q"}[byte_width]
back_jump = struct.unpack(unpack_str, self.buffer[offset : offset + byte_width])[0]
return offset - back_jump
def decode_keys(self, end, size, byte_width):
"""Decodes the flexbuffer type vector. Map keys are stored in this form"""
# Keys are strings here. The format is all strings separated by null, followed by back
# offsets for each of the string. For example, (str1)\0(str1)\0(offset1)(offset2) The end
# pointer is pointing at the end of all strings
keys = list()
for i in range(0, size):
offset_pos = end + i * byte_width
start_index = self.indirect_jump(offset_pos, byte_width)
str_size = self.buffer[start_index:].find(b"\0")
assert str_size != -1
s = self.buffer[start_index : start_index + str_size].decode("utf-8")
keys.append(s)
return keys
def decode_vector(self, end, size, byte_width):
"""Decodes the flexbuffer vector"""
# Each entry in the vector can have different datatype. Each entry is of fixed length. The
# format is a sequence of all values followed by a sequence of datatype of all values. For
# example - (4)(3.56)(int)(float) The end here points to the start of the values.
# Each type byte contains: (type << 2) | bit_width, where bit_width determines actual size.
values = list()
for i in range(0, size):
value_type_pos = end + size * byte_width + i
value_type_packed = self.buffer[value_type_pos]
value_type = FlexBufferType(value_type_packed >> 2)
value_bit_width = BitWidth(value_type_packed & 3)
value_byte_width = 1 << value_bit_width
value_bytes = self.buffer[
end + i * byte_width : end + i * byte_width + value_byte_width
]
if value_type == FlexBufferType.FBT_BOOL:
value = bool(value_bytes[0])
elif value_type == FlexBufferType.FBT_INT:
fmt = {1: "<b", 2: "<h", 4: "<i", 8: "<q"}[value_byte_width]
value = struct.unpack(fmt, value_bytes)[0]
elif value_type == FlexBufferType.FBT_UINT:
fmt = {1: "<B", 2: "<H", 4: "<I", 8: "<Q"}[value_byte_width]
value = struct.unpack(fmt, value_bytes)[0]
elif value_type == FlexBufferType.FBT_FLOAT:
fmt = {4: "<f", 8: "<d"}[value_byte_width]
value = struct.unpack(fmt, value_bytes)[0]
else:
raise Exception
values.append(value)
return values
def decode_map(self, end, byte_width, parent_byte_width):
"""Decodes the flexbuffer map and returns a dict"""
mid_loc = self.indirect_jump(end, parent_byte_width)
size_fmt = {1: "<b", 2: "<h", 4: "<i", 8: "<q"}[byte_width]
map_size = struct.unpack(size_fmt, self.buffer[mid_loc - byte_width : mid_loc])[0]
# Find keys
keys_offset = mid_loc - byte_width * 3
keys_end = self.indirect_jump(keys_offset, byte_width)
keys = self.decode_keys(keys_end, map_size, 1)
# Find values
values_end = self.indirect_jump(end, parent_byte_width)
values = self.decode_vector(values_end, map_size, byte_width)
return dict(zip(keys, values))
def decode(self):
"""Decode the buffer. Decoding is partially implemented"""
root_end = len(self.buffer) - 1
root_byte_width = self.buffer[root_end]
root_end -= 1
root_packed_type = self.buffer[root_end]
root_end -= root_byte_width
root_type = FlexBufferType(root_packed_type >> 2)
byte_width = 1 << BitWidth(root_packed_type & 3)
if root_type == FlexBufferType.FBT_MAP:
return self.decode_map(root_end, byte_width, root_byte_width)
raise NotImplementedError("Flexbuffer Decoding is partially imlpemented.")