Skip to content

Commit 1aa68d4

Browse files
committed
add ext support
1 parent 7164328 commit 1aa68d4

File tree

4 files changed

+122
-4
lines changed

4 files changed

+122
-4
lines changed

src/msgpack_stream/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22

33
from ._io import pack as pack, unpack as unpack
44
from ._msgpack import pack_stream as pack_stream, unpack_stream as unpack_stream
5+
from ._ext import ExtType as ExtType

src/msgpack_stream/_ext.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
class ExtType:
2+
__slots__ = ("code", "data")
3+
4+
def __init__(self, code: int, data: bytes):
5+
self.code = code
6+
self.data = data
7+
8+
def __eq__(self, other):
9+
if isinstance(other, ExtType):
10+
return self.code == other.code and self.data == other.data
11+
return False

src/msgpack_stream/_msgpack.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
f32_b_t,
1212
f64_b_t,
1313
)
14+
from ._ext import ExtType
1415

1516
# deference for performance
1617

@@ -49,6 +50,7 @@
4950
f64_b_unpack = f64_b_t.unpack
5051

5152
_B = tuple([bytes([i]) for i in range(256)])
53+
_PO2 = {1: b"\xd4", 2: b"\xd5", 4: b"\xd6", 8: b"\xd7", 16: b"\xd8"}
5254

5355

5456
def pack_stream(stream, obj):
@@ -137,6 +139,21 @@ def pack_stream(stream, obj):
137139
else:
138140
raise ValueError("bin too large", obj)
139141
stream.write(obj)
142+
elif _type is ExtType:
143+
data = obj.data
144+
p_code = s8_b_pack(obj.code)
145+
extl = len(data)
146+
if extl <= 16 and extl in _PO2: # fixext (0xD4 - 0xD8)
147+
stream.write(_PO2[extl] + p_code)
148+
elif extl <= 0xFF: # ext8
149+
stream.write(b"\xc7" + _B[extl] + p_code)
150+
elif extl <= 0xFF_FF: # ext16
151+
stream.write(b"\xc8" + u16_b_pack(extl) + p_code)
152+
elif extl <= 0xFF_FF_FF_FF: # ext32
153+
stream.write(b"\xc9" + u32_b_pack(extl) + p_code)
154+
else:
155+
raise ValueError("ext too large", obj)
156+
stream.write(data)
140157
else:
141158
raise TypeError("type not supported:", obj, _type)
142159

@@ -172,8 +189,14 @@ def unpack_stream(stream):
172189
elif first_byte == 0xC6:
173190
bl = u32_b_unpack(stream)
174191
obj = stream.read(bl)
175-
elif 0xC7 <= first_byte <= 0xC9:
176-
raise NotImplementedError
192+
elif first_byte <= 0xC9: # ext (0xC7 - 0xC9)
193+
if first_byte == 0xC7: # ext8
194+
extl = u8_b_unpack(stream)
195+
elif first_byte == 0xC8: # ext16
196+
extl = u16_b_unpack(stream)
197+
else: # ext32
198+
extl = u32_b_unpack(stream)
199+
obj = ExtType(s8_b_unpack(stream), stream.read(extl))
177200
elif first_byte == 0xCA:
178201
obj = f32_b_unpack(stream)
179202
elif first_byte == 0xCB:
@@ -194,8 +217,8 @@ def unpack_stream(stream):
194217
obj = s32_b_unpack(stream)
195218
elif first_byte == 0xD3:
196219
obj = s64_b_unpack(stream)
197-
elif 0xD4 <= first_byte <= 0xD8:
198-
raise NotImplementedError
220+
elif first_byte <= 0xD8: # fixext (0xD4 - 0xD8)
221+
obj = ExtType(s8_b_unpack(stream), stream.read(1 << (first_byte - 0xD4)))
199222
elif first_byte == 0xD9:
200223
sl = u8_b_unpack(stream)
201224
obj = stream.read(sl).decode("utf-8")

tests/test_ext.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import io
2+
import pytest
3+
from msgpack_stream import pack_stream, unpack_stream, ExtType
4+
5+
6+
def test_fixext1():
7+
stream = io.BytesIO()
8+
obj = ExtType(1, b"a")
9+
pack_stream(stream, obj)
10+
stream.seek(0)
11+
assert stream.read(1) == b"\xd4"
12+
stream.seek(0)
13+
assert unpack_stream(stream) == obj
14+
15+
16+
def test_fixext2():
17+
stream = io.BytesIO()
18+
obj = ExtType(2, b"ab")
19+
pack_stream(stream, obj)
20+
stream.seek(0)
21+
assert stream.read(1) == b"\xd5"
22+
stream.seek(0)
23+
assert unpack_stream(stream) == obj
24+
25+
26+
def test_fixext4():
27+
stream = io.BytesIO()
28+
obj = ExtType(3, b"abcd")
29+
pack_stream(stream, obj)
30+
stream.seek(0)
31+
assert stream.read(1) == b"\xd6"
32+
stream.seek(0)
33+
assert unpack_stream(stream) == obj
34+
35+
36+
def test_fixext8():
37+
stream = io.BytesIO()
38+
obj = ExtType(4, b"a" * 8)
39+
pack_stream(stream, obj)
40+
stream.seek(0)
41+
assert stream.read(1) == b"\xd7"
42+
stream.seek(0)
43+
assert unpack_stream(stream) == obj
44+
45+
46+
def test_fixext16():
47+
stream = io.BytesIO()
48+
obj = ExtType(5, b"a" * 16)
49+
pack_stream(stream, obj)
50+
stream.seek(0)
51+
assert stream.read(1) == b"\xd8"
52+
stream.seek(0)
53+
assert unpack_stream(stream) == obj
54+
55+
56+
def test_ext8():
57+
stream = io.BytesIO()
58+
obj = ExtType(6, b"abc")
59+
pack_stream(stream, obj)
60+
stream.seek(0)
61+
assert stream.read(1) == b"\xc7"
62+
stream.seek(0)
63+
assert unpack_stream(stream) == obj
64+
65+
66+
def test_ext16():
67+
stream = io.BytesIO()
68+
obj = ExtType(7, b"a" * 256)
69+
pack_stream(stream, obj)
70+
stream.seek(0)
71+
assert stream.read(1) == b"\xc8"
72+
stream.seek(0)
73+
assert unpack_stream(stream) == obj
74+
75+
76+
def test_ext32():
77+
stream = io.BytesIO()
78+
obj = ExtType(8, b"a" * 65536)
79+
pack_stream(stream, obj)
80+
stream.seek(0)
81+
assert stream.read(1) == b"\xc9"
82+
stream.seek(0)
83+
assert unpack_stream(stream) == obj

0 commit comments

Comments
 (0)