zlmdb 25.10.1__cp313-cp313-manylinux_2_34_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of zlmdb might be problematic. Click here for more details.
- flatbuffers/__init__.py +19 -0
- flatbuffers/_version.py +17 -0
- flatbuffers/builder.py +776 -0
- flatbuffers/compat.py +86 -0
- flatbuffers/encode.py +42 -0
- flatbuffers/flexbuffers.py +1527 -0
- flatbuffers/number_types.py +181 -0
- flatbuffers/packer.py +42 -0
- flatbuffers/reflection/AdvancedFeatures.py +10 -0
- flatbuffers/reflection/BaseType.py +24 -0
- flatbuffers/reflection/Enum.py +169 -0
- flatbuffers/reflection/EnumVal.py +96 -0
- flatbuffers/reflection/Field.py +208 -0
- flatbuffers/reflection/KeyValue.py +56 -0
- flatbuffers/reflection/Object.py +175 -0
- flatbuffers/reflection/RPCCall.py +131 -0
- flatbuffers/reflection/Schema.py +206 -0
- flatbuffers/reflection/SchemaFile.py +77 -0
- flatbuffers/reflection/Service.py +145 -0
- flatbuffers/reflection/Type.py +98 -0
- flatbuffers/reflection/__init__.py +0 -0
- flatbuffers/table.py +129 -0
- flatbuffers/util.py +43 -0
- zlmdb/__init__.py +312 -0
- zlmdb/_database.py +990 -0
- zlmdb/_errors.py +31 -0
- zlmdb/_meta.py +27 -0
- zlmdb/_pmap.py +1667 -0
- zlmdb/_schema.py +137 -0
- zlmdb/_transaction.py +181 -0
- zlmdb/_types.py +1596 -0
- zlmdb/_version.py +27 -0
- zlmdb/cli.py +41 -0
- zlmdb/flatbuffers/__init__.py +5 -0
- zlmdb/flatbuffers/reflection/AdvancedFeatures.py +10 -0
- zlmdb/flatbuffers/reflection/BaseType.py +25 -0
- zlmdb/flatbuffers/reflection/Enum.py +252 -0
- zlmdb/flatbuffers/reflection/EnumVal.py +144 -0
- zlmdb/flatbuffers/reflection/Field.py +325 -0
- zlmdb/flatbuffers/reflection/KeyValue.py +84 -0
- zlmdb/flatbuffers/reflection/Object.py +260 -0
- zlmdb/flatbuffers/reflection/RPCCall.py +195 -0
- zlmdb/flatbuffers/reflection/Schema.py +301 -0
- zlmdb/flatbuffers/reflection/SchemaFile.py +112 -0
- zlmdb/flatbuffers/reflection/Service.py +213 -0
- zlmdb/flatbuffers/reflection/Type.py +148 -0
- zlmdb/flatbuffers/reflection/__init__.py +0 -0
- zlmdb/flatbuffers/reflection.fbs +152 -0
- zlmdb/lmdb/__init__.py +37 -0
- zlmdb/lmdb/__main__.py +25 -0
- zlmdb/lmdb/_config.py +10 -0
- zlmdb/lmdb/_lmdb_cffi.cpython-313-x86_64-linux-gnu.so +0 -0
- zlmdb/lmdb/cffi.py +2606 -0
- zlmdb/lmdb/tool.py +670 -0
- zlmdb/tests/lmdb/__init__.py +0 -0
- zlmdb/tests/lmdb/address_book.py +287 -0
- zlmdb/tests/lmdb/crash_test.py +339 -0
- zlmdb/tests/lmdb/cursor_test.py +333 -0
- zlmdb/tests/lmdb/env_test.py +919 -0
- zlmdb/tests/lmdb/getmulti_test.py +92 -0
- zlmdb/tests/lmdb/iteration_test.py +258 -0
- zlmdb/tests/lmdb/package_test.py +70 -0
- zlmdb/tests/lmdb/test_lmdb.py +188 -0
- zlmdb/tests/lmdb/testlib.py +185 -0
- zlmdb/tests/lmdb/tool_test.py +60 -0
- zlmdb/tests/lmdb/txn_test.py +575 -0
- zlmdb/tests/orm/MNodeLog.py +853 -0
- zlmdb/tests/orm/__init__.py +0 -0
- zlmdb/tests/orm/_schema_fbs.py +215 -0
- zlmdb/tests/orm/_schema_mnode_log.py +1201 -0
- zlmdb/tests/orm/_schema_py2.py +250 -0
- zlmdb/tests/orm/_schema_py3.py +307 -0
- zlmdb/tests/orm/_test_flatbuffers.py +144 -0
- zlmdb/tests/orm/_test_serialization.py +144 -0
- zlmdb/tests/orm/test_basic.py +217 -0
- zlmdb/tests/orm/test_etcd.py +275 -0
- zlmdb/tests/orm/test_pmap_indexes.py +466 -0
- zlmdb/tests/orm/test_pmap_types.py +90 -0
- zlmdb/tests/orm/test_pmaps.py +295 -0
- zlmdb/tests/orm/test_select.py +619 -0
- zlmdb-25.10.1.dist-info/METADATA +264 -0
- zlmdb-25.10.1.dist-info/RECORD +87 -0
- zlmdb-25.10.1.dist-info/WHEEL +5 -0
- zlmdb-25.10.1.dist-info/entry_points.txt +2 -0
- zlmdb-25.10.1.dist-info/licenses/LICENSE +137 -0
- zlmdb-25.10.1.dist-info/licenses/NOTICE +41 -0
- zlmdb-25.10.1.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,1527 @@
|
|
|
1
|
+
# Lint as: python3
|
|
2
|
+
# Copyright 2020 Google Inc. All rights reserved.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
"""Implementation of FlexBuffers binary format.
|
|
16
|
+
|
|
17
|
+
For more info check https://google.github.io/flatbuffers/flexbuffers.html and
|
|
18
|
+
corresponding C++ implementation at
|
|
19
|
+
https://github.com/google/flatbuffers/blob/master/include/flatbuffers/flexbuffers.h
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
# pylint: disable=invalid-name
|
|
23
|
+
# TODO(dkovalev): Add type hints everywhere, so tools like pytypes could work.
|
|
24
|
+
|
|
25
|
+
import array
|
|
26
|
+
import contextlib
|
|
27
|
+
import enum
|
|
28
|
+
import struct
|
|
29
|
+
|
|
30
|
+
__all__ = ('Type', 'Builder', 'GetRoot', 'Dumps', 'Loads')
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class BitWidth(enum.IntEnum):
|
|
34
|
+
"""Supported bit widths of value types.
|
|
35
|
+
|
|
36
|
+
These are used in the lower 2 bits of a type field to determine the size of
|
|
37
|
+
the elements (and or size field) of the item pointed to (e.g. vector).
|
|
38
|
+
"""
|
|
39
|
+
W8 = 0 # 2^0 = 1 byte
|
|
40
|
+
W16 = 1 # 2^1 = 2 bytes
|
|
41
|
+
W32 = 2 # 2^2 = 4 bytes
|
|
42
|
+
W64 = 3 # 2^3 = 8 bytes
|
|
43
|
+
|
|
44
|
+
@staticmethod
|
|
45
|
+
def U(value):
|
|
46
|
+
"""Returns the minimum `BitWidth` to encode unsigned integer value."""
|
|
47
|
+
assert value >= 0
|
|
48
|
+
|
|
49
|
+
if value < (1 << 8):
|
|
50
|
+
return BitWidth.W8
|
|
51
|
+
elif value < (1 << 16):
|
|
52
|
+
return BitWidth.W16
|
|
53
|
+
elif value < (1 << 32):
|
|
54
|
+
return BitWidth.W32
|
|
55
|
+
elif value < (1 << 64):
|
|
56
|
+
return BitWidth.W64
|
|
57
|
+
else:
|
|
58
|
+
raise ValueError('value is too big to encode: %s' % value)
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def I(value):
|
|
62
|
+
"""Returns the minimum `BitWidth` to encode signed integer value."""
|
|
63
|
+
# -2^(n-1) <= value < 2^(n-1)
|
|
64
|
+
# -2^n <= 2 * value < 2^n
|
|
65
|
+
# 2 * value < 2^n, when value >= 0 or 2 * (-value) <= 2^n, when value < 0
|
|
66
|
+
# 2 * value < 2^n, when value >= 0 or 2 * (-value) - 1 < 2^n, when value < 0
|
|
67
|
+
#
|
|
68
|
+
# if value >= 0:
|
|
69
|
+
# return BitWidth.U(2 * value)
|
|
70
|
+
# else:
|
|
71
|
+
# return BitWidth.U(2 * (-value) - 1) # ~x = -x - 1
|
|
72
|
+
value *= 2
|
|
73
|
+
return BitWidth.U(value if value >= 0 else ~value)
|
|
74
|
+
|
|
75
|
+
@staticmethod
|
|
76
|
+
def F(value):
|
|
77
|
+
"""Returns the `BitWidth` to encode floating point value."""
|
|
78
|
+
if struct.unpack('f', struct.pack('f', value))[0] == value:
|
|
79
|
+
return BitWidth.W32
|
|
80
|
+
return BitWidth.W64
|
|
81
|
+
|
|
82
|
+
@staticmethod
|
|
83
|
+
def B(byte_width):
|
|
84
|
+
return {
|
|
85
|
+
1: BitWidth.W8,
|
|
86
|
+
2: BitWidth.W16,
|
|
87
|
+
4: BitWidth.W32,
|
|
88
|
+
8: BitWidth.W64
|
|
89
|
+
}[byte_width]
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
I = {1: 'b', 2: 'h', 4: 'i', 8: 'q'} # Integer formats
|
|
93
|
+
U = {1: 'B', 2: 'H', 4: 'I', 8: 'Q'} # Unsigned integer formats
|
|
94
|
+
F = {4: 'f', 8: 'd'} # Floating point formats
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _Unpack(fmt, buf):
|
|
98
|
+
return struct.unpack(fmt[len(buf)], buf)[0]
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _UnpackVector(fmt, buf, length):
|
|
102
|
+
byte_width = len(buf) // length
|
|
103
|
+
return struct.unpack('%d%s' % (length, fmt[byte_width]), buf)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _Pack(fmt, value, byte_width):
|
|
107
|
+
return struct.pack(fmt[byte_width], value)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _PackVector(fmt, values, byte_width):
|
|
111
|
+
return struct.pack('%d%s' % (len(values), fmt[byte_width]), *values)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _Mutate(fmt, buf, value, byte_width, value_bit_width):
|
|
115
|
+
if (1 << value_bit_width) <= byte_width:
|
|
116
|
+
buf[:byte_width] = _Pack(fmt, value, byte_width)
|
|
117
|
+
return True
|
|
118
|
+
return False
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
# Computes how many bytes you'd have to pad to be able to write an
|
|
122
|
+
# "scalar_size" scalar if the buffer had grown to "buf_size",
|
|
123
|
+
# "scalar_size" is a power of two.
|
|
124
|
+
def _PaddingBytes(buf_size, scalar_size):
|
|
125
|
+
# ((buf_size + (scalar_size - 1)) // scalar_size) * scalar_size - buf_size
|
|
126
|
+
return -buf_size & (scalar_size - 1)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _ShiftSlice(s, offset, length):
|
|
130
|
+
start = offset + (0 if s.start is None else s.start)
|
|
131
|
+
stop = offset + (length if s.stop is None else s.stop)
|
|
132
|
+
return slice(start, stop, s.step)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
# https://en.cppreference.com/w/cpp/algorithm/lower_bound
|
|
136
|
+
def _LowerBound(values, value, pred):
|
|
137
|
+
"""Implementation of C++ std::lower_bound() algorithm."""
|
|
138
|
+
first, last = 0, len(values)
|
|
139
|
+
count = last - first
|
|
140
|
+
while count > 0:
|
|
141
|
+
i = first
|
|
142
|
+
step = count // 2
|
|
143
|
+
i += step
|
|
144
|
+
if pred(values[i], value):
|
|
145
|
+
i += 1
|
|
146
|
+
first = i
|
|
147
|
+
count -= step + 1
|
|
148
|
+
else:
|
|
149
|
+
count = step
|
|
150
|
+
return first
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
# https://en.cppreference.com/w/cpp/algorithm/binary_search
|
|
154
|
+
def _BinarySearch(values, value, pred=lambda x, y: x < y):
|
|
155
|
+
"""Implementation of C++ std::binary_search() algorithm."""
|
|
156
|
+
index = _LowerBound(values, value, pred)
|
|
157
|
+
if index != len(values) and not pred(value, values[index]):
|
|
158
|
+
return index
|
|
159
|
+
return -1
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class Type(enum.IntEnum):
|
|
163
|
+
"""Supported types of encoded data.
|
|
164
|
+
|
|
165
|
+
These are used as the upper 6 bits of a type field to indicate the actual
|
|
166
|
+
type.
|
|
167
|
+
"""
|
|
168
|
+
NULL = 0
|
|
169
|
+
INT = 1
|
|
170
|
+
UINT = 2
|
|
171
|
+
FLOAT = 3
|
|
172
|
+
# Types above stored inline, types below store an offset.
|
|
173
|
+
KEY = 4
|
|
174
|
+
STRING = 5
|
|
175
|
+
INDIRECT_INT = 6
|
|
176
|
+
INDIRECT_UINT = 7
|
|
177
|
+
INDIRECT_FLOAT = 8
|
|
178
|
+
MAP = 9
|
|
179
|
+
VECTOR = 10 # Untyped.
|
|
180
|
+
|
|
181
|
+
VECTOR_INT = 11 # Typed any size (stores no type table).
|
|
182
|
+
VECTOR_UINT = 12
|
|
183
|
+
VECTOR_FLOAT = 13
|
|
184
|
+
VECTOR_KEY = 14
|
|
185
|
+
# DEPRECATED, use VECTOR or VECTOR_KEY instead.
|
|
186
|
+
# Read test.cpp/FlexBuffersDeprecatedTest() for details on why.
|
|
187
|
+
VECTOR_STRING_DEPRECATED = 15
|
|
188
|
+
|
|
189
|
+
VECTOR_INT2 = 16 # Typed tuple (no type table, no size field).
|
|
190
|
+
VECTOR_UINT2 = 17
|
|
191
|
+
VECTOR_FLOAT2 = 18
|
|
192
|
+
VECTOR_INT3 = 19 # Typed triple (no type table, no size field).
|
|
193
|
+
VECTOR_UINT3 = 20
|
|
194
|
+
VECTOR_FLOAT3 = 21
|
|
195
|
+
VECTOR_INT4 = 22 # Typed quad (no type table, no size field).
|
|
196
|
+
VECTOR_UINT4 = 23
|
|
197
|
+
VECTOR_FLOAT4 = 24
|
|
198
|
+
|
|
199
|
+
BLOB = 25
|
|
200
|
+
BOOL = 26
|
|
201
|
+
VECTOR_BOOL = 36 # To do the same type of conversion of type to vector type
|
|
202
|
+
|
|
203
|
+
@staticmethod
|
|
204
|
+
def Pack(type_, bit_width):
|
|
205
|
+
return (int(type_) << 2) | bit_width
|
|
206
|
+
|
|
207
|
+
@staticmethod
|
|
208
|
+
def Unpack(packed_type):
|
|
209
|
+
return 1 << (packed_type & 0b11), Type(packed_type >> 2)
|
|
210
|
+
|
|
211
|
+
@staticmethod
|
|
212
|
+
def IsInline(type_):
|
|
213
|
+
return type_ <= Type.FLOAT or type_ == Type.BOOL
|
|
214
|
+
|
|
215
|
+
@staticmethod
|
|
216
|
+
def IsTypedVector(type_):
|
|
217
|
+
return Type.VECTOR_INT <= type_ <= Type.VECTOR_STRING_DEPRECATED or \
|
|
218
|
+
type_ == Type.VECTOR_BOOL
|
|
219
|
+
|
|
220
|
+
@staticmethod
|
|
221
|
+
def IsTypedVectorElementType(type_):
|
|
222
|
+
return Type.INT <= type_ <= Type.STRING or type_ == Type.BOOL
|
|
223
|
+
|
|
224
|
+
@staticmethod
|
|
225
|
+
def ToTypedVectorElementType(type_):
|
|
226
|
+
if not Type.IsTypedVector(type_):
|
|
227
|
+
raise ValueError('must be typed vector type')
|
|
228
|
+
|
|
229
|
+
return Type(type_ - Type.VECTOR_INT + Type.INT)
|
|
230
|
+
|
|
231
|
+
@staticmethod
|
|
232
|
+
def IsFixedTypedVector(type_):
|
|
233
|
+
return Type.VECTOR_INT2 <= type_ <= Type.VECTOR_FLOAT4
|
|
234
|
+
|
|
235
|
+
@staticmethod
|
|
236
|
+
def IsFixedTypedVectorElementType(type_):
|
|
237
|
+
return Type.INT <= type_ <= Type.FLOAT
|
|
238
|
+
|
|
239
|
+
@staticmethod
|
|
240
|
+
def ToFixedTypedVectorElementType(type_):
|
|
241
|
+
if not Type.IsFixedTypedVector(type_):
|
|
242
|
+
raise ValueError('must be fixed typed vector type')
|
|
243
|
+
|
|
244
|
+
# 3 types each, starting from length 2.
|
|
245
|
+
fixed_type = type_ - Type.VECTOR_INT2
|
|
246
|
+
return Type(fixed_type % 3 + Type.INT), fixed_type // 3 + 2
|
|
247
|
+
|
|
248
|
+
@staticmethod
|
|
249
|
+
def ToTypedVector(element_type, fixed_len=0):
|
|
250
|
+
"""Converts element type to corresponding vector type.
|
|
251
|
+
|
|
252
|
+
Args:
|
|
253
|
+
element_type: vector element type
|
|
254
|
+
fixed_len: number of elements: 0 for typed vector; 2, 3, or 4 for fixed
|
|
255
|
+
typed vector.
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
Typed vector type or fixed typed vector type.
|
|
259
|
+
"""
|
|
260
|
+
if fixed_len == 0:
|
|
261
|
+
if not Type.IsTypedVectorElementType(element_type):
|
|
262
|
+
raise ValueError('must be typed vector element type')
|
|
263
|
+
else:
|
|
264
|
+
if not Type.IsFixedTypedVectorElementType(element_type):
|
|
265
|
+
raise ValueError('must be fixed typed vector element type')
|
|
266
|
+
|
|
267
|
+
offset = element_type - Type.INT
|
|
268
|
+
if fixed_len == 0:
|
|
269
|
+
return Type(offset + Type.VECTOR_INT) # TypedVector
|
|
270
|
+
elif fixed_len == 2:
|
|
271
|
+
return Type(offset + Type.VECTOR_INT2) # FixedTypedVector
|
|
272
|
+
elif fixed_len == 3:
|
|
273
|
+
return Type(offset + Type.VECTOR_INT3) # FixedTypedVector
|
|
274
|
+
elif fixed_len == 4:
|
|
275
|
+
return Type(offset + Type.VECTOR_INT4) # FixedTypedVector
|
|
276
|
+
else:
|
|
277
|
+
raise ValueError('unsupported fixed_len: %s' % fixed_len)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
class Buf:
|
|
281
|
+
"""Class to access underlying buffer object starting from the given offset."""
|
|
282
|
+
|
|
283
|
+
def __init__(self, buf, offset):
|
|
284
|
+
self._buf = buf
|
|
285
|
+
self._offset = offset if offset >= 0 else len(buf) + offset
|
|
286
|
+
self._length = len(buf) - self._offset
|
|
287
|
+
|
|
288
|
+
def __getitem__(self, key):
|
|
289
|
+
if isinstance(key, slice):
|
|
290
|
+
return self._buf[_ShiftSlice(key, self._offset, self._length)]
|
|
291
|
+
elif isinstance(key, int):
|
|
292
|
+
return self._buf[self._offset + key]
|
|
293
|
+
else:
|
|
294
|
+
raise TypeError('invalid key type')
|
|
295
|
+
|
|
296
|
+
def __setitem__(self, key, value):
|
|
297
|
+
if isinstance(key, slice):
|
|
298
|
+
self._buf[_ShiftSlice(key, self._offset, self._length)] = value
|
|
299
|
+
elif isinstance(key, int):
|
|
300
|
+
self._buf[self._offset + key] = key
|
|
301
|
+
else:
|
|
302
|
+
raise TypeError('invalid key type')
|
|
303
|
+
|
|
304
|
+
def __repr__(self):
|
|
305
|
+
return 'buf[%d:]' % self._offset
|
|
306
|
+
|
|
307
|
+
def Find(self, sub):
|
|
308
|
+
"""Returns the lowest index where the sub subsequence is found."""
|
|
309
|
+
return self._buf[self._offset:].find(sub)
|
|
310
|
+
|
|
311
|
+
def Slice(self, offset):
|
|
312
|
+
"""Returns new `Buf` which starts from the given offset."""
|
|
313
|
+
return Buf(self._buf, self._offset + offset)
|
|
314
|
+
|
|
315
|
+
def Indirect(self, offset, byte_width):
|
|
316
|
+
"""Return new `Buf` based on the encoded offset (indirect encoding)."""
|
|
317
|
+
return self.Slice(offset - _Unpack(U, self[offset:offset + byte_width]))
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
class Object:
|
|
321
|
+
"""Base class for all non-trivial data accessors."""
|
|
322
|
+
__slots__ = '_buf', '_byte_width'
|
|
323
|
+
|
|
324
|
+
def __init__(self, buf, byte_width):
|
|
325
|
+
self._buf = buf
|
|
326
|
+
self._byte_width = byte_width
|
|
327
|
+
|
|
328
|
+
@property
|
|
329
|
+
def ByteWidth(self):
|
|
330
|
+
return self._byte_width
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
class Sized(Object):
|
|
334
|
+
"""Base class for all data accessors which need to read encoded size."""
|
|
335
|
+
__slots__ = '_size',
|
|
336
|
+
|
|
337
|
+
def __init__(self, buf, byte_width, size=0):
|
|
338
|
+
super().__init__(buf, byte_width)
|
|
339
|
+
if size == 0:
|
|
340
|
+
self._size = _Unpack(U, self.SizeBytes)
|
|
341
|
+
else:
|
|
342
|
+
self._size = size
|
|
343
|
+
|
|
344
|
+
@property
|
|
345
|
+
def SizeBytes(self):
|
|
346
|
+
return self._buf[-self._byte_width:0]
|
|
347
|
+
|
|
348
|
+
def __len__(self):
|
|
349
|
+
return self._size
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
class Blob(Sized):
|
|
353
|
+
"""Data accessor for the encoded blob bytes."""
|
|
354
|
+
__slots__ = ()
|
|
355
|
+
|
|
356
|
+
@property
|
|
357
|
+
def Bytes(self):
|
|
358
|
+
return self._buf[0:len(self)]
|
|
359
|
+
|
|
360
|
+
def __repr__(self):
|
|
361
|
+
return 'Blob(%s, size=%d)' % (self._buf, len(self))
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
class String(Sized):
|
|
365
|
+
"""Data accessor for the encoded string bytes."""
|
|
366
|
+
__slots__ = ()
|
|
367
|
+
|
|
368
|
+
@property
|
|
369
|
+
def Bytes(self):
|
|
370
|
+
return self._buf[0:len(self)]
|
|
371
|
+
|
|
372
|
+
def Mutate(self, value):
|
|
373
|
+
"""Mutates underlying string bytes in place.
|
|
374
|
+
|
|
375
|
+
Args:
|
|
376
|
+
value: New string to replace the existing one. New string must have less
|
|
377
|
+
or equal UTF-8-encoded bytes than the existing one to successfully
|
|
378
|
+
mutate underlying byte buffer.
|
|
379
|
+
|
|
380
|
+
Returns:
|
|
381
|
+
Whether the value was mutated or not.
|
|
382
|
+
"""
|
|
383
|
+
encoded = value.encode('utf-8')
|
|
384
|
+
n = len(encoded)
|
|
385
|
+
if n <= len(self):
|
|
386
|
+
self._buf[-self._byte_width:0] = _Pack(U, n, self._byte_width)
|
|
387
|
+
self._buf[0:n] = encoded
|
|
388
|
+
self._buf[n:len(self)] = bytearray(len(self) - n)
|
|
389
|
+
return True
|
|
390
|
+
return False
|
|
391
|
+
|
|
392
|
+
def __str__(self):
|
|
393
|
+
return self.Bytes.decode('utf-8')
|
|
394
|
+
|
|
395
|
+
def __repr__(self):
|
|
396
|
+
return 'String(%s, size=%d)' % (self._buf, len(self))
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
class Key(Object):
|
|
400
|
+
"""Data accessor for the encoded key bytes."""
|
|
401
|
+
__slots__ = ()
|
|
402
|
+
|
|
403
|
+
def __init__(self, buf, byte_width):
|
|
404
|
+
assert byte_width == 1
|
|
405
|
+
super().__init__(buf, byte_width)
|
|
406
|
+
|
|
407
|
+
@property
|
|
408
|
+
def Bytes(self):
|
|
409
|
+
return self._buf[0:len(self)]
|
|
410
|
+
|
|
411
|
+
def __len__(self):
|
|
412
|
+
return self._buf.Find(0)
|
|
413
|
+
|
|
414
|
+
def __str__(self):
|
|
415
|
+
return self.Bytes.decode('ascii')
|
|
416
|
+
|
|
417
|
+
def __repr__(self):
|
|
418
|
+
return 'Key(%s, size=%d)' % (self._buf, len(self))
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
class Vector(Sized):
|
|
422
|
+
"""Data accessor for the encoded vector bytes."""
|
|
423
|
+
__slots__ = ()
|
|
424
|
+
|
|
425
|
+
def __getitem__(self, index):
|
|
426
|
+
if index < 0 or index >= len(self):
|
|
427
|
+
raise IndexError('vector index %s is out of [0, %d) range' % \
|
|
428
|
+
(index, len(self)))
|
|
429
|
+
|
|
430
|
+
packed_type = self._buf[len(self) * self._byte_width + index]
|
|
431
|
+
buf = self._buf.Slice(index * self._byte_width)
|
|
432
|
+
return Ref.PackedType(buf, self._byte_width, packed_type)
|
|
433
|
+
|
|
434
|
+
@property
|
|
435
|
+
def Value(self):
|
|
436
|
+
"""Returns the underlying encoded data as a list object."""
|
|
437
|
+
return [e.Value for e in self]
|
|
438
|
+
|
|
439
|
+
def __repr__(self):
|
|
440
|
+
return 'Vector(%s, byte_width=%d, size=%d)' % \
|
|
441
|
+
(self._buf, self._byte_width, self._size)
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
class TypedVector(Sized):
|
|
445
|
+
"""Data accessor for the encoded typed vector or fixed typed vector bytes."""
|
|
446
|
+
__slots__ = '_element_type', '_size'
|
|
447
|
+
|
|
448
|
+
def __init__(self, buf, byte_width, element_type, size=0):
|
|
449
|
+
super().__init__(buf, byte_width, size)
|
|
450
|
+
|
|
451
|
+
if element_type == Type.STRING:
|
|
452
|
+
# These can't be accessed as strings, since we don't know the bit-width
|
|
453
|
+
# of the size field, see the declaration of
|
|
454
|
+
# FBT_VECTOR_STRING_DEPRECATED above for details.
|
|
455
|
+
# We change the type here to be keys, which are a subtype of strings,
|
|
456
|
+
# and will ignore the size field. This will truncate strings with
|
|
457
|
+
# embedded nulls.
|
|
458
|
+
element_type = Type.KEY
|
|
459
|
+
|
|
460
|
+
self._element_type = element_type
|
|
461
|
+
|
|
462
|
+
@property
|
|
463
|
+
def Bytes(self):
|
|
464
|
+
return self._buf[:self._byte_width * len(self)]
|
|
465
|
+
|
|
466
|
+
@property
|
|
467
|
+
def ElementType(self):
|
|
468
|
+
return self._element_type
|
|
469
|
+
|
|
470
|
+
def __getitem__(self, index):
|
|
471
|
+
if index < 0 or index >= len(self):
|
|
472
|
+
raise IndexError('vector index %s is out of [0, %d) range' % \
|
|
473
|
+
(index, len(self)))
|
|
474
|
+
|
|
475
|
+
buf = self._buf.Slice(index * self._byte_width)
|
|
476
|
+
return Ref(buf, self._byte_width, 1, self._element_type)
|
|
477
|
+
|
|
478
|
+
@property
|
|
479
|
+
def Value(self):
|
|
480
|
+
"""Returns underlying data as list object."""
|
|
481
|
+
if not self:
|
|
482
|
+
return []
|
|
483
|
+
|
|
484
|
+
if self._element_type is Type.BOOL:
|
|
485
|
+
return [bool(e) for e in _UnpackVector(U, self.Bytes, len(self))]
|
|
486
|
+
elif self._element_type is Type.INT:
|
|
487
|
+
return list(_UnpackVector(I, self.Bytes, len(self)))
|
|
488
|
+
elif self._element_type is Type.UINT:
|
|
489
|
+
return list(_UnpackVector(U, self.Bytes, len(self)))
|
|
490
|
+
elif self._element_type is Type.FLOAT:
|
|
491
|
+
return list(_UnpackVector(F, self.Bytes, len(self)))
|
|
492
|
+
elif self._element_type is Type.KEY:
|
|
493
|
+
return [e.AsKey for e in self]
|
|
494
|
+
elif self._element_type is Type.STRING:
|
|
495
|
+
return [e.AsString for e in self]
|
|
496
|
+
else:
|
|
497
|
+
raise TypeError('unsupported element_type: %s' % self._element_type)
|
|
498
|
+
|
|
499
|
+
def __repr__(self):
|
|
500
|
+
return 'TypedVector(%s, byte_width=%d, element_type=%s, size=%d)' % \
|
|
501
|
+
(self._buf, self._byte_width, self._element_type, self._size)
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
class Map(Vector):
|
|
505
|
+
"""Data accessor for the encoded map bytes."""
|
|
506
|
+
|
|
507
|
+
@staticmethod
|
|
508
|
+
def CompareKeys(a, b):
|
|
509
|
+
if isinstance(a, Ref):
|
|
510
|
+
a = a.AsKeyBytes
|
|
511
|
+
if isinstance(b, Ref):
|
|
512
|
+
b = b.AsKeyBytes
|
|
513
|
+
return a < b
|
|
514
|
+
|
|
515
|
+
def __getitem__(self, key):
|
|
516
|
+
if isinstance(key, int):
|
|
517
|
+
return super().__getitem__(key)
|
|
518
|
+
|
|
519
|
+
index = _BinarySearch(self.Keys, key.encode('ascii'), self.CompareKeys)
|
|
520
|
+
if index != -1:
|
|
521
|
+
return super().__getitem__(index)
|
|
522
|
+
|
|
523
|
+
raise KeyError(key)
|
|
524
|
+
|
|
525
|
+
@property
|
|
526
|
+
def Keys(self):
|
|
527
|
+
byte_width = _Unpack(U, self._buf[-2 * self._byte_width:-self._byte_width])
|
|
528
|
+
buf = self._buf.Indirect(-3 * self._byte_width, self._byte_width)
|
|
529
|
+
return TypedVector(buf, byte_width, Type.KEY)
|
|
530
|
+
|
|
531
|
+
@property
|
|
532
|
+
def Values(self):
|
|
533
|
+
return Vector(self._buf, self._byte_width)
|
|
534
|
+
|
|
535
|
+
@property
|
|
536
|
+
def Value(self):
|
|
537
|
+
return {k.Value: v.Value for k, v in zip(self.Keys, self.Values)}
|
|
538
|
+
|
|
539
|
+
def __repr__(self):
|
|
540
|
+
return 'Map(%s, size=%d)' % (self._buf, len(self))
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
class Ref:
|
|
544
|
+
"""Data accessor for the encoded data bytes."""
|
|
545
|
+
__slots__ = '_buf', '_parent_width', '_byte_width', '_type'
|
|
546
|
+
|
|
547
|
+
@staticmethod
|
|
548
|
+
def PackedType(buf, parent_width, packed_type):
|
|
549
|
+
byte_width, type_ = Type.Unpack(packed_type)
|
|
550
|
+
return Ref(buf, parent_width, byte_width, type_)
|
|
551
|
+
|
|
552
|
+
def __init__(self, buf, parent_width, byte_width, type_):
|
|
553
|
+
self._buf = buf
|
|
554
|
+
self._parent_width = parent_width
|
|
555
|
+
self._byte_width = byte_width
|
|
556
|
+
self._type = type_
|
|
557
|
+
|
|
558
|
+
def __repr__(self):
|
|
559
|
+
return 'Ref(%s, parent_width=%d, byte_width=%d, type_=%s)' % \
|
|
560
|
+
(self._buf, self._parent_width, self._byte_width, self._type)
|
|
561
|
+
|
|
562
|
+
@property
|
|
563
|
+
def _Bytes(self):
|
|
564
|
+
return self._buf[:self._parent_width]
|
|
565
|
+
|
|
566
|
+
def _ConvertError(self, target_type):
|
|
567
|
+
raise TypeError('cannot convert %s to %s' % (self._type, target_type))
|
|
568
|
+
|
|
569
|
+
def _Indirect(self):
|
|
570
|
+
return self._buf.Indirect(0, self._parent_width)
|
|
571
|
+
|
|
572
|
+
@property
|
|
573
|
+
def IsNull(self):
|
|
574
|
+
return self._type is Type.NULL
|
|
575
|
+
|
|
576
|
+
@property
|
|
577
|
+
def IsBool(self):
|
|
578
|
+
return self._type is Type.BOOL
|
|
579
|
+
|
|
580
|
+
@property
|
|
581
|
+
def AsBool(self):
|
|
582
|
+
if self._type is Type.BOOL:
|
|
583
|
+
return bool(_Unpack(U, self._Bytes))
|
|
584
|
+
else:
|
|
585
|
+
return self.AsInt != 0
|
|
586
|
+
|
|
587
|
+
def MutateBool(self, value):
|
|
588
|
+
"""Mutates underlying boolean value bytes in place.
|
|
589
|
+
|
|
590
|
+
Args:
|
|
591
|
+
value: New boolean value.
|
|
592
|
+
|
|
593
|
+
Returns:
|
|
594
|
+
Whether the value was mutated or not.
|
|
595
|
+
"""
|
|
596
|
+
return self.IsBool and \
|
|
597
|
+
_Mutate(U, self._buf, value, self._parent_width, BitWidth.W8)
|
|
598
|
+
|
|
599
|
+
@property
|
|
600
|
+
def IsNumeric(self):
|
|
601
|
+
return self.IsInt or self.IsFloat
|
|
602
|
+
|
|
603
|
+
@property
|
|
604
|
+
def IsInt(self):
|
|
605
|
+
return self._type in (Type.INT, Type.INDIRECT_INT, Type.UINT,
|
|
606
|
+
Type.INDIRECT_UINT)
|
|
607
|
+
|
|
608
|
+
@property
|
|
609
|
+
def AsInt(self):
|
|
610
|
+
"""Returns current reference as integer value."""
|
|
611
|
+
if self.IsNull:
|
|
612
|
+
return 0
|
|
613
|
+
elif self.IsBool:
|
|
614
|
+
return int(self.AsBool)
|
|
615
|
+
elif self._type is Type.INT:
|
|
616
|
+
return _Unpack(I, self._Bytes)
|
|
617
|
+
elif self._type is Type.INDIRECT_INT:
|
|
618
|
+
return _Unpack(I, self._Indirect()[:self._byte_width])
|
|
619
|
+
if self._type is Type.UINT:
|
|
620
|
+
return _Unpack(U, self._Bytes)
|
|
621
|
+
elif self._type is Type.INDIRECT_UINT:
|
|
622
|
+
return _Unpack(U, self._Indirect()[:self._byte_width])
|
|
623
|
+
elif self.IsString:
|
|
624
|
+
return len(self.AsString)
|
|
625
|
+
elif self.IsKey:
|
|
626
|
+
return len(self.AsKey)
|
|
627
|
+
elif self.IsBlob:
|
|
628
|
+
return len(self.AsBlob)
|
|
629
|
+
elif self.IsVector:
|
|
630
|
+
return len(self.AsVector)
|
|
631
|
+
elif self.IsTypedVector:
|
|
632
|
+
return len(self.AsTypedVector)
|
|
633
|
+
elif self.IsFixedTypedVector:
|
|
634
|
+
return len(self.AsFixedTypedVector)
|
|
635
|
+
else:
|
|
636
|
+
raise self._ConvertError(Type.INT)
|
|
637
|
+
|
|
638
|
+
def MutateInt(self, value):
|
|
639
|
+
"""Mutates underlying integer value bytes in place.
|
|
640
|
+
|
|
641
|
+
Args:
|
|
642
|
+
value: New integer value. It must fit to the byte size of the existing
|
|
643
|
+
encoded value.
|
|
644
|
+
|
|
645
|
+
Returns:
|
|
646
|
+
Whether the value was mutated or not.
|
|
647
|
+
"""
|
|
648
|
+
if self._type is Type.INT:
|
|
649
|
+
return _Mutate(I, self._buf, value, self._parent_width, BitWidth.I(value))
|
|
650
|
+
elif self._type is Type.INDIRECT_INT:
|
|
651
|
+
return _Mutate(I, self._Indirect(), value, self._byte_width,
|
|
652
|
+
BitWidth.I(value))
|
|
653
|
+
elif self._type is Type.UINT:
|
|
654
|
+
return _Mutate(U, self._buf, value, self._parent_width, BitWidth.U(value))
|
|
655
|
+
elif self._type is Type.INDIRECT_UINT:
|
|
656
|
+
return _Mutate(U, self._Indirect(), value, self._byte_width,
|
|
657
|
+
BitWidth.U(value))
|
|
658
|
+
else:
|
|
659
|
+
return False
|
|
660
|
+
|
|
661
|
+
@property
|
|
662
|
+
def IsFloat(self):
|
|
663
|
+
return self._type in (Type.FLOAT, Type.INDIRECT_FLOAT)
|
|
664
|
+
|
|
665
|
+
@property
|
|
666
|
+
def AsFloat(self):
|
|
667
|
+
"""Returns current reference as floating point value."""
|
|
668
|
+
if self.IsNull:
|
|
669
|
+
return 0.0
|
|
670
|
+
elif self.IsBool:
|
|
671
|
+
return float(self.AsBool)
|
|
672
|
+
elif self.IsInt:
|
|
673
|
+
return float(self.AsInt)
|
|
674
|
+
elif self._type is Type.FLOAT:
|
|
675
|
+
return _Unpack(F, self._Bytes)
|
|
676
|
+
elif self._type is Type.INDIRECT_FLOAT:
|
|
677
|
+
return _Unpack(F, self._Indirect()[:self._byte_width])
|
|
678
|
+
elif self.IsString:
|
|
679
|
+
return float(self.AsString)
|
|
680
|
+
elif self.IsVector:
|
|
681
|
+
return float(len(self.AsVector))
|
|
682
|
+
elif self.IsTypedVector():
|
|
683
|
+
return float(len(self.AsTypedVector))
|
|
684
|
+
elif self.IsFixedTypedVector():
|
|
685
|
+
return float(len(self.FixedTypedVector))
|
|
686
|
+
else:
|
|
687
|
+
raise self._ConvertError(Type.FLOAT)
|
|
688
|
+
|
|
689
|
+
def MutateFloat(self, value):
|
|
690
|
+
"""Mutates underlying floating point value bytes in place.
|
|
691
|
+
|
|
692
|
+
Args:
|
|
693
|
+
value: New float value. It must fit to the byte size of the existing
|
|
694
|
+
encoded value.
|
|
695
|
+
|
|
696
|
+
Returns:
|
|
697
|
+
Whether the value was mutated or not.
|
|
698
|
+
"""
|
|
699
|
+
if self._type is Type.FLOAT:
|
|
700
|
+
return _Mutate(F, self._buf, value, self._parent_width,
|
|
701
|
+
BitWidth.B(self._parent_width))
|
|
702
|
+
elif self._type is Type.INDIRECT_FLOAT:
|
|
703
|
+
return _Mutate(F, self._Indirect(), value, self._byte_width,
|
|
704
|
+
BitWidth.B(self._byte_width))
|
|
705
|
+
else:
|
|
706
|
+
return False
|
|
707
|
+
|
|
708
|
+
@property
|
|
709
|
+
def IsKey(self):
|
|
710
|
+
return self._type is Type.KEY
|
|
711
|
+
|
|
712
|
+
@property
|
|
713
|
+
def AsKeyBytes(self):
|
|
714
|
+
if self.IsKey:
|
|
715
|
+
return Key(self._Indirect(), self._byte_width).Bytes
|
|
716
|
+
else:
|
|
717
|
+
raise self._ConvertError(Type.KEY)
|
|
718
|
+
|
|
719
|
+
@property
|
|
720
|
+
def AsKey(self):
|
|
721
|
+
if self.IsKey:
|
|
722
|
+
return str(Key(self._Indirect(), self._byte_width))
|
|
723
|
+
else:
|
|
724
|
+
raise self._ConvertError(Type.KEY)
|
|
725
|
+
|
|
726
|
+
@property
|
|
727
|
+
def IsString(self):
|
|
728
|
+
return self._type is Type.STRING
|
|
729
|
+
|
|
730
|
+
@property
|
|
731
|
+
def AsString(self):
|
|
732
|
+
if self.IsString:
|
|
733
|
+
return str(String(self._Indirect(), self._byte_width))
|
|
734
|
+
elif self.IsKey:
|
|
735
|
+
return self.AsKey
|
|
736
|
+
else:
|
|
737
|
+
raise self._ConvertError(Type.STRING)
|
|
738
|
+
|
|
739
|
+
def MutateString(self, value):
|
|
740
|
+
return String(self._Indirect(), self._byte_width).Mutate(value)
|
|
741
|
+
|
|
742
|
+
@property
|
|
743
|
+
def IsBlob(self):
|
|
744
|
+
return self._type is Type.BLOB
|
|
745
|
+
|
|
746
|
+
@property
|
|
747
|
+
def AsBlob(self):
|
|
748
|
+
if self.IsBlob:
|
|
749
|
+
return Blob(self._Indirect(), self._byte_width).Bytes
|
|
750
|
+
else:
|
|
751
|
+
raise self._ConvertError(Type.BLOB)
|
|
752
|
+
|
|
753
|
+
@property
|
|
754
|
+
def IsAnyVector(self):
|
|
755
|
+
return self.IsVector or self.IsTypedVector or self.IsFixedTypedVector()
|
|
756
|
+
|
|
757
|
+
@property
|
|
758
|
+
def IsVector(self):
|
|
759
|
+
return self._type in (Type.VECTOR, Type.MAP)
|
|
760
|
+
|
|
761
|
+
@property
|
|
762
|
+
def AsVector(self):
|
|
763
|
+
if self.IsVector:
|
|
764
|
+
return Vector(self._Indirect(), self._byte_width)
|
|
765
|
+
else:
|
|
766
|
+
raise self._ConvertError(Type.VECTOR)
|
|
767
|
+
|
|
768
|
+
@property
|
|
769
|
+
def IsTypedVector(self):
|
|
770
|
+
return Type.IsTypedVector(self._type)
|
|
771
|
+
|
|
772
|
+
@property
|
|
773
|
+
def AsTypedVector(self):
|
|
774
|
+
if self.IsTypedVector:
|
|
775
|
+
return TypedVector(self._Indirect(), self._byte_width,
|
|
776
|
+
Type.ToTypedVectorElementType(self._type))
|
|
777
|
+
else:
|
|
778
|
+
raise self._ConvertError('TYPED_VECTOR')
|
|
779
|
+
|
|
780
|
+
@property
|
|
781
|
+
def IsFixedTypedVector(self):
|
|
782
|
+
return Type.IsFixedTypedVector(self._type)
|
|
783
|
+
|
|
784
|
+
@property
|
|
785
|
+
def AsFixedTypedVector(self):
|
|
786
|
+
if self.IsFixedTypedVector:
|
|
787
|
+
element_type, size = Type.ToFixedTypedVectorElementType(self._type)
|
|
788
|
+
return TypedVector(self._Indirect(), self._byte_width, element_type, size)
|
|
789
|
+
else:
|
|
790
|
+
raise self._ConvertError('FIXED_TYPED_VECTOR')
|
|
791
|
+
|
|
792
|
+
@property
|
|
793
|
+
def IsMap(self):
|
|
794
|
+
return self._type is Type.MAP
|
|
795
|
+
|
|
796
|
+
@property
|
|
797
|
+
def AsMap(self):
|
|
798
|
+
if self.IsMap:
|
|
799
|
+
return Map(self._Indirect(), self._byte_width)
|
|
800
|
+
else:
|
|
801
|
+
raise self._ConvertError(Type.MAP)
|
|
802
|
+
|
|
803
|
+
@property
|
|
804
|
+
def Value(self):
|
|
805
|
+
"""Converts current reference to value of corresponding type.
|
|
806
|
+
|
|
807
|
+
This is equivalent to calling `AsInt` for integer values, `AsFloat` for
|
|
808
|
+
floating point values, etc.
|
|
809
|
+
|
|
810
|
+
Returns:
|
|
811
|
+
Value of corresponding type.
|
|
812
|
+
"""
|
|
813
|
+
if self.IsNull:
|
|
814
|
+
return None
|
|
815
|
+
elif self.IsBool:
|
|
816
|
+
return self.AsBool
|
|
817
|
+
elif self.IsInt:
|
|
818
|
+
return self.AsInt
|
|
819
|
+
elif self.IsFloat:
|
|
820
|
+
return self.AsFloat
|
|
821
|
+
elif self.IsString:
|
|
822
|
+
return self.AsString
|
|
823
|
+
elif self.IsKey:
|
|
824
|
+
return self.AsKey
|
|
825
|
+
elif self.IsBlob:
|
|
826
|
+
return self.AsBlob
|
|
827
|
+
elif self.IsMap:
|
|
828
|
+
return self.AsMap.Value
|
|
829
|
+
elif self.IsVector:
|
|
830
|
+
return self.AsVector.Value
|
|
831
|
+
elif self.IsTypedVector:
|
|
832
|
+
return self.AsTypedVector.Value
|
|
833
|
+
elif self.IsFixedTypedVector:
|
|
834
|
+
return self.AsFixedTypedVector.Value
|
|
835
|
+
else:
|
|
836
|
+
raise TypeError('cannot convert %r to value' % self)
|
|
837
|
+
|
|
838
|
+
|
|
839
|
+
def _IsIterable(obj):
|
|
840
|
+
try:
|
|
841
|
+
iter(obj)
|
|
842
|
+
return True
|
|
843
|
+
except TypeError:
|
|
844
|
+
return False
|
|
845
|
+
|
|
846
|
+
|
|
847
|
+
class Value:
|
|
848
|
+
"""Class to represent given value during the encoding process."""
|
|
849
|
+
|
|
850
|
+
@staticmethod
|
|
851
|
+
def Null():
|
|
852
|
+
return Value(0, Type.NULL, BitWidth.W8)
|
|
853
|
+
|
|
854
|
+
@staticmethod
|
|
855
|
+
def Bool(value):
|
|
856
|
+
return Value(value, Type.BOOL, BitWidth.W8)
|
|
857
|
+
|
|
858
|
+
@staticmethod
|
|
859
|
+
def Int(value, bit_width):
|
|
860
|
+
return Value(value, Type.INT, bit_width)
|
|
861
|
+
|
|
862
|
+
@staticmethod
|
|
863
|
+
def UInt(value, bit_width):
|
|
864
|
+
return Value(value, Type.UINT, bit_width)
|
|
865
|
+
|
|
866
|
+
@staticmethod
|
|
867
|
+
def Float(value, bit_width):
|
|
868
|
+
return Value(value, Type.FLOAT, bit_width)
|
|
869
|
+
|
|
870
|
+
@staticmethod
|
|
871
|
+
def Key(offset):
|
|
872
|
+
return Value(offset, Type.KEY, BitWidth.W8)
|
|
873
|
+
|
|
874
|
+
def __init__(self, value, type_, min_bit_width):
|
|
875
|
+
self._value = value
|
|
876
|
+
self._type = type_
|
|
877
|
+
|
|
878
|
+
# For scalars: of itself, for vector: of its elements, for string: length.
|
|
879
|
+
self._min_bit_width = min_bit_width
|
|
880
|
+
|
|
881
|
+
@property
|
|
882
|
+
def Value(self):
|
|
883
|
+
return self._value
|
|
884
|
+
|
|
885
|
+
@property
|
|
886
|
+
def Type(self):
|
|
887
|
+
return self._type
|
|
888
|
+
|
|
889
|
+
@property
|
|
890
|
+
def MinBitWidth(self):
|
|
891
|
+
return self._min_bit_width
|
|
892
|
+
|
|
893
|
+
def StoredPackedType(self, parent_bit_width=BitWidth.W8):
|
|
894
|
+
return Type.Pack(self._type, self.StoredWidth(parent_bit_width))
|
|
895
|
+
|
|
896
|
+
# We have an absolute offset, but want to store a relative offset
|
|
897
|
+
# elem_index elements beyond the current buffer end. Since whether
|
|
898
|
+
# the relative offset fits in a certain byte_width depends on
|
|
899
|
+
# the size of the elements before it (and their alignment), we have
|
|
900
|
+
# to test for each size in turn.
|
|
901
|
+
def ElemWidth(self, buf_size, elem_index=0):
|
|
902
|
+
if Type.IsInline(self._type):
|
|
903
|
+
return self._min_bit_width
|
|
904
|
+
for byte_width in 1, 2, 4, 8:
|
|
905
|
+
offset_loc = buf_size + _PaddingBytes(buf_size, byte_width) + \
|
|
906
|
+
elem_index * byte_width
|
|
907
|
+
bit_width = BitWidth.U(offset_loc - self._value)
|
|
908
|
+
if byte_width == (1 << bit_width):
|
|
909
|
+
return bit_width
|
|
910
|
+
raise ValueError('relative offset is too big')
|
|
911
|
+
|
|
912
|
+
def StoredWidth(self, parent_bit_width=BitWidth.W8):
|
|
913
|
+
if Type.IsInline(self._type):
|
|
914
|
+
return max(self._min_bit_width, parent_bit_width)
|
|
915
|
+
return self._min_bit_width
|
|
916
|
+
|
|
917
|
+
def __repr__(self):
|
|
918
|
+
return 'Value(%s, %s, %s)' % (self._value, self._type, self._min_bit_width)
|
|
919
|
+
|
|
920
|
+
def __str__(self):
|
|
921
|
+
return str(self._value)
|
|
922
|
+
|
|
923
|
+
|
|
924
|
+
def InMap(func):
|
|
925
|
+
def wrapper(self, *args, **kwargs):
|
|
926
|
+
if isinstance(args[0], str):
|
|
927
|
+
self.Key(args[0])
|
|
928
|
+
func(self, *args[1:], **kwargs)
|
|
929
|
+
else:
|
|
930
|
+
func(self, *args, **kwargs)
|
|
931
|
+
return wrapper
|
|
932
|
+
|
|
933
|
+
|
|
934
|
+
def InMapForString(func):
|
|
935
|
+
def wrapper(self, *args):
|
|
936
|
+
if len(args) == 1:
|
|
937
|
+
func(self, args[0])
|
|
938
|
+
elif len(args) == 2:
|
|
939
|
+
self.Key(args[0])
|
|
940
|
+
func(self, args[1])
|
|
941
|
+
else:
|
|
942
|
+
raise ValueError('invalid number of arguments')
|
|
943
|
+
return wrapper
|
|
944
|
+
|
|
945
|
+
|
|
946
|
+
class Pool:
|
|
947
|
+
"""Collection of (data, offset) pairs sorted by data for quick access."""
|
|
948
|
+
|
|
949
|
+
def __init__(self):
|
|
950
|
+
self._pool = [] # sorted list of (data, offset) tuples
|
|
951
|
+
|
|
952
|
+
def FindOrInsert(self, data, offset):
|
|
953
|
+
do = data, offset
|
|
954
|
+
index = _BinarySearch(self._pool, do, lambda a, b: a[0] < b[0])
|
|
955
|
+
if index != -1:
|
|
956
|
+
_, offset = self._pool[index]
|
|
957
|
+
return offset
|
|
958
|
+
self._pool.insert(index, do)
|
|
959
|
+
return None
|
|
960
|
+
|
|
961
|
+
def Clear(self):
|
|
962
|
+
self._pool = []
|
|
963
|
+
|
|
964
|
+
@property
|
|
965
|
+
def Elements(self):
|
|
966
|
+
return [data for data, _ in self._pool]
|
|
967
|
+
|
|
968
|
+
|
|
969
|
+
class Builder:
|
|
970
|
+
"""Helper class to encode structural data into flexbuffers format."""
|
|
971
|
+
|
|
972
|
+
def __init__(self,
|
|
973
|
+
share_strings=False,
|
|
974
|
+
share_keys=True,
|
|
975
|
+
force_min_bit_width=BitWidth.W8):
|
|
976
|
+
self._share_strings = share_strings
|
|
977
|
+
self._share_keys = share_keys
|
|
978
|
+
self._force_min_bit_width = force_min_bit_width
|
|
979
|
+
|
|
980
|
+
self._string_pool = Pool()
|
|
981
|
+
self._key_pool = Pool()
|
|
982
|
+
|
|
983
|
+
self._finished = False
|
|
984
|
+
self._buf = bytearray()
|
|
985
|
+
self._stack = []
|
|
986
|
+
|
|
987
|
+
def __len__(self):
|
|
988
|
+
return len(self._buf)
|
|
989
|
+
|
|
990
|
+
@property
|
|
991
|
+
def StringPool(self):
|
|
992
|
+
return self._string_pool
|
|
993
|
+
|
|
994
|
+
@property
|
|
995
|
+
def KeyPool(self):
|
|
996
|
+
return self._key_pool
|
|
997
|
+
|
|
998
|
+
def Clear(self):
|
|
999
|
+
self._string_pool.Clear()
|
|
1000
|
+
self._key_pool.Clear()
|
|
1001
|
+
self._finished = False
|
|
1002
|
+
self._buf = bytearray()
|
|
1003
|
+
self._stack = []
|
|
1004
|
+
|
|
1005
|
+
def Finish(self):
|
|
1006
|
+
"""Finishes encoding process and returns underlying buffer."""
|
|
1007
|
+
if self._finished:
|
|
1008
|
+
raise RuntimeError('builder has been already finished')
|
|
1009
|
+
|
|
1010
|
+
# If you hit this exception, you likely have objects that were never
|
|
1011
|
+
# included in a parent. You need to have exactly one root to finish a
|
|
1012
|
+
# buffer. Check your Start/End calls are matched, and all objects are inside
|
|
1013
|
+
# some other object.
|
|
1014
|
+
if len(self._stack) != 1:
|
|
1015
|
+
raise RuntimeError('internal stack size must be one')
|
|
1016
|
+
|
|
1017
|
+
value = self._stack[0]
|
|
1018
|
+
byte_width = self._Align(value.ElemWidth(len(self._buf)))
|
|
1019
|
+
self._WriteAny(value, byte_width=byte_width) # Root value
|
|
1020
|
+
self._Write(U, value.StoredPackedType(), byte_width=1) # Root type
|
|
1021
|
+
self._Write(U, byte_width, byte_width=1) # Root size
|
|
1022
|
+
|
|
1023
|
+
self.finished = True
|
|
1024
|
+
return self._buf
|
|
1025
|
+
|
|
1026
|
+
def _ReadKey(self, offset):
|
|
1027
|
+
key = self._buf[offset:]
|
|
1028
|
+
return key[:key.find(0)]
|
|
1029
|
+
|
|
1030
|
+
def _Align(self, alignment):
|
|
1031
|
+
byte_width = 1 << alignment
|
|
1032
|
+
self._buf.extend(b'\x00' * _PaddingBytes(len(self._buf), byte_width))
|
|
1033
|
+
return byte_width
|
|
1034
|
+
|
|
1035
|
+
def _Write(self, fmt, value, byte_width):
|
|
1036
|
+
self._buf.extend(_Pack(fmt, value, byte_width))
|
|
1037
|
+
|
|
1038
|
+
def _WriteVector(self, fmt, values, byte_width):
|
|
1039
|
+
self._buf.extend(_PackVector(fmt, values, byte_width))
|
|
1040
|
+
|
|
1041
|
+
def _WriteOffset(self, offset, byte_width):
|
|
1042
|
+
relative_offset = len(self._buf) - offset
|
|
1043
|
+
assert byte_width == 8 or relative_offset < (1 << (8 * byte_width))
|
|
1044
|
+
self._Write(U, relative_offset, byte_width)
|
|
1045
|
+
|
|
1046
|
+
def _WriteAny(self, value, byte_width):
|
|
1047
|
+
fmt = {
|
|
1048
|
+
Type.NULL: U, Type.BOOL: U, Type.INT: I, Type.UINT: U, Type.FLOAT: F
|
|
1049
|
+
}.get(value.Type)
|
|
1050
|
+
if fmt:
|
|
1051
|
+
self._Write(fmt, value.Value, byte_width)
|
|
1052
|
+
else:
|
|
1053
|
+
self._WriteOffset(value.Value, byte_width)
|
|
1054
|
+
|
|
1055
|
+
def _WriteBlob(self, data, append_zero, type_):
|
|
1056
|
+
bit_width = BitWidth.U(len(data))
|
|
1057
|
+
byte_width = self._Align(bit_width)
|
|
1058
|
+
self._Write(U, len(data), byte_width)
|
|
1059
|
+
loc = len(self._buf)
|
|
1060
|
+
self._buf.extend(data)
|
|
1061
|
+
if append_zero:
|
|
1062
|
+
self._buf.append(0)
|
|
1063
|
+
self._stack.append(Value(loc, type_, bit_width))
|
|
1064
|
+
return loc
|
|
1065
|
+
|
|
1066
|
+
def _WriteScalarVector(self, element_type, byte_width, elements, fixed):
|
|
1067
|
+
"""Writes scalar vector elements to the underlying buffer."""
|
|
1068
|
+
bit_width = BitWidth.B(byte_width)
|
|
1069
|
+
# If you get this exception, you're trying to write a vector with a size
|
|
1070
|
+
# field that is bigger than the scalars you're trying to write (e.g. a
|
|
1071
|
+
# byte vector > 255 elements). For such types, write a "blob" instead.
|
|
1072
|
+
if BitWidth.U(len(elements)) > bit_width:
|
|
1073
|
+
raise ValueError('too many elements for the given byte_width')
|
|
1074
|
+
|
|
1075
|
+
self._Align(bit_width)
|
|
1076
|
+
if not fixed:
|
|
1077
|
+
self._Write(U, len(elements), byte_width)
|
|
1078
|
+
|
|
1079
|
+
loc = len(self._buf)
|
|
1080
|
+
|
|
1081
|
+
fmt = {Type.INT: I, Type.UINT: U, Type.FLOAT: F}.get(element_type)
|
|
1082
|
+
if not fmt:
|
|
1083
|
+
raise TypeError('unsupported element_type')
|
|
1084
|
+
self._WriteVector(fmt, elements, byte_width)
|
|
1085
|
+
|
|
1086
|
+
type_ = Type.ToTypedVector(element_type, len(elements) if fixed else 0)
|
|
1087
|
+
self._stack.append(Value(loc, type_, bit_width))
|
|
1088
|
+
return loc
|
|
1089
|
+
|
|
1090
|
+
def _CreateVector(self, elements, typed, fixed, keys=None):
|
|
1091
|
+
"""Writes vector elements to the underlying buffer."""
|
|
1092
|
+
length = len(elements)
|
|
1093
|
+
|
|
1094
|
+
if fixed and not typed:
|
|
1095
|
+
raise ValueError('fixed vector must be typed')
|
|
1096
|
+
|
|
1097
|
+
# Figure out smallest bit width we can store this vector with.
|
|
1098
|
+
bit_width = max(self._force_min_bit_width, BitWidth.U(length))
|
|
1099
|
+
prefix_elems = 1 # Vector size
|
|
1100
|
+
if keys:
|
|
1101
|
+
bit_width = max(bit_width, keys.ElemWidth(len(self._buf)))
|
|
1102
|
+
prefix_elems += 2 # Offset to the keys vector and its byte width.
|
|
1103
|
+
|
|
1104
|
+
vector_type = Type.KEY
|
|
1105
|
+
# Check bit widths and types for all elements.
|
|
1106
|
+
for i, e in enumerate(elements):
|
|
1107
|
+
bit_width = max(bit_width, e.ElemWidth(len(self._buf), prefix_elems + i))
|
|
1108
|
+
|
|
1109
|
+
if typed:
|
|
1110
|
+
if i == 0:
|
|
1111
|
+
vector_type = e.Type
|
|
1112
|
+
else:
|
|
1113
|
+
if vector_type != e.Type:
|
|
1114
|
+
raise RuntimeError('typed vector elements must be of the same type')
|
|
1115
|
+
|
|
1116
|
+
if fixed and not Type.IsFixedTypedVectorElementType(vector_type):
|
|
1117
|
+
raise RuntimeError('must be fixed typed vector element type')
|
|
1118
|
+
|
|
1119
|
+
byte_width = self._Align(bit_width)
|
|
1120
|
+
# Write vector. First the keys width/offset if available, and size.
|
|
1121
|
+
if keys:
|
|
1122
|
+
self._WriteOffset(keys.Value, byte_width)
|
|
1123
|
+
self._Write(U, 1 << keys.MinBitWidth, byte_width)
|
|
1124
|
+
|
|
1125
|
+
if not fixed:
|
|
1126
|
+
self._Write(U, length, byte_width)
|
|
1127
|
+
|
|
1128
|
+
# Then the actual data.
|
|
1129
|
+
loc = len(self._buf)
|
|
1130
|
+
for e in elements:
|
|
1131
|
+
self._WriteAny(e, byte_width)
|
|
1132
|
+
|
|
1133
|
+
# Then the types.
|
|
1134
|
+
if not typed:
|
|
1135
|
+
for e in elements:
|
|
1136
|
+
self._buf.append(e.StoredPackedType(bit_width))
|
|
1137
|
+
|
|
1138
|
+
if keys:
|
|
1139
|
+
type_ = Type.MAP
|
|
1140
|
+
else:
|
|
1141
|
+
if typed:
|
|
1142
|
+
type_ = Type.ToTypedVector(vector_type, length if fixed else 0)
|
|
1143
|
+
else:
|
|
1144
|
+
type_ = Type.VECTOR
|
|
1145
|
+
|
|
1146
|
+
return Value(loc, type_, bit_width)
|
|
1147
|
+
|
|
1148
|
+
def _PushIndirect(self, value, type_, bit_width):
|
|
1149
|
+
byte_width = self._Align(bit_width)
|
|
1150
|
+
loc = len(self._buf)
|
|
1151
|
+
fmt = {
|
|
1152
|
+
Type.INDIRECT_INT: I,
|
|
1153
|
+
Type.INDIRECT_UINT: U,
|
|
1154
|
+
Type.INDIRECT_FLOAT: F
|
|
1155
|
+
}[type_]
|
|
1156
|
+
self._Write(fmt, value, byte_width)
|
|
1157
|
+
self._stack.append(Value(loc, type_, bit_width))
|
|
1158
|
+
|
|
1159
|
+
@InMapForString
|
|
1160
|
+
def String(self, value):
|
|
1161
|
+
"""Encodes string value."""
|
|
1162
|
+
reset_to = len(self._buf)
|
|
1163
|
+
encoded = value.encode('utf-8')
|
|
1164
|
+
loc = self._WriteBlob(encoded, append_zero=True, type_=Type.STRING)
|
|
1165
|
+
if self._share_strings:
|
|
1166
|
+
prev_loc = self._string_pool.FindOrInsert(encoded, loc)
|
|
1167
|
+
if prev_loc is not None:
|
|
1168
|
+
del self._buf[reset_to:]
|
|
1169
|
+
self._stack[-1]._value = loc = prev_loc # pylint: disable=protected-access
|
|
1170
|
+
|
|
1171
|
+
return loc
|
|
1172
|
+
|
|
1173
|
+
@InMap
|
|
1174
|
+
def Blob(self, value):
|
|
1175
|
+
"""Encodes binary blob value.
|
|
1176
|
+
|
|
1177
|
+
Args:
|
|
1178
|
+
value: A byte/bytearray value to encode
|
|
1179
|
+
|
|
1180
|
+
Returns:
|
|
1181
|
+
Offset of the encoded value in underlying the byte buffer.
|
|
1182
|
+
"""
|
|
1183
|
+
return self._WriteBlob(value, append_zero=False, type_=Type.BLOB)
|
|
1184
|
+
|
|
1185
|
+
def Key(self, value):
|
|
1186
|
+
"""Encodes key value.
|
|
1187
|
+
|
|
1188
|
+
Args:
|
|
1189
|
+
value: A byte/bytearray/str value to encode. Byte object must not contain
|
|
1190
|
+
zero bytes. String object must be convertible to ASCII.
|
|
1191
|
+
|
|
1192
|
+
Returns:
|
|
1193
|
+
Offset of the encoded value in the underlying byte buffer.
|
|
1194
|
+
"""
|
|
1195
|
+
if isinstance(value, (bytes, bytearray)):
|
|
1196
|
+
encoded = value
|
|
1197
|
+
else:
|
|
1198
|
+
encoded = value.encode('ascii')
|
|
1199
|
+
|
|
1200
|
+
if 0 in encoded:
|
|
1201
|
+
raise ValueError('key contains zero byte')
|
|
1202
|
+
|
|
1203
|
+
loc = len(self._buf)
|
|
1204
|
+
self._buf.extend(encoded)
|
|
1205
|
+
self._buf.append(0)
|
|
1206
|
+
if self._share_keys:
|
|
1207
|
+
prev_loc = self._key_pool.FindOrInsert(encoded, loc)
|
|
1208
|
+
if prev_loc is not None:
|
|
1209
|
+
del self._buf[loc:]
|
|
1210
|
+
loc = prev_loc
|
|
1211
|
+
|
|
1212
|
+
self._stack.append(Value.Key(loc))
|
|
1213
|
+
return loc
|
|
1214
|
+
|
|
1215
|
+
def Null(self, key=None):
|
|
1216
|
+
"""Encodes None value."""
|
|
1217
|
+
if key:
|
|
1218
|
+
self.Key(key)
|
|
1219
|
+
self._stack.append(Value.Null())
|
|
1220
|
+
|
|
1221
|
+
@InMap
|
|
1222
|
+
def Bool(self, value):
|
|
1223
|
+
"""Encodes boolean value.
|
|
1224
|
+
|
|
1225
|
+
Args:
|
|
1226
|
+
value: A boolean value.
|
|
1227
|
+
"""
|
|
1228
|
+
self._stack.append(Value.Bool(value))
|
|
1229
|
+
|
|
1230
|
+
@InMap
|
|
1231
|
+
def Int(self, value, byte_width=0):
|
|
1232
|
+
"""Encodes signed integer value.
|
|
1233
|
+
|
|
1234
|
+
Args:
|
|
1235
|
+
value: A signed integer value.
|
|
1236
|
+
byte_width: Number of bytes to use: 1, 2, 4, or 8.
|
|
1237
|
+
"""
|
|
1238
|
+
bit_width = BitWidth.I(value) if byte_width == 0 else BitWidth.B(byte_width)
|
|
1239
|
+
self._stack.append(Value.Int(value, bit_width))
|
|
1240
|
+
|
|
1241
|
+
@InMap
|
|
1242
|
+
def IndirectInt(self, value, byte_width=0):
|
|
1243
|
+
"""Encodes signed integer value indirectly.
|
|
1244
|
+
|
|
1245
|
+
Args:
|
|
1246
|
+
value: A signed integer value.
|
|
1247
|
+
byte_width: Number of bytes to use: 1, 2, 4, or 8.
|
|
1248
|
+
"""
|
|
1249
|
+
bit_width = BitWidth.I(value) if byte_width == 0 else BitWidth.B(byte_width)
|
|
1250
|
+
self._PushIndirect(value, Type.INDIRECT_INT, bit_width)
|
|
1251
|
+
|
|
1252
|
+
@InMap
|
|
1253
|
+
def UInt(self, value, byte_width=0):
|
|
1254
|
+
"""Encodes unsigned integer value.
|
|
1255
|
+
|
|
1256
|
+
Args:
|
|
1257
|
+
value: An unsigned integer value.
|
|
1258
|
+
byte_width: Number of bytes to use: 1, 2, 4, or 8.
|
|
1259
|
+
"""
|
|
1260
|
+
bit_width = BitWidth.U(value) if byte_width == 0 else BitWidth.B(byte_width)
|
|
1261
|
+
self._stack.append(Value.UInt(value, bit_width))
|
|
1262
|
+
|
|
1263
|
+
@InMap
|
|
1264
|
+
def IndirectUInt(self, value, byte_width=0):
|
|
1265
|
+
"""Encodes unsigned integer value indirectly.
|
|
1266
|
+
|
|
1267
|
+
Args:
|
|
1268
|
+
value: An unsigned integer value.
|
|
1269
|
+
byte_width: Number of bytes to use: 1, 2, 4, or 8.
|
|
1270
|
+
"""
|
|
1271
|
+
bit_width = BitWidth.U(value) if byte_width == 0 else BitWidth.B(byte_width)
|
|
1272
|
+
self._PushIndirect(value, Type.INDIRECT_UINT, bit_width)
|
|
1273
|
+
|
|
1274
|
+
@InMap
|
|
1275
|
+
def Float(self, value, byte_width=0):
|
|
1276
|
+
"""Encodes floating point value.
|
|
1277
|
+
|
|
1278
|
+
Args:
|
|
1279
|
+
value: A floating point value.
|
|
1280
|
+
byte_width: Number of bytes to use: 4 or 8.
|
|
1281
|
+
"""
|
|
1282
|
+
bit_width = BitWidth.F(value) if byte_width == 0 else BitWidth.B(byte_width)
|
|
1283
|
+
self._stack.append(Value.Float(value, bit_width))
|
|
1284
|
+
|
|
1285
|
+
@InMap
|
|
1286
|
+
def IndirectFloat(self, value, byte_width=0):
|
|
1287
|
+
"""Encodes floating point value indirectly.
|
|
1288
|
+
|
|
1289
|
+
Args:
|
|
1290
|
+
value: A floating point value.
|
|
1291
|
+
byte_width: Number of bytes to use: 4 or 8.
|
|
1292
|
+
"""
|
|
1293
|
+
bit_width = BitWidth.F(value) if byte_width == 0 else BitWidth.B(byte_width)
|
|
1294
|
+
self._PushIndirect(value, Type.INDIRECT_FLOAT, bit_width)
|
|
1295
|
+
|
|
1296
|
+
def _StartVector(self):
|
|
1297
|
+
"""Starts vector construction."""
|
|
1298
|
+
return len(self._stack)
|
|
1299
|
+
|
|
1300
|
+
def _EndVector(self, start, typed, fixed):
|
|
1301
|
+
"""Finishes vector construction by encodung its elements."""
|
|
1302
|
+
vec = self._CreateVector(self._stack[start:], typed, fixed)
|
|
1303
|
+
del self._stack[start:]
|
|
1304
|
+
self._stack.append(vec)
|
|
1305
|
+
return vec.Value
|
|
1306
|
+
|
|
1307
|
+
@contextlib.contextmanager
|
|
1308
|
+
def Vector(self, key=None):
|
|
1309
|
+
if key:
|
|
1310
|
+
self.Key(key)
|
|
1311
|
+
|
|
1312
|
+
try:
|
|
1313
|
+
start = self._StartVector()
|
|
1314
|
+
yield self
|
|
1315
|
+
finally:
|
|
1316
|
+
self._EndVector(start, typed=False, fixed=False)
|
|
1317
|
+
|
|
1318
|
+
@InMap
|
|
1319
|
+
def VectorFromElements(self, elements):
|
|
1320
|
+
"""Encodes sequence of any elements as a vector.
|
|
1321
|
+
|
|
1322
|
+
Args:
|
|
1323
|
+
elements: sequence of elements, they may have different types.
|
|
1324
|
+
"""
|
|
1325
|
+
with self.Vector():
|
|
1326
|
+
for e in elements:
|
|
1327
|
+
self.Add(e)
|
|
1328
|
+
|
|
1329
|
+
@contextlib.contextmanager
|
|
1330
|
+
def TypedVector(self, key=None):
|
|
1331
|
+
if key:
|
|
1332
|
+
self.Key(key)
|
|
1333
|
+
|
|
1334
|
+
try:
|
|
1335
|
+
start = self._StartVector()
|
|
1336
|
+
yield self
|
|
1337
|
+
finally:
|
|
1338
|
+
self._EndVector(start, typed=True, fixed=False)
|
|
1339
|
+
|
|
1340
|
+
@InMap
|
|
1341
|
+
def TypedVectorFromElements(self, elements, element_type=None):
|
|
1342
|
+
"""Encodes sequence of elements of the same type as typed vector.
|
|
1343
|
+
|
|
1344
|
+
Args:
|
|
1345
|
+
elements: Sequence of elements, they must be of the same type.
|
|
1346
|
+
element_type: Suggested element type. Setting it to None means determining
|
|
1347
|
+
correct value automatically based on the given elements.
|
|
1348
|
+
"""
|
|
1349
|
+
if isinstance(elements, array.array):
|
|
1350
|
+
if elements.typecode == 'f':
|
|
1351
|
+
self._WriteScalarVector(Type.FLOAT, 4, elements, fixed=False)
|
|
1352
|
+
elif elements.typecode == 'd':
|
|
1353
|
+
self._WriteScalarVector(Type.FLOAT, 8, elements, fixed=False)
|
|
1354
|
+
elif elements.typecode in ('b', 'h', 'i', 'l', 'q'):
|
|
1355
|
+
self._WriteScalarVector(
|
|
1356
|
+
Type.INT, elements.itemsize, elements, fixed=False)
|
|
1357
|
+
elif elements.typecode in ('B', 'H', 'I', 'L', 'Q'):
|
|
1358
|
+
self._WriteScalarVector(
|
|
1359
|
+
Type.UINT, elements.itemsize, elements, fixed=False)
|
|
1360
|
+
else:
|
|
1361
|
+
raise ValueError('unsupported array typecode: %s' % elements.typecode)
|
|
1362
|
+
else:
|
|
1363
|
+
add = self.Add if element_type is None else self.Adder(element_type)
|
|
1364
|
+
with self.TypedVector():
|
|
1365
|
+
for e in elements:
|
|
1366
|
+
add(e)
|
|
1367
|
+
|
|
1368
|
+
@InMap
|
|
1369
|
+
def FixedTypedVectorFromElements(self,
|
|
1370
|
+
elements,
|
|
1371
|
+
element_type=None,
|
|
1372
|
+
byte_width=0):
|
|
1373
|
+
"""Encodes sequence of elements of the same type as fixed typed vector.
|
|
1374
|
+
|
|
1375
|
+
Args:
|
|
1376
|
+
elements: Sequence of elements, they must be of the same type. Allowed
|
|
1377
|
+
types are `Type.INT`, `Type.UINT`, `Type.FLOAT`. Allowed number of
|
|
1378
|
+
elements are 2, 3, or 4.
|
|
1379
|
+
element_type: Suggested element type. Setting it to None means determining
|
|
1380
|
+
correct value automatically based on the given elements.
|
|
1381
|
+
byte_width: Number of bytes to use per element. For `Type.INT` and
|
|
1382
|
+
`Type.UINT`: 1, 2, 4, or 8. For `Type.FLOAT`: 4 or 8. Setting it to 0
|
|
1383
|
+
means determining correct value automatically based on the given
|
|
1384
|
+
elements.
|
|
1385
|
+
"""
|
|
1386
|
+
if not 2 <= len(elements) <= 4:
|
|
1387
|
+
raise ValueError('only 2, 3, or 4 elements are supported')
|
|
1388
|
+
|
|
1389
|
+
types = {type(e) for e in elements}
|
|
1390
|
+
if len(types) != 1:
|
|
1391
|
+
raise TypeError('all elements must be of the same type')
|
|
1392
|
+
|
|
1393
|
+
type_, = types
|
|
1394
|
+
|
|
1395
|
+
if element_type is None:
|
|
1396
|
+
element_type = {int: Type.INT, float: Type.FLOAT}.get(type_)
|
|
1397
|
+
if not element_type:
|
|
1398
|
+
raise TypeError('unsupported element_type: %s' % type_)
|
|
1399
|
+
|
|
1400
|
+
if byte_width == 0:
|
|
1401
|
+
width = {
|
|
1402
|
+
Type.UINT: BitWidth.U,
|
|
1403
|
+
Type.INT: BitWidth.I,
|
|
1404
|
+
Type.FLOAT: BitWidth.F
|
|
1405
|
+
}[element_type]
|
|
1406
|
+
byte_width = 1 << max(width(e) for e in elements)
|
|
1407
|
+
|
|
1408
|
+
self._WriteScalarVector(element_type, byte_width, elements, fixed=True)
|
|
1409
|
+
|
|
1410
|
+
def _StartMap(self):
|
|
1411
|
+
"""Starts map construction."""
|
|
1412
|
+
return len(self._stack)
|
|
1413
|
+
|
|
1414
|
+
def _EndMap(self, start):
|
|
1415
|
+
"""Finishes map construction by encodung its elements."""
|
|
1416
|
+
# Interleaved keys and values on the stack.
|
|
1417
|
+
stack = self._stack[start:]
|
|
1418
|
+
|
|
1419
|
+
if len(stack) % 2 != 0:
|
|
1420
|
+
raise RuntimeError('must be even number of keys and values')
|
|
1421
|
+
|
|
1422
|
+
for key in stack[::2]:
|
|
1423
|
+
if key.Type is not Type.KEY:
|
|
1424
|
+
raise RuntimeError('all map keys must be of %s type' % Type.KEY)
|
|
1425
|
+
|
|
1426
|
+
pairs = zip(stack[::2], stack[1::2]) # [(key, value), ...]
|
|
1427
|
+
pairs = sorted(pairs, key=lambda pair: self._ReadKey(pair[0].Value))
|
|
1428
|
+
|
|
1429
|
+
del self._stack[start:]
|
|
1430
|
+
for pair in pairs:
|
|
1431
|
+
self._stack.extend(pair)
|
|
1432
|
+
|
|
1433
|
+
keys = self._CreateVector(self._stack[start::2], typed=True, fixed=False)
|
|
1434
|
+
values = self._CreateVector(
|
|
1435
|
+
self._stack[start + 1::2], typed=False, fixed=False, keys=keys)
|
|
1436
|
+
|
|
1437
|
+
del self._stack[start:]
|
|
1438
|
+
self._stack.append(values)
|
|
1439
|
+
return values.Value
|
|
1440
|
+
|
|
1441
|
+
@contextlib.contextmanager
|
|
1442
|
+
def Map(self, key=None):
|
|
1443
|
+
if key:
|
|
1444
|
+
self.Key(key)
|
|
1445
|
+
|
|
1446
|
+
try:
|
|
1447
|
+
start = self._StartMap()
|
|
1448
|
+
yield self
|
|
1449
|
+
finally:
|
|
1450
|
+
self._EndMap(start)
|
|
1451
|
+
|
|
1452
|
+
def MapFromElements(self, elements):
|
|
1453
|
+
start = self._StartMap()
|
|
1454
|
+
for k, v in elements.items():
|
|
1455
|
+
self.Key(k)
|
|
1456
|
+
self.Add(v)
|
|
1457
|
+
self._EndMap(start)
|
|
1458
|
+
|
|
1459
|
+
def Adder(self, type_):
|
|
1460
|
+
return {
|
|
1461
|
+
Type.BOOL: self.Bool,
|
|
1462
|
+
Type.INT: self.Int,
|
|
1463
|
+
Type.INDIRECT_INT: self.IndirectInt,
|
|
1464
|
+
Type.UINT: self.UInt,
|
|
1465
|
+
Type.INDIRECT_UINT: self.IndirectUInt,
|
|
1466
|
+
Type.FLOAT: self.Float,
|
|
1467
|
+
Type.INDIRECT_FLOAT: self.IndirectFloat,
|
|
1468
|
+
Type.KEY: self.Key,
|
|
1469
|
+
Type.BLOB: self.Blob,
|
|
1470
|
+
Type.STRING: self.String,
|
|
1471
|
+
}[type_]
|
|
1472
|
+
|
|
1473
|
+
@InMapForString
|
|
1474
|
+
def Add(self, value):
|
|
1475
|
+
"""Encodes value of any supported type."""
|
|
1476
|
+
if value is None:
|
|
1477
|
+
self.Null()
|
|
1478
|
+
elif isinstance(value, bool):
|
|
1479
|
+
self.Bool(value)
|
|
1480
|
+
elif isinstance(value, int):
|
|
1481
|
+
self.Int(value)
|
|
1482
|
+
elif isinstance(value, float):
|
|
1483
|
+
self.Float(value)
|
|
1484
|
+
elif isinstance(value, str):
|
|
1485
|
+
self.String(value)
|
|
1486
|
+
elif isinstance(value, (bytes, bytearray)):
|
|
1487
|
+
self.Blob(value)
|
|
1488
|
+
elif isinstance(value, dict):
|
|
1489
|
+
with self.Map():
|
|
1490
|
+
for k, v in value.items():
|
|
1491
|
+
self.Key(k)
|
|
1492
|
+
self.Add(v)
|
|
1493
|
+
elif isinstance(value, array.array):
|
|
1494
|
+
self.TypedVectorFromElements(value)
|
|
1495
|
+
elif _IsIterable(value):
|
|
1496
|
+
self.VectorFromElements(value)
|
|
1497
|
+
else:
|
|
1498
|
+
raise TypeError('unsupported python type: %s' % type(value))
|
|
1499
|
+
|
|
1500
|
+
@property
|
|
1501
|
+
def LastValue(self):
|
|
1502
|
+
return self._stack[-1]
|
|
1503
|
+
|
|
1504
|
+
@InMap
|
|
1505
|
+
def ReuseValue(self, value):
|
|
1506
|
+
self._stack.append(value)
|
|
1507
|
+
|
|
1508
|
+
|
|
1509
|
+
def GetRoot(buf):
|
|
1510
|
+
"""Returns root `Ref` object for the given buffer."""
|
|
1511
|
+
if len(buf) < 3:
|
|
1512
|
+
raise ValueError('buffer is too small')
|
|
1513
|
+
byte_width = buf[-1]
|
|
1514
|
+
return Ref.PackedType(
|
|
1515
|
+
Buf(buf, -(2 + byte_width)), byte_width, packed_type=buf[-2])
|
|
1516
|
+
|
|
1517
|
+
|
|
1518
|
+
def Dumps(obj):
|
|
1519
|
+
"""Returns bytearray with the encoded python object."""
|
|
1520
|
+
fbb = Builder()
|
|
1521
|
+
fbb.Add(obj)
|
|
1522
|
+
return fbb.Finish()
|
|
1523
|
+
|
|
1524
|
+
|
|
1525
|
+
def Loads(buf):
|
|
1526
|
+
"""Returns python object decoded from the buffer."""
|
|
1527
|
+
return GetRoot(buf).Value
|