Skip to content

Commit 2407780

Browse files
authored
Merge pull request #2 from FusionSolutions/patch
Update to v0.1.0
2 parents e106058 + 58988b5 commit 2407780

File tree

11 files changed

+123
-96
lines changed

11 files changed

+123
-96
lines changed

.github/workflows/python-package.yml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,17 @@ jobs:
2323
- name: Install dependencies
2424
run: |
2525
python -m pip install --upgrade pip
26-
python -m pip install mypy
26+
python -m pip install mypy pyflakes
2727
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
2828
- name: MyPy check
2929
run: |
30-
mypy fsPacker
30+
mypy --strict fsPacker
31+
- name: PyFlakes check
32+
run: |
33+
pyflakes fsPacker
3134
- name: Install package
3235
run: |
3336
python setup.py install
34-
- name: Unittest
37+
- name: Test package
3538
run: |
36-
python -m fsPacker
37-
39+
python setup.py test

.github/workflows/python-publish.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ jobs:
2626
- name: Install dependencies
2727
run: |
2828
python -m pip install --upgrade pip
29-
python -m fsPacker
3029
pip install build
3130
- name: Build package
3231
run: python -m build
@@ -35,4 +34,3 @@ jobs:
3534
with:
3635
user: __token__
3736
password: ${{ secrets.PYPI_API_TOKEN }}
38-

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,4 +127,3 @@ dmypy.json
127127

128128
# Pyre type checker
129129
.pyre/
130-

README.md

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ The following types are supported for packing and unpacking:
1616
- `list` (during unpacking it will be converted to `tuple`)
1717
- `tuple`
1818
- `dict` (dict key type can be any from this list)
19+
- `set` (full support)
1920

2021
## Installation
2122

@@ -57,42 +58,42 @@ $/python-fspacker: python3 -m benchmark
5758
Test data one [1 times]
5859
pickle
5960
dump size: 369436 byte
60-
dump : best: 0.00192141 <- median: 0.00303648 - average: 0.00289015 -> worst: 0.00357831
61-
loads: best: 0.00176141 <- median: 0.00193337 - average: 0.00192555 -> worst: 0.00245063
61+
dump : best: 0.00158157 <- median: 0.00163573 - average: 0.00164407 -> worst: 0.00173204
62+
loads: best: 0.00157589 <- median: 0.00160991 - average: 0.00162757 -> worst: 0.00216137
6263
marshal
6364
dump size: 474624 byte
64-
dump : best: 0.00091804 <- median: 0.00146398 - average: 0.00146232 -> worst: 0.00209858
65-
loads: best: 0.00144557 <- median: 0.00150470 - average: 0.00159074 -> worst: 0.00230714
65+
dump : best: 0.00084558 <- median: 0.00089093 - average: 0.00089481 -> worst: 0.00096658
66+
loads: best: 0.00135764 <- median: 0.00137184 - average: 0.00138647 -> worst: 0.00170787
6667
FSPacker
67-
dump size: 329289 byte
68-
dump : best: 0.02956123 <- median: 0.03191117 - average: 0.03488318 -> worst: 0.06962921
69-
loads: best: 0.02214191 <- median: 0.02311805 - average: 0.02343034 -> worst: 0.02914553
68+
dump size: 329293 byte
69+
dump : best: 0.03001423 <- median: 0.03062541 - average: 0.03170544 -> worst: 0.04718978
70+
loads: best: 0.02231823 <- median: 0.02331613 - average: 0.02334542 -> worst: 0.02514797
7071
Test data two [1 times]
7172
pickle
7273
dump size: 274491 byte
73-
dump : best: 0.00102806 <- median: 0.00109109 - average: 0.00112886 -> worst: 0.00150502
74-
loads: best: 0.00137711 <- median: 0.00147515 - average: 0.00148870 -> worst: 0.00195776
74+
dump : best: 0.00107667 <- median: 0.00108967 - average: 0.00110260 -> worst: 0.00119756
75+
loads: best: 0.00145767 <- median: 0.00148819 - average: 0.00150257 -> worst: 0.00176494
7576
marshal
7677
dump size: 360242 byte
77-
dump : best: 0.00073095 <- median: 0.00080529 - average: 0.00079686 -> worst: 0.00087429
78-
loads: best: 0.00122311 <- median: 0.00139272 - average: 0.00154981 -> worst: 0.00244541
78+
dump : best: 0.00077111 <- median: 0.00081842 - average: 0.00099891 -> worst: 0.00156944
79+
loads: best: 0.00127497 <- median: 0.00128988 - average: 0.00141144 -> worst: 0.00256521
7980
FSPacker
80-
dump size: 238495 byte
81-
dump : best: 0.02812932 <- median: 0.02927591 - average: 0.03252403 -> worst: 0.06384002
82-
loads: best: 0.02134671 <- median: 0.02191628 - average: 0.02265632 -> worst: 0.03327830
81+
dump size: 238499 byte
82+
dump : best: 0.02852817 <- median: 0.02961052 - average: 0.03016038 -> worst: 0.03880055
83+
loads: best: 0.02128199 <- median: 0.02241915 - average: 0.02516957 -> worst: 0.03794705
8384
Test data three [1000 times]
8485
pickle
8586
dump size: 97 byte
86-
dump : best: 0.00065989 <- median: 0.00066162 - average: 0.00066534 -> worst: 0.00073160
87-
loads: best: 0.00080160 <- median: 0.00080605 - average: 0.00080777 -> worst: 0.00084383
87+
dump : best: 0.00066121 <- median: 0.00067347 - average: 0.00067562 -> worst: 0.00069691
88+
loads: best: 0.00081164 <- median: 0.00081801 - average: 0.00082066 -> worst: 0.00083911
8889
marshal
8990
dump size: 79 byte
90-
dump : best: 0.00061424 <- median: 0.00061704 - average: 0.00061919 -> worst: 0.00063762
91-
loads: best: 0.00080728 <- median: 0.00081835 - average: 0.00082229 -> worst: 0.00090104
91+
dump : best: 0.00061814 <- median: 0.00062130 - average: 0.00062324 -> worst: 0.00063816
92+
loads: best: 0.00083109 <- median: 0.00083498 - average: 0.00084022 -> worst: 0.00088368
9293
FSPacker
9394
dump size: 85 byte
94-
dump : best: 0.01630956 <- median: 0.01642815 - average: 0.01642205 -> worst: 0.01668519
95-
loads: best: 0.01521912 <- median: 0.01530806 - average: 0.01553064 -> worst: 0.02047744
95+
dump : best: 0.01618888 <- median: 0.01634808 - average: 0.01681845 -> worst: 0.02476933
96+
loads: best: 0.01527784 <- median: 0.01556471 - average: 0.01594638 -> worst: 0.02066095
9697
```
9798
## Contribution
9899

benchmark/__main__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@
66
import fsPacker
77
# Local modules
88
# Program
9-
def benchmark(dumpFn, loadsFn, data, name, counter=1000, repeat=4):
9+
def benchmark(dumpFn, loadsFn, data, name, counter=1000, repeat=4) -> None:
1010
print(" {}".format(name))
1111
q = dumpFn(data)
1212
print(" dump size: {:>9} byte".format(len(q)))
1313
res = timeit.repeat("fn(data)", number=counter, repeat=repeat, globals={"fn":dumpFn, "data":data})
1414
print(" dump : best: {:.8F} <- median: {:.8F} - average: {:.8F} -> worst: {:.8F}".format( min(res), median(res), sum(res)/repeat, max(res)))
1515
res = timeit.repeat("fn(data)", number=counter, repeat=repeat, globals={"fn":loadsFn, "data":q})
1616
print(" loads: best: {:.8F} <- median: {:.8F} - average: {:.8F} -> worst: {:.8F}".format( min(res), median(res), sum(res)/repeat, max(res)))
17+
return None
1718

1819
print("Test data one [1 times]")
1920
testData1 = pickle.loads(zlib.decompress(open("{}/testData1.dat".format(dirname(__file__)), 'rb').read()))

fsPacker/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "0.0.2"
1+
__version__ = "0.1.0"
22
__doc__ = """
33
FS Message Packer v{}
44
Copyright (C) 2021 Fusion Solutions KFT <[email protected]>
@@ -20,4 +20,4 @@
2020
MaxDictProtection, MaxOPProtection, OutOfData, packMessage, unpackMessage)
2121

2222
__all__ = ("dump", "dumps", "load", "loads", "FSPackerError", "UnsupportedType", "UnsupportedVersion", "InvalidOP",
23-
"MaxDictProtection", "MaxOPProtection", "OutOfData", "packMessage", "unpackMessage")
23+
"MaxDictProtection", "MaxOPProtection", "OutOfData", "packMessage", "unpackMessage")

fsPacker/fsPacker.py

Lines changed: 83 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,27 @@
66
# Third party modules
77
# Local modules
88
# Program
9-
10-
VAR_ENDIANNESS:str ="little"
11-
VAR_VERSION:bytes =b"\x01"
12-
OP_NONE:bytes =b"\xEF"
13-
OP_BOOL_FALSE:bytes =b"\xF0"
14-
OP_BOOL_TRUE:bytes =b"\xF1"
15-
OP_INTERGER:bytes =b"\xF2"
16-
OP_NEG_INTERGER:bytes =b"\xF3"
17-
OP_ZERO_INTERGER:bytes=b"\xF4"
18-
OP_FLOAT:bytes =b"\xF5"
19-
OP_NEG_FLOAT:bytes =b"\xF6"
20-
OP_ZERO_FLOAT:bytes =b"\xF7"
21-
OP_STRING:bytes =b"\xF8"
22-
OP_ZERO_STRING:bytes =b"\xF9"
23-
OP_BYTES:bytes =b"\xFA"
24-
OP_ZERO_BYTES:bytes =b"\xFB"
25-
OP_LIST:bytes =b"\xFC"
26-
OP_ZERO_LIST:bytes =b"\xFD"
27-
OP_DICT:bytes =b"\xFE"
28-
OP_ZERO_DICT:bytes =b"\xFF"
9+
VAR_ENDIANNESS = "little"
10+
VAR_VERSION = b"\x01"
11+
OP_NONE = b"\xED"
12+
OP_BOOL_FALSE = b"\xEE"
13+
OP_BOOL_TRUE = b"\xEF"
14+
OP_INTERGER = b"\xF0"
15+
OP_NEG_INTERGER = b"\xF1"
16+
OP_ZERO_INTERGER = b"\xF2"
17+
OP_FLOAT = b"\xF3"
18+
OP_NEG_FLOAT = b"\xF4"
19+
OP_ZERO_FLOAT = b"\xF5"
20+
OP_STRING = b"\xF6"
21+
OP_ZERO_STRING = b"\xF7"
22+
OP_BYTES = b"\xF8"
23+
OP_ZERO_BYTES = b"\xF9"
24+
OP_LIST = b"\xFA"
25+
OP_ZERO_LIST = b"\xFB"
26+
OP_DICT = b"\xFC"
27+
OP_ZERO_DICT = b"\xFD"
28+
OP_SET = b"\xFE"
29+
OP_ZERO_SET = b"\xFF"
2930

3031
class FSPackerError(Exception): pass
3132

@@ -42,7 +43,7 @@ class MaxOPProtection(FSPackerError): pass
4243
class OutOfData(FSPackerError): pass
4344

4445
def packMessage(message:bytes) -> bytes:
45-
d:int = len(message)
46+
d = len(message)
4647
if d < 0xFD:
4748
return d.to_bytes(1, VAR_ENDIANNESS) + message
4849
elif d <= 0xFFFF:
@@ -54,7 +55,7 @@ def packMessage(message:bytes) -> bytes:
5455
raise RuntimeError("Too big data to pack")
5556

5657
def unpackMessage(buffer:bytes) -> Tuple[int, int]:
57-
d:int = buffer[0]
58+
d = buffer[0]
5859
if d < 0xFD:
5960
return 1, d
6061
elif d == 0xFD:
@@ -66,28 +67,34 @@ def unpackMessage(buffer:bytes) -> Tuple[int, int]:
6667
return 0, 0
6768

6869
def _create_vint(d:int) -> bytes:
69-
if d < 0xEC:
70+
if d < 0xEA:
7071
return d.to_bytes(1, VAR_ENDIANNESS)
7172
elif d <= 0xFFFF:
72-
return b"\xEC" + d.to_bytes(2, VAR_ENDIANNESS)
73+
return b"\xEA" + d.to_bytes(2, VAR_ENDIANNESS)
74+
elif d <= 0xFFFFFF:
75+
return b"\xEB" + d.to_bytes(3, VAR_ENDIANNESS)
7376
elif d <= 0xFFFFFFFF:
74-
return b"\xED" + d.to_bytes(4, VAR_ENDIANNESS)
77+
return b"\xEC" + d.to_bytes(4, VAR_ENDIANNESS)
7578
else:
76-
return b"\xEE" + d.to_bytes(8, VAR_ENDIANNESS)
79+
raise FSPackerError("Too big number")
7780

7881
class FSPacker:
82+
dictCounter:int
83+
dictByKey:Dict[Any, int]
84+
dictBuffer:BytesIO
85+
opBuffer:BytesIO
7986
def __init__(self) -> None:
80-
self.dictCounter:int = 0
81-
self.dictByKey:Dict[Any, int] = {}
82-
self.dictBuffer:BytesIO = BytesIO()
83-
self.opBuffer:BytesIO = BytesIO()
87+
self.dictCounter = 0
88+
self.dictByKey = {}
89+
self.dictBuffer = BytesIO()
90+
self.opBuffer = BytesIO()
8491
@classmethod
85-
def dump(self, data:Any, fp:IO[bytes]) -> None:
86-
fp.write( self()._parse(data) )
92+
def dump(cls, data:Any, fp:IO[bytes]) -> None:
93+
fp.write( cls()._parse(data) )
8794
return
8895
@classmethod
89-
def dumps(self, data:Any) -> bytes:
90-
return self()._parse(data)
96+
def dumps(cls, data:Any) -> bytes:
97+
return cls()._parse(data)
9198
def _parse(self, data:Any) -> bytes:
9299
self._dump(data)
93100
return VAR_VERSION + _create_vint(len(self.dictByKey)) + self.dictBuffer.getbuffer() + self.opBuffer.getbuffer()
@@ -143,13 +150,21 @@ def _dump(self, d:Any) -> None:
143150
else:
144151
self.opBuffer.write(OP_ZERO_DICT)
145152
return
153+
if dt is set:
154+
if len(d):
155+
self.opBuffer.write(OP_SET + _create_vint(len(d)))
156+
for sd in d:
157+
self._dump(sd)
158+
else:
159+
self.opBuffer.write(OP_ZERO_SET)
160+
return
146161
raise UnsupportedType(dt)
147162
def _register(self, k:Any) -> int:
148163
if k not in self.dictByKey:
149-
kt:type = type(k)
164+
kt = type(k)
150165
s:bytes
151166
if kt is int:
152-
nl:int = ceil(k.bit_length() / 8)
167+
nl = ceil(k.bit_length() / 8)
153168
self.dictBuffer.write(
154169
(OP_INTERGER if k > 0 else OP_NEG_INTERGER) + \
155170
_create_vint(nl) + \
@@ -174,17 +189,22 @@ def _register(self, k:Any) -> int:
174189
return self.dictByKey[k]
175190

176191
class FSUnpacker:
192+
dict:List[Any]
193+
buffer:BytesIO
194+
maxDictSize:int
195+
maxOPSize:int
196+
OPs:Dict[bytes, Any]
177197
@classmethod
178-
def load(self, data:IO[bytes], maxDictSize:int=0, maxOPSize:int=0) -> Any:
179-
return self(BytesIO(data.read()), maxDictSize, maxOPSize)._parse()
198+
def load(cls, data:IO[bytes], maxDictSize:int=0, maxOPSize:int=0) -> Any:
199+
return cls(BytesIO(data.read()), maxDictSize, maxOPSize)._parse()
180200
@classmethod
181-
def loads(self, data:bytes, maxDictSize:int=0, maxOPSize:int=0) -> Any:
182-
return self(BytesIO(data), maxDictSize, maxOPSize)._parse()
201+
def loads(cls, data:bytes, maxDictSize:int=0, maxOPSize:int=0) -> Any:
202+
return cls(BytesIO(data), maxDictSize, maxOPSize)._parse()
183203
def __init__(self, buffer:BytesIO, maxDictSize:int=0, maxOPSize:int=0):
184-
self.dict:List[Any] = []
185-
self.buffer:BytesIO = buffer
186-
self.maxDictSize:int = maxDictSize
187-
self.maxOPSize:int = maxOPSize
204+
self.dict = []
205+
self.buffer = buffer
206+
self.maxDictSize = maxDictSize
207+
self.maxOPSize = maxOPSize
188208
self.OPs = {
189209
OP_NONE: None,
190210
OP_BOOL_FALSE: False,
@@ -195,16 +215,13 @@ def __init__(self, buffer:BytesIO, maxDictSize:int=0, maxOPSize:int=0):
195215
OP_ZERO_BYTES: b"",
196216
}
197217
def _parse(self) -> Any:
198-
bver:bytes = self.buffer.read(1)
218+
bver = self.buffer.read(1)
199219
if bver != VAR_VERSION:
200220
raise UnsupportedVersion(bver[0])
201221
self._parse_dicts()
202222
return self._parse_ops()
203223
def _parse_dicts(self) -> None:
204-
i:int
205-
t:bytes
206-
dl:int
207-
dictLen:int = self._read_vint()
224+
dictLen = self._read_vint()
208225
if self.maxDictSize > 0 and dictLen > self.maxDictSize:
209226
raise MaxDictProtection()
210227
for i in range(dictLen):
@@ -236,36 +253,40 @@ def _parse_ops(self) -> Any:
236253
raise MaxOPProtection()
237254
return self._loads()
238255
def _read_vint(self) -> int:
239-
d:bytes = self.buffer.read(1)
240-
if d[0] < 0xEC:
256+
d = self.buffer.read(1)
257+
if d[0] < 0xEA:
241258
return d[0]
242-
if d[0] == 0xEC:
259+
if d[0] == 0xEA:
243260
return int.from_bytes(self.buffer.read(2), VAR_ENDIANNESS)
244-
if d[0] == 0xED:
245-
return int.from_bytes(self.buffer.read(4), VAR_ENDIANNESS)
246-
return int.from_bytes(self.buffer.read(8), VAR_ENDIANNESS)
261+
if d[0] == 0xEB:
262+
return int.from_bytes(self.buffer.read(3), VAR_ENDIANNESS)
263+
return int.from_bytes(self.buffer.read(4), VAR_ENDIANNESS)
247264
def _loads(self) -> Any:
248-
op:bytes = self.buffer.read(1)
265+
op = self.buffer.read(1)
249266
if op == b"":
250267
raise OutOfData()
251-
if op[0] < 0xEF:
252-
if op[0] < 0xEC:
268+
if op[0] < 0xED:
269+
if op[0] < 0xEA:
253270
return self.dict[ op[0] ]
254-
if op[0] == 0xEC:
271+
if op[0] == 0xEA:
255272
return self.dict[ int.from_bytes(self.buffer.read(2), VAR_ENDIANNESS) ]
256-
if op[0] == 0xED:
257-
return self.dict[ int.from_bytes(self.buffer.read(4), VAR_ENDIANNESS) ]
258-
return self.dict[ int.from_bytes(self.buffer.read(8), VAR_ENDIANNESS) ]
273+
if op[0] == 0xEB:
274+
return self.dict[ int.from_bytes(self.buffer.read(3), VAR_ENDIANNESS) ]
275+
return self.dict[ int.from_bytes(self.buffer.read(4), VAR_ENDIANNESS) ]
259276
if op in self.OPs:
260277
return self.OPs[op]
261278
if op == OP_ZERO_LIST:
262279
return tuple()
263280
if op == OP_ZERO_DICT:
264281
return dict()
282+
if op == OP_ZERO_SET:
283+
return set()
265284
if op == OP_LIST:
266285
return tuple(( self._loads() for i in range(self._read_vint()) ))
267286
if op == OP_DICT:
268287
return dict(( (self._loads(), self._loads()) for i in range(self._read_vint()) ))
288+
if op == OP_SET:
289+
return set(( self._loads() for i in range(self._read_vint()) ))
269290
raise InvalidOP(op)
270291

271292
dump = FSPacker.dump

fsPacker/py.typed

Whitespace-only changes.

fsPacker/test/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)