jamcodec 0.0.1__tar.gz → 0.1.0__tar.gz
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.
- jamcodec-0.1.0/PKG-INFO +90 -0
- jamcodec-0.1.0/README.md +66 -0
- jamcodec-0.1.0/jamcodec/__init__.py +22 -0
- jamcodec-0.1.0/jamcodec/base.py +370 -0
- jamcodec-0.1.0/jamcodec/constants.py +1 -0
- jamcodec-0.1.0/jamcodec/exceptions.py +43 -0
- jamcodec-0.1.0/jamcodec/mixins.py +100 -0
- jamcodec-0.1.0/jamcodec/types.py +1234 -0
- jamcodec-0.1.0/jamcodec.egg-info/PKG-INFO +90 -0
- jamcodec-0.1.0/jamcodec.egg-info/SOURCES.txt +35 -0
- jamcodec-0.1.0/jamcodec.egg-info/requires.txt +10 -0
- {jamcodec-0.0.1 → jamcodec-0.1.0}/jamcodec.egg-info/top_level.txt +1 -0
- jamcodec-0.1.0/pyproject.toml +48 -0
- jamcodec-0.1.0/requirements.txt +1 -0
- jamcodec-0.1.0/test/test_array.py +82 -0
- jamcodec-0.1.0/test/test_bitarray.py +81 -0
- jamcodec-0.1.0/test/test_bitvec.py +108 -0
- jamcodec-0.1.0/test/test_boolean.py +57 -0
- jamcodec-0.1.0/test/test_bytes.py +71 -0
- jamcodec-0.1.0/test/test_compact.py +123 -0
- jamcodec-0.1.0/test/test_enum.py +65 -0
- jamcodec-0.1.0/test/test_float.py +53 -0
- jamcodec-0.1.0/test/test_hashmap.py +38 -0
- jamcodec-0.1.0/test/test_integer.py +60 -0
- jamcodec-0.1.0/test/test_map.py +87 -0
- jamcodec-0.1.0/test/test_mixins.py +156 -0
- jamcodec-0.1.0/test/test_option.py +61 -0
- jamcodec-0.1.0/test/test_scale_types.py +47 -0
- jamcodec-0.1.0/test/test_scalebytes.py +62 -0
- jamcodec-0.1.0/test/test_string.py +57 -0
- jamcodec-0.1.0/test/test_struct.py +99 -0
- jamcodec-0.1.0/test/test_tuple.py +54 -0
- jamcodec-0.1.0/test/test_varint.py +53 -0
- jamcodec-0.1.0/test/test_vec.py +52 -0
- jamcodec-0.0.1/PKG-INFO +0 -222
- jamcodec-0.0.1/README.md +0 -3
- jamcodec-0.0.1/jamcodec/__init__.py +0 -6
- jamcodec-0.0.1/jamcodec.egg-info/PKG-INFO +0 -222
- jamcodec-0.0.1/jamcodec.egg-info/SOURCES.txt +0 -10
- jamcodec-0.0.1/jamcodec.egg-info/requires.txt +0 -5
- jamcodec-0.0.1/pyproject.toml +0 -39
- jamcodec-0.0.1/requirements.txt +0 -1
- {jamcodec-0.0.1 → jamcodec-0.1.0}/LICENSE +0 -0
- {jamcodec-0.0.1 → jamcodec-0.1.0}/jamcodec.egg-info/dependency_links.txt +0 -0
- {jamcodec-0.0.1 → jamcodec-0.1.0}/setup.cfg +0 -0
jamcodec-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: jamcodec
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python JAM Codec Library
|
|
5
|
+
Author-email: JAMdot Technologies <dev@jamdot.tech>
|
|
6
|
+
Project-URL: Homepage, https://github.com/JAMdotTech/py-jam-codec
|
|
7
|
+
Keywords: jam,codec
|
|
8
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Requires-Python: <4,>=3.8
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: more-itertools>=8.6.0
|
|
16
|
+
Provides-Extra: dev
|
|
17
|
+
Requires-Dist: coverage==5.3; extra == "dev"
|
|
18
|
+
Requires-Dist: pytest>=6.1.2; extra == "dev"
|
|
19
|
+
Requires-Dist: mkdocs; extra == "dev"
|
|
20
|
+
Requires-Dist: mkdocs-material; extra == "dev"
|
|
21
|
+
Requires-Dist: mkdocs-autorefs; extra == "dev"
|
|
22
|
+
Requires-Dist: mkdocstrings; extra == "dev"
|
|
23
|
+
Requires-Dist: mkdocstrings[python]; extra == "dev"
|
|
24
|
+
|
|
25
|
+
# Python JAM Codec
|
|
26
|
+
|
|
27
|
+
[](https://github.com/polkascan/py-scale-codec/actions/workflows/unittests.yml?query=workflow%3A%22Run+unit+tests%22)
|
|
28
|
+
[](https://pypi.org/project/scalecodec/)
|
|
29
|
+
[](https://pypi.org/project/scalecodec/)
|
|
30
|
+
[](https://github.com/polkascan/py-scale-codec/blob/master/LICENSE)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
## Description
|
|
34
|
+
Python implementation of the JAM codec as specified in the graypaper.
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
```bash
|
|
39
|
+
pip install jamcodec
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Code examples
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from jamcodec.types import JamBytes, Bool, String, U32, U8, U16, Struct, Vec, Compact, Tuple, Enum
|
|
46
|
+
|
|
47
|
+
# encode a Vec<u16>
|
|
48
|
+
obj = Vec(U16).new()
|
|
49
|
+
value = [1, 2]
|
|
50
|
+
data = obj.encode(value)
|
|
51
|
+
|
|
52
|
+
# Define and decode a Struct
|
|
53
|
+
scale_obj = Struct(test=U8, test2=Tuple(U8, U8)).new()
|
|
54
|
+
value = scale_obj.decode(JamBytes("0x020105"))
|
|
55
|
+
|
|
56
|
+
# Define and encode an Enum
|
|
57
|
+
scale_obj = Enum(
|
|
58
|
+
Bool=Bool(),
|
|
59
|
+
Number=U32,
|
|
60
|
+
Complex=Struct(data=String(), version=Compact(U8)),
|
|
61
|
+
None_=None
|
|
62
|
+
).new()
|
|
63
|
+
value = {'Bool': True}
|
|
64
|
+
|
|
65
|
+
data = scale_obj.encode(value)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Examples of different types
|
|
69
|
+
|
|
70
|
+
| Type | Description | Example SCALE decoding value | SCALE encoded value |
|
|
71
|
+
|------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------|---------------------------------------------------------------------------------|
|
|
72
|
+
| `bool` | Boolean values are encoded using the least significant bit of a single byte. | `True` | `0x01` |
|
|
73
|
+
| `u16` | Basic integers are encoded using a fixed-width little-endian (LE) format. | `42` | `0x2a00` |
|
|
74
|
+
| `VarInt64` | xxxxxxx | `0` | `0x00` |
|
|
75
|
+
| | | `1` | `0x04` |
|
|
76
|
+
| | | `42` | `0xa8` |
|
|
77
|
+
| | | `69` | `0x1501` |
|
|
78
|
+
| | | `100000000000000` | `0x0b00407a10f35a` |
|
|
79
|
+
| `Vec` | A collection of same-typed values is encoded, prefixed with a compact encoding of the number of items, followed by each item's encoding concatenated in turn. | `[4, 8, 15, 16, 23, 42]` | `0x18040008000f00100017002a00` |
|
|
80
|
+
| `BitVec` | A sequence of bools, represented in a more space efficient bit format | `0b00000010_01111101` | `0x287d02` |
|
|
81
|
+
| `str`,`Bytes`, `String` | Strings are Vectors of bytes (`Vec<u8>`) containing a valid UTF8 sequence. | `"Test"` | `0x1054657374` |
|
|
82
|
+
| | | `b"Test"` | `0x1054657374` |
|
|
83
|
+
| | | `[84, 101, 115, 116]` | `0x1054657374` |
|
|
84
|
+
| `[u8; 4]` | Fixed sized array of in this case an `u8` | `b"babe"` | `0x62616265` |
|
|
85
|
+
| | | `"0x62616265"` | `0x62616265` |
|
|
86
|
+
| | | `[98, 97, 98, 101]` | `0x62616265` |
|
|
87
|
+
| `Enum` Example: `enum IntOrBool { Int(u8), Bool(bool),}` | A fixed number of variants, each mutually exclusive and potentially implying a further value or series of values. Encoded as the first byte identifying the index of the variant that the value is. Any further bytes are used to encode any data that the variant implies. Thus, no more than 256 variants are supported. | `{'Int': 8}` | `0x002a` |
|
|
88
|
+
| | | `{'Bool': True}` | `0x0101` |
|
|
89
|
+
| `Struct` Example: `struct Motion { pub votes: Vec<AccountId>, pub id: u32 }` | For structures, the values are named, but that is irrelevant for the encoding (names are ignored - only order matters). All containers store elements consecutively. The order of the elements is not fixed, depends on the container, and cannot be relied on at decoding. This implicitly means that decoding some byte-array into a specified structure that enforces an order and then re-encoding it could result in a different byte array than the original that was decoded. | `{"votes": ["5GDyPHLVHcQYPTWfygtPYeo gQjyZy7J9fsi4brPhgEFq4pcv"], "id": 4}` | `0x04b80269ec500e458a630846b99105c397ee57 4125823d6f4388e9c7572e115c0504000000` |
|
|
90
|
+
|
jamcodec-0.1.0/README.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Python JAM Codec
|
|
2
|
+
|
|
3
|
+
[](https://github.com/polkascan/py-scale-codec/actions/workflows/unittests.yml?query=workflow%3A%22Run+unit+tests%22)
|
|
4
|
+
[](https://pypi.org/project/scalecodec/)
|
|
5
|
+
[](https://pypi.org/project/scalecodec/)
|
|
6
|
+
[](https://github.com/polkascan/py-scale-codec/blob/master/LICENSE)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
## Description
|
|
10
|
+
Python implementation of the JAM codec as specified in the graypaper.
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
```bash
|
|
15
|
+
pip install jamcodec
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Code examples
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
from jamcodec.types import JamBytes, Bool, String, U32, U8, U16, Struct, Vec, Compact, Tuple, Enum
|
|
22
|
+
|
|
23
|
+
# encode a Vec<u16>
|
|
24
|
+
obj = Vec(U16).new()
|
|
25
|
+
value = [1, 2]
|
|
26
|
+
data = obj.encode(value)
|
|
27
|
+
|
|
28
|
+
# Define and decode a Struct
|
|
29
|
+
scale_obj = Struct(test=U8, test2=Tuple(U8, U8)).new()
|
|
30
|
+
value = scale_obj.decode(JamBytes("0x020105"))
|
|
31
|
+
|
|
32
|
+
# Define and encode an Enum
|
|
33
|
+
scale_obj = Enum(
|
|
34
|
+
Bool=Bool(),
|
|
35
|
+
Number=U32,
|
|
36
|
+
Complex=Struct(data=String(), version=Compact(U8)),
|
|
37
|
+
None_=None
|
|
38
|
+
).new()
|
|
39
|
+
value = {'Bool': True}
|
|
40
|
+
|
|
41
|
+
data = scale_obj.encode(value)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Examples of different types
|
|
45
|
+
|
|
46
|
+
| Type | Description | Example SCALE decoding value | SCALE encoded value |
|
|
47
|
+
|------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------|---------------------------------------------------------------------------------|
|
|
48
|
+
| `bool` | Boolean values are encoded using the least significant bit of a single byte. | `True` | `0x01` |
|
|
49
|
+
| `u16` | Basic integers are encoded using a fixed-width little-endian (LE) format. | `42` | `0x2a00` |
|
|
50
|
+
| `VarInt64` | xxxxxxx | `0` | `0x00` |
|
|
51
|
+
| | | `1` | `0x04` |
|
|
52
|
+
| | | `42` | `0xa8` |
|
|
53
|
+
| | | `69` | `0x1501` |
|
|
54
|
+
| | | `100000000000000` | `0x0b00407a10f35a` |
|
|
55
|
+
| `Vec` | A collection of same-typed values is encoded, prefixed with a compact encoding of the number of items, followed by each item's encoding concatenated in turn. | `[4, 8, 15, 16, 23, 42]` | `0x18040008000f00100017002a00` |
|
|
56
|
+
| `BitVec` | A sequence of bools, represented in a more space efficient bit format | `0b00000010_01111101` | `0x287d02` |
|
|
57
|
+
| `str`,`Bytes`, `String` | Strings are Vectors of bytes (`Vec<u8>`) containing a valid UTF8 sequence. | `"Test"` | `0x1054657374` |
|
|
58
|
+
| | | `b"Test"` | `0x1054657374` |
|
|
59
|
+
| | | `[84, 101, 115, 116]` | `0x1054657374` |
|
|
60
|
+
| `[u8; 4]` | Fixed sized array of in this case an `u8` | `b"babe"` | `0x62616265` |
|
|
61
|
+
| | | `"0x62616265"` | `0x62616265` |
|
|
62
|
+
| | | `[98, 97, 98, 101]` | `0x62616265` |
|
|
63
|
+
| `Enum` Example: `enum IntOrBool { Int(u8), Bool(bool),}` | A fixed number of variants, each mutually exclusive and potentially implying a further value or series of values. Encoded as the first byte identifying the index of the variant that the value is. Any further bytes are used to encode any data that the variant implies. Thus, no more than 256 variants are supported. | `{'Int': 8}` | `0x002a` |
|
|
64
|
+
| | | `{'Bool': True}` | `0x0101` |
|
|
65
|
+
| `Struct` Example: `struct Motion { pub votes: Vec<AccountId>, pub id: u32 }` | For structures, the values are named, but that is irrelevant for the encoding (names are ignored - only order matters). All containers store elements consecutively. The order of the elements is not fixed, depends on the container, and cannot be relied on at decoding. This implicitly means that decoding some byte-array into a specified structure that enforces an order and then re-encoding it could result in a different byte array than the original that was decoded. | `{"votes": ["5GDyPHLVHcQYPTWfygtPYeo gQjyZy7J9fsi4brPhgEFq4pcv"], "id": 4}` | `0x04b80269ec500e458a630846b99105c397ee57 4125823d6f4388e9c7572e115c0504000000` |
|
|
66
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Python SCALE Codec Library
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2018-2024 Stichting Polkascan (Polkascan Foundation).
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
import os
|
|
18
|
+
|
|
19
|
+
if os.getenv('GITHUB_REF') and os.getenv('GITHUB_REF').startswith('refs/tags/v'):
|
|
20
|
+
__version__ = os.getenv('GITHUB_REF').replace('refs/tags/v', '')
|
|
21
|
+
else:
|
|
22
|
+
__version__ = '0.0.0-dev1'
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
# Python SCALE Codec Library
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2018-2024 Stichting Polkascan (Polkascan Foundation).
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
import typing
|
|
17
|
+
from abc import abstractmethod
|
|
18
|
+
from typing import Optional, Union
|
|
19
|
+
|
|
20
|
+
from jamcodec.constants import TYPE_DECOMP_MAX_RECURSIVE
|
|
21
|
+
from jamcodec.exceptions import RemainingScaleBytesNotEmptyException, ScaleDecodeException
|
|
22
|
+
|
|
23
|
+
if typing.TYPE_CHECKING:
|
|
24
|
+
from jamcodec.mixins import Serializable
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class JamBytes:
|
|
28
|
+
"""
|
|
29
|
+
Representation of SCALE encoded Bytes.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(self, data: Union[str, bytes, bytearray, int]):
|
|
33
|
+
"""
|
|
34
|
+
Constructs a SCALE bytes-stream with provided `data`
|
|
35
|
+
|
|
36
|
+
Parameters
|
|
37
|
+
----------
|
|
38
|
+
data
|
|
39
|
+
"""
|
|
40
|
+
self.offset = 0
|
|
41
|
+
|
|
42
|
+
if type(data) is bytearray:
|
|
43
|
+
self.data = data
|
|
44
|
+
elif type(data) is bytes:
|
|
45
|
+
self.data = bytearray(data)
|
|
46
|
+
elif type(data) is str and data[0:2] == '0x':
|
|
47
|
+
self.data = bytearray.fromhex(data[2:])
|
|
48
|
+
else:
|
|
49
|
+
raise ValueError("Provided data is not in supported format: provided '{}'".format(type(data)))
|
|
50
|
+
|
|
51
|
+
self.length = len(self.data)
|
|
52
|
+
|
|
53
|
+
def get_next_bytes(self, length: int) -> bytearray:
|
|
54
|
+
"""
|
|
55
|
+
Retrieve `length` amount of bytes of the stream
|
|
56
|
+
|
|
57
|
+
Parameters
|
|
58
|
+
----------
|
|
59
|
+
length: amount of requested bytes
|
|
60
|
+
|
|
61
|
+
Returns
|
|
62
|
+
-------
|
|
63
|
+
bytearray
|
|
64
|
+
"""
|
|
65
|
+
if self.offset + length > self.length:
|
|
66
|
+
raise RemainingScaleBytesNotEmptyException(
|
|
67
|
+
f'No more bytes available (needed: {self.offset + length} / total: {self.length})'
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
data = self.data[self.offset:self.offset + length]
|
|
71
|
+
self.offset += length
|
|
72
|
+
return data
|
|
73
|
+
|
|
74
|
+
def get_remaining_bytes(self) -> bytearray:
|
|
75
|
+
"""
|
|
76
|
+
Retrieves all remaining bytes from the stream
|
|
77
|
+
|
|
78
|
+
Returns
|
|
79
|
+
-------
|
|
80
|
+
bytearray
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
data = self.data[self.offset:]
|
|
84
|
+
self.offset = self.length
|
|
85
|
+
return data
|
|
86
|
+
|
|
87
|
+
def get_remaining_length(self) -> int:
|
|
88
|
+
"""
|
|
89
|
+
Returns how many bytes are left in the stream
|
|
90
|
+
|
|
91
|
+
Returns
|
|
92
|
+
-------
|
|
93
|
+
int
|
|
94
|
+
"""
|
|
95
|
+
return self.length - self.offset
|
|
96
|
+
|
|
97
|
+
def reset(self):
|
|
98
|
+
"""
|
|
99
|
+
Resets the pointer of the stream to the beginning
|
|
100
|
+
|
|
101
|
+
Returns
|
|
102
|
+
-------
|
|
103
|
+
|
|
104
|
+
"""
|
|
105
|
+
self.offset = 0
|
|
106
|
+
|
|
107
|
+
def copy(self):
|
|
108
|
+
return JamBytes(self.data)
|
|
109
|
+
|
|
110
|
+
def __str__(self):
|
|
111
|
+
return "0x{}".format(self.data.hex())
|
|
112
|
+
|
|
113
|
+
def __eq__(self, other):
|
|
114
|
+
if not hasattr(other, 'data'):
|
|
115
|
+
return False
|
|
116
|
+
return self.data == other.data
|
|
117
|
+
|
|
118
|
+
def __len__(self):
|
|
119
|
+
return len(self.data)
|
|
120
|
+
|
|
121
|
+
def __repr__(self):
|
|
122
|
+
return "<{}(data=0x{})>".format(self.__class__.__name__, self.data.hex())
|
|
123
|
+
|
|
124
|
+
def __add__(self, data):
|
|
125
|
+
|
|
126
|
+
if type(data) is JamBytes:
|
|
127
|
+
return JamBytes(self.data + data.data)
|
|
128
|
+
|
|
129
|
+
if type(data) is bytes:
|
|
130
|
+
data = bytearray(data)
|
|
131
|
+
elif type(data) == str and data[0:2] == '0x':
|
|
132
|
+
data = bytearray.fromhex(data[2:])
|
|
133
|
+
|
|
134
|
+
if type(data) is bytearray:
|
|
135
|
+
return JamBytes(self.data + data)
|
|
136
|
+
|
|
137
|
+
def __bytes__(self):
|
|
138
|
+
return self.to_bytes()
|
|
139
|
+
|
|
140
|
+
def to_bytes(self):
|
|
141
|
+
return bytes(self.data)
|
|
142
|
+
|
|
143
|
+
def to_hex(self) -> str:
|
|
144
|
+
"""
|
|
145
|
+
Return a hex-string (e.g. "0x00") representation of the byte-stream
|
|
146
|
+
|
|
147
|
+
Returns
|
|
148
|
+
-------
|
|
149
|
+
str
|
|
150
|
+
"""
|
|
151
|
+
return f'0x{self.data.hex()}'
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class JamCodecTypeDef:
|
|
155
|
+
|
|
156
|
+
scale_type_cls = None
|
|
157
|
+
_deserialize_type = None
|
|
158
|
+
|
|
159
|
+
def __init__(self, name: str = None, metadata=None):
|
|
160
|
+
if self.scale_type_cls is None:
|
|
161
|
+
self.scale_type_cls = JamCodecType
|
|
162
|
+
self.name = name
|
|
163
|
+
self.runtime_config = None
|
|
164
|
+
self.metadata = metadata
|
|
165
|
+
|
|
166
|
+
def new(self, **kwargs) -> 'JamCodecType':
|
|
167
|
+
obj = self.scale_type_cls(type_def=self, **kwargs)
|
|
168
|
+
if 'value' in kwargs:
|
|
169
|
+
obj.deserialize(kwargs['value'])
|
|
170
|
+
|
|
171
|
+
if 'scale' in kwargs:
|
|
172
|
+
obj.decode(kwargs['scale'])
|
|
173
|
+
|
|
174
|
+
return obj
|
|
175
|
+
|
|
176
|
+
def impl(self, scale_type_cls: type = None, runtime_config=None) -> 'JamCodecTypeDef':
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
object:
|
|
181
|
+
"""
|
|
182
|
+
if scale_type_cls:
|
|
183
|
+
self.scale_type_cls = scale_type_cls
|
|
184
|
+
if runtime_config:
|
|
185
|
+
self.runtime_config = runtime_config
|
|
186
|
+
|
|
187
|
+
return self
|
|
188
|
+
|
|
189
|
+
# def create_from_registry_type(self, registry_type):
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def encode(self, value: any) -> JamBytes:
|
|
193
|
+
pass
|
|
194
|
+
|
|
195
|
+
@abstractmethod
|
|
196
|
+
def decode(self, data: JamBytes) -> any:
|
|
197
|
+
pass
|
|
198
|
+
|
|
199
|
+
@abstractmethod
|
|
200
|
+
def serialize(self, value: any) -> any:
|
|
201
|
+
raise NotImplementedError()
|
|
202
|
+
|
|
203
|
+
@abstractmethod
|
|
204
|
+
def deserialize(self, value: any) -> any:
|
|
205
|
+
raise NotImplementedError()
|
|
206
|
+
|
|
207
|
+
def to_serializable_obj(self, value_object: any) -> 'Serializable':
|
|
208
|
+
return value_object
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
# TODO implement
|
|
212
|
+
|
|
213
|
+
@abstractmethod
|
|
214
|
+
def example_value(self, _recursion_level: int = 0, max_recursion: int = TYPE_DECOMP_MAX_RECURSIVE):
|
|
215
|
+
|
|
216
|
+
if _recursion_level > max_recursion:
|
|
217
|
+
return self.__class__.__name__
|
|
218
|
+
return self.__class__.__name__
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
class JamCodecType:
|
|
222
|
+
|
|
223
|
+
def __init__(self, type_def: JamCodecTypeDef, **kwargs):
|
|
224
|
+
# TODO remove kwargs
|
|
225
|
+
|
|
226
|
+
self.meta_info = None
|
|
227
|
+
self.type_def: JamCodecTypeDef = type_def
|
|
228
|
+
self.value_serialized = None
|
|
229
|
+
self.value_object = None
|
|
230
|
+
|
|
231
|
+
self._data = None
|
|
232
|
+
self._data_start_offset = 0
|
|
233
|
+
self._data_end_offset = 0
|
|
234
|
+
|
|
235
|
+
super().__init__()
|
|
236
|
+
|
|
237
|
+
def encode(self, value: Optional[any] = None) -> JamBytes:
|
|
238
|
+
if value is not None and issubclass(self.__class__, value.__class__):
|
|
239
|
+
# Accept instance of current class directly
|
|
240
|
+
self._data = value.data
|
|
241
|
+
self.value_object = value.value_object
|
|
242
|
+
self.value_serialized = value.value_serialized
|
|
243
|
+
return value.data
|
|
244
|
+
|
|
245
|
+
if value is None:
|
|
246
|
+
value = self.value_serialized
|
|
247
|
+
|
|
248
|
+
self._data = self.type_def.encode(value)
|
|
249
|
+
self._data_start_offset = self._data.offset
|
|
250
|
+
self._data_end_offset = self._data.length
|
|
251
|
+
|
|
252
|
+
self.value_serialized = value
|
|
253
|
+
self.value_object = self.deserialize(value)
|
|
254
|
+
|
|
255
|
+
return self._data
|
|
256
|
+
|
|
257
|
+
def decode(self, data: JamBytes, check_remaining=False) -> any:
|
|
258
|
+
self._data = data
|
|
259
|
+
self._data_start_offset = data.offset
|
|
260
|
+
# Decode type
|
|
261
|
+
self.value_object = self.type_def.decode(data)
|
|
262
|
+
|
|
263
|
+
self._data_end_offset = data.offset
|
|
264
|
+
|
|
265
|
+
if check_remaining and self._data_end_offset != self._data.length:
|
|
266
|
+
raise ScaleDecodeException(
|
|
267
|
+
f'Remaining ScaleBytes - Current offset: {self._data_end_offset} / length: {self._data.length}'
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
self.value_serialized = self.serialize()
|
|
271
|
+
return self.value_serialized
|
|
272
|
+
|
|
273
|
+
def serialize(self) -> Union[int, str, dict, tuple, bool]:
|
|
274
|
+
self.value_serialized = self.type_def.serialize(self.value_object)
|
|
275
|
+
return self.value_serialized
|
|
276
|
+
|
|
277
|
+
def deserialize(self, value_serialized: any):
|
|
278
|
+
if value_serialized and issubclass(self.__class__, self.value_serialized.__class__):
|
|
279
|
+
# Accept instance of current class directly
|
|
280
|
+
self.value_object = self.value_serialized.value_object
|
|
281
|
+
self.value_serialized = self.value_serialized.value_serialized
|
|
282
|
+
return self.value_object
|
|
283
|
+
|
|
284
|
+
self.value_object = self.type_def.deserialize(value_serialized)
|
|
285
|
+
self.value_serialized = self.type_def.serialize(self.value_object)
|
|
286
|
+
|
|
287
|
+
return self.value_object
|
|
288
|
+
|
|
289
|
+
def to_serializable_obj(self):
|
|
290
|
+
return self.type_def.to_serializable_obj(self.value_object)
|
|
291
|
+
|
|
292
|
+
@property
|
|
293
|
+
def value(self):
|
|
294
|
+
return self.value_serialized
|
|
295
|
+
|
|
296
|
+
@value.setter
|
|
297
|
+
def value(self, value):
|
|
298
|
+
self.value_serialized = value
|
|
299
|
+
|
|
300
|
+
@property
|
|
301
|
+
def data(self) -> Optional[JamBytes]:
|
|
302
|
+
"""
|
|
303
|
+
Returns a ScaleBytes instance of the SCALE-bytes used in the decoding process
|
|
304
|
+
|
|
305
|
+
Returns
|
|
306
|
+
-------
|
|
307
|
+
bytearray
|
|
308
|
+
"""
|
|
309
|
+
if self._data is not None:
|
|
310
|
+
return JamBytes(self._data.data[self._data_start_offset:self._data_end_offset])
|
|
311
|
+
|
|
312
|
+
def example_value(self):
|
|
313
|
+
return self.type_def.example_value()
|
|
314
|
+
|
|
315
|
+
def __repr__(self):
|
|
316
|
+
|
|
317
|
+
# if self.__class__ is not ScaleType:
|
|
318
|
+
# name = self.__class__.__name__
|
|
319
|
+
# else:
|
|
320
|
+
# name = self.type_def.__class__.__name__
|
|
321
|
+
|
|
322
|
+
name = self.type_def.__class__.__name__
|
|
323
|
+
|
|
324
|
+
if self.value_serialized is not None:
|
|
325
|
+
return f"<{name}(value={self.value_serialized})>"
|
|
326
|
+
elif self.data:
|
|
327
|
+
return f"<{name}(data={self.data.to_hex()})>"
|
|
328
|
+
else:
|
|
329
|
+
return f"<{name}>"
|
|
330
|
+
|
|
331
|
+
def __getitem__(self, item):
|
|
332
|
+
return self.value_object[item]
|
|
333
|
+
|
|
334
|
+
def __iter__(self):
|
|
335
|
+
for item in self.value_object:
|
|
336
|
+
yield item
|
|
337
|
+
|
|
338
|
+
def __eq__(self, other):
|
|
339
|
+
if isinstance(other, JamCodecType):
|
|
340
|
+
return other.value_serialized == self.value_serialized
|
|
341
|
+
else:
|
|
342
|
+
return other == self.value_serialized
|
|
343
|
+
|
|
344
|
+
def __gt__(self, other):
|
|
345
|
+
if isinstance(other, JamCodecType):
|
|
346
|
+
return self.value_serialized > other.value_serialized
|
|
347
|
+
else:
|
|
348
|
+
return self.value_serialized > other
|
|
349
|
+
|
|
350
|
+
def __ge__(self, other):
|
|
351
|
+
if isinstance(other, JamCodecType):
|
|
352
|
+
return self.value_serialized >= other.value_serialized
|
|
353
|
+
else:
|
|
354
|
+
return self.value_serialized >= other
|
|
355
|
+
|
|
356
|
+
def __lt__(self, other):
|
|
357
|
+
if isinstance(other, JamCodecType):
|
|
358
|
+
return self.value_serialized < other.value_serialized
|
|
359
|
+
else:
|
|
360
|
+
return self.value_serialized < other
|
|
361
|
+
|
|
362
|
+
def __le__(self, other):
|
|
363
|
+
if isinstance(other, JamCodecType):
|
|
364
|
+
return self.value_serialized <= other.value_serialized
|
|
365
|
+
else:
|
|
366
|
+
return self.value_serialized <= other
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
class JamCodecPrimitive(JamCodecTypeDef):
|
|
370
|
+
pass
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
TYPE_DECOMP_MAX_RECURSIVE = 9
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Python SCALE Codec Library
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2018-2024 Stichting Polkascan (Polkascan Foundation).
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class RemainingScaleBytesNotEmptyException(Exception):
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class InvalidScaleTypeValueException(Exception):
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class MetadataCallFunctionNotFound(ValueError):
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ScaleDecodeException(ValueError):
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ScaleEncodeException(ValueError):
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ScaleDeserializeException(ValueError):
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ScaleSerializeException(ValueError):
|
|
43
|
+
pass
|