Skip to content

Commit 52fcb8e

Browse files
iabdalkaderdpgeorge
authored andcommitted
cbor2: Add cbor2 library.
This aims to follow the API of the cbor2 library found at https://github.com/agronholm/cbor2 (also on PyPI as cbor2). The original source for this MicroPython version of cbor2 is from https://github.com/kpn-iot/senml-micropython-library.
1 parent 78900af commit 52fcb8e

File tree

5 files changed

+516
-0
lines changed

5 files changed

+516
-0
lines changed

python-ecosys/cbor2/cbor2/__init__.py

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""
2+
The MIT License (MIT)
3+
4+
Copyright (c) 2023 Arduino SA
5+
Copyright (c) 2018 KPN (Jan Bogaerts)
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a copy
8+
of this software and associated documentation files (the "Software"), to deal
9+
in the Software without restriction, including without limitation the rights
10+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the Software is
12+
furnished to do so, subject to the following conditions:
13+
14+
The above copyright notice and this permission notice shall be included in
15+
all copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
THE SOFTWARE.
24+
"""
25+
26+
27+
from . import decoder
28+
from . import encoder

python-ecosys/cbor2/cbor2/decoder.py

+262
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
"""
2+
The MIT License (MIT)
3+
4+
Copyright (c) 2023 Arduino SA
5+
Copyright (c) 2018 KPN (Jan Bogaerts)
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a copy
8+
of this software and associated documentation files (the "Software"), to deal
9+
in the Software without restriction, including without limitation the rights
10+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the Software is
12+
furnished to do so, subject to the following conditions:
13+
14+
The above copyright notice and this permission notice shall be included in
15+
all copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
THE SOFTWARE.
24+
"""
25+
26+
27+
import uio
28+
import ustruct as struct
29+
30+
31+
class CBORDecodeError(Exception):
32+
"""Raised when an error occurs deserializing a CBOR datastream."""
33+
34+
35+
break_marker = object()
36+
37+
38+
class CBORSimpleValue(object):
39+
"""
40+
Represents a CBOR "simple value".
41+
:param int value: the value (0-255)
42+
"""
43+
44+
def __init__(self, value):
45+
if value < 0 or value > 255:
46+
raise TypeError("simple value too big")
47+
self.value = value
48+
49+
def __eq__(self, other):
50+
if isinstance(other, CBORSimpleValue):
51+
return self.value == other.value
52+
elif isinstance(other, int):
53+
return self.value == other
54+
return NotImplemented
55+
56+
def __repr__(self):
57+
return "CBORSimpleValue({self.value})".format(self=self)
58+
59+
60+
def decode_uint(decoder, subtype, allow_indefinite=False):
61+
# Major tag 0
62+
if subtype < 24:
63+
return subtype
64+
elif subtype == 24:
65+
return struct.unpack(">B", decoder.read(1))[0]
66+
elif subtype == 25:
67+
return struct.unpack(">H", decoder.read(2))[0]
68+
elif subtype == 26:
69+
return struct.unpack(">L", decoder.read(4))[0]
70+
elif subtype == 27:
71+
return struct.unpack(">Q", decoder.read(8))[0]
72+
elif subtype == 31 and allow_indefinite:
73+
return None
74+
else:
75+
raise CBORDecodeError("unknown unsigned integer subtype 0x%x" % subtype)
76+
77+
78+
def decode_negint(decoder, subtype):
79+
# Major tag 1
80+
uint = decode_uint(decoder, subtype)
81+
return -uint - 1
82+
83+
84+
def decode_bytestring(decoder, subtype):
85+
# Major tag 2
86+
length = decode_uint(decoder, subtype, allow_indefinite=True)
87+
if length is None:
88+
# Indefinite length
89+
buf = bytearray()
90+
while True:
91+
initial_byte = decoder.read(1)[0]
92+
if initial_byte == 255:
93+
return buf
94+
else:
95+
length = decode_uint(decoder, initial_byte & 31)
96+
value = decoder.read(length)
97+
buf.extend(value)
98+
else:
99+
return decoder.read(length)
100+
101+
102+
def decode_string(decoder, subtype):
103+
# Major tag 3
104+
return decode_bytestring(decoder, subtype).decode("utf-8")
105+
106+
107+
def decode_array(decoder, subtype):
108+
# Major tag 4
109+
items = []
110+
length = decode_uint(decoder, subtype, allow_indefinite=True)
111+
if length is None:
112+
# Indefinite length
113+
while True:
114+
value = decoder.decode()
115+
if value is break_marker:
116+
break
117+
else:
118+
items.append(value)
119+
else:
120+
for _ in range(length):
121+
item = decoder.decode()
122+
items.append(item)
123+
return items
124+
125+
126+
def decode_map(decoder, subtype):
127+
# Major tag 5
128+
dictionary = {}
129+
length = decode_uint(decoder, subtype, allow_indefinite=True)
130+
if length is None:
131+
# Indefinite length
132+
while True:
133+
key = decoder.decode()
134+
if key is break_marker:
135+
break
136+
else:
137+
value = decoder.decode()
138+
dictionary[key] = value
139+
else:
140+
for _ in range(length):
141+
key = decoder.decode()
142+
value = decoder.decode()
143+
dictionary[key] = value
144+
145+
return dictionary
146+
147+
148+
def decode_special(decoder, subtype):
149+
# Simple value
150+
if subtype < 20:
151+
return CBORSimpleValue(subtype)
152+
153+
# Major tag 7
154+
return special_decoders[subtype](decoder)
155+
156+
157+
def decode_simple_value(decoder):
158+
return CBORSimpleValue(struct.unpack(">B", decoder.read(1))[0])
159+
160+
161+
def decode_float16(decoder):
162+
payload = decoder.read(2)
163+
return unpack_float16(payload)
164+
165+
166+
def decode_float32(decoder):
167+
return struct.unpack(">f", decoder.read(4))[0]
168+
169+
170+
def decode_float64(decoder):
171+
return struct.unpack(">d", decoder.read(8))[0]
172+
173+
174+
major_decoders = {
175+
0: decode_uint,
176+
1: decode_negint,
177+
2: decode_bytestring,
178+
3: decode_string,
179+
4: decode_array,
180+
5: decode_map,
181+
7: decode_special,
182+
}
183+
184+
special_decoders = {
185+
20: lambda self: False,
186+
21: lambda self: True,
187+
22: lambda self: None,
188+
23: lambda self: undefined,
189+
24: decode_simple_value,
190+
25: decode_float16,
191+
26: decode_float32,
192+
27: decode_float64,
193+
31: lambda self: break_marker,
194+
}
195+
196+
197+
class CBORDecoder(object):
198+
"""
199+
Deserializes a CBOR encoded byte stream.
200+
"""
201+
202+
def __init__(self, fp):
203+
self.fp = fp
204+
205+
def read(self, amount):
206+
"""
207+
Read bytes from the data stream.
208+
:param int amount: the number of bytes to read
209+
"""
210+
data = self.fp.read(amount)
211+
if len(data) < amount:
212+
raise CBORDecodeError(
213+
"premature end of stream (expected to read {} bytes, got {} "
214+
"instead)".format(amount, len(data))
215+
)
216+
217+
return data
218+
219+
def decode(self):
220+
"""
221+
Decode the next value from the stream.
222+
:raises CBORDecodeError: if there is any problem decoding the stream
223+
"""
224+
try:
225+
initial_byte = self.fp.read(1)[0]
226+
major_type = initial_byte >> 5
227+
subtype = initial_byte & 31
228+
except Exception as e:
229+
raise CBORDecodeError(
230+
"error reading major type at index {}: {}".format(self.fp.tell(), e)
231+
)
232+
233+
decoder = major_decoders[major_type]
234+
try:
235+
return decoder(self, subtype)
236+
except CBORDecodeError:
237+
raise
238+
except Exception as e:
239+
raise CBORDecodeError(
240+
"error decoding value {}".format(e)
241+
) # tell doesn't work on micropython at the moment
242+
243+
244+
def loads(payload, **kwargs):
245+
"""
246+
Deserialize an object from a bytestring.
247+
:param bytes payload: the bytestring to serialize
248+
:param kwargs: keyword arguments passed to :class:`~.CBORDecoder`
249+
:return: the deserialized object
250+
"""
251+
fp = uio.BytesIO(payload)
252+
return CBORDecoder(fp, **kwargs).decode()
253+
254+
255+
def load(fp, **kwargs):
256+
"""
257+
Deserialize an object from an open file.
258+
:param fp: the input file (any file-like object)
259+
:param kwargs: keyword arguments passed to :class:`~.CBORDecoder`
260+
:return: the deserialized object
261+
"""
262+
return CBORDecoder(fp, **kwargs).decode()

0 commit comments

Comments
 (0)