Skip to content

Commit f328213

Browse files
author
FusionSolutions
committed
upload
1 parent 8fbddb1 commit f328213

File tree

14 files changed

+4724
-454
lines changed

14 files changed

+4724
-454
lines changed

.github/workflows/python-package.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
strategy:
1313
fail-fast: false
1414
matrix:
15-
python-version: [3.7, 3.8, 3.9]
15+
python-version: [3.8, 3.9]
1616

1717
steps:
1818
- uses: actions/checkout@v2

LICENSE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Fusion Solutions message packer
1+
Fusion Solutions Message Packer
22
Copyright (C) 2021 Fusion Solutions KFT <contact@fusionsolutions.io>
33

44
This program is free software: you can redistribute it and/or modify

MANIFEST.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
recursive-include fsPacker *.py
1+
recursive-include fsPacker *.py *.pyi *.so

README.md

Lines changed: 103 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,23 @@
44
## Introduction
55

66
Message packer for socket communications.
7-
Pure-Python implementation and it is [*much slower*](#benchmark) as `pickle`, `marshal` or even `json`, but much safer for production.
7+
Pure-Python implementation and it is [*slightly slower*](#benchmark) as `pickle`, `marshal` or even `json`, but much safer for production.
88
The following types are supported for packing and unpacking:
99
- `None`
10-
- `bool`
10+
- Booleans: `True` and `False`
1111
- `int`
12-
- `float`
13-
- `string`
12+
- Floats: `float`, `inf` and `-inf`
13+
- `str`
1414
- `bytearray` (during unpacking it will be converted to `bytes`)
1515
- `bytes`
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)
19+
- `set`
2020

2121
## Installation
2222

23-
Requires python version 3.7 or later.
23+
Requires python version 3.8 or later.
2424

2525
To install the latest release on [PyPI](https://pypi.org/project/python-fspacker/),
2626
simply run:
@@ -55,45 +55,109 @@ print( fsPacker.loads(data) )
5555
Environment: Intel(R) Xeon(R) CPU E5-1650 v4 @ 3.60GHz, DIMM DDR4 Synchronous Registered (Buffered) 2133 MHz
5656
```shell
5757
$/python-fspacker: python3 -m benchmark
58-
Test data one [1 times]
58+
Batch 1# started
5959
pickle
60-
dump size: 369436 byte
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
60+
packed data size: 369436 byte
61+
dump : best: 0.00097681 <- median avg: 0.00097981 - average: 0.00098675 -> worst: 0.00115466
62+
loads: best: 0.00116250 <- median avg: 0.00116567 - average: 0.00116768 -> worst: 0.00123618
6363
marshal
64-
dump size: 474624 byte
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
67-
FSPacker
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
71-
Test data two [1 times]
64+
packed data size: 474624 byte
65+
dump : best: 0.00060849 <- median avg: 0.00060978 - average: 0.00061214 -> worst: 0.00066010
66+
loads: best: 0.00093875 <- median avg: 0.00094239 - average: 0.00094469 -> worst: 0.00099110
67+
FSPacker version 1
68+
packed data size: 348332 byte
69+
dump : best: 0.00140599 <- median avg: 0.00142470 - average: 0.00143550 -> worst: 0.00179069
70+
loads: best: 0.00092720 <- median avg: 0.00108024 - average: 0.00107529 -> worst: 0.00113729
71+
FSPacker PURE PYTHON version 1
72+
packed data size: 329293 byte
73+
dump : best: 0.02668814 <- median avg: 0.02687568 - average: 0.02691436 -> worst: 0.02727839
74+
loads: best: 0.02563514 <- median avg: 0.02585654 - average: 0.02623195 -> worst: 0.02991657
75+
FSPacker version 2
76+
packed data size: 324346 byte
77+
dump : best: 0.00133745 <- median avg: 0.00135156 - average: 0.00136461 -> worst: 0.00155367
78+
loads: best: 0.00094138 <- median avg: 0.00107694 - average: 0.00107477 -> worst: 0.00114164
79+
FSPacker PURE PYTHON version 2
80+
packed data size: 314318 byte
81+
dump : best: 0.02291694 <- median avg: 0.02304409 - average: 0.02304181 -> worst: 0.02318106
82+
loads: best: 0.02749131 <- median avg: 0.02768044 - average: 0.02768100 -> worst: 0.02799873
83+
84+
Batch 2# started
7285
pickle
73-
dump size: 274491 byte
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
86+
packed data size: 274491 byte
87+
dump : best: 0.00084737 <- median avg: 0.00085283 - average: 0.00085566 -> worst: 0.00091460
88+
loads: best: 0.00100438 <- median avg: 0.00101581 - average: 0.00101901 -> worst: 0.00110241
7689
marshal
77-
dump size: 360242 byte
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
80-
FSPacker
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
84-
Test data three [1000 times]
90+
packed data size: 360242 byte
91+
dump : best: 0.00051488 <- median avg: 0.00051733 - average: 0.00051980 -> worst: 0.00054507
92+
loads: best: 0.00083179 <- median avg: 0.00083660 - average: 0.00084212 -> worst: 0.00090665
93+
FSPacker version 1
94+
packed data size: 271694 byte
95+
dump : best: 0.00146050 <- median avg: 0.00147431 - average: 0.00147690 -> worst: 0.00153197
96+
loads: best: 0.00092286 <- median avg: 0.00100156 - average: 0.00099671 -> worst: 0.00102327
97+
FSPacker PURE PYTHON version 1
98+
packed data size: 238499 byte
99+
dump : best: 0.02538159 <- median avg: 0.02551851 - average: 0.02557210 -> worst: 0.02591332
100+
loads: best: 0.02445310 <- median avg: 0.02459201 - average: 0.02464271 -> worst: 0.02524533
101+
FSPacker version 2
102+
packed data size: 238735 byte
103+
dump : best: 0.00135346 <- median avg: 0.00136909 - average: 0.00137037 -> worst: 0.00141406
104+
loads: best: 0.00090187 <- median avg: 0.00100334 - average: 0.00101569 -> worst: 0.00130636
105+
FSPacker PURE PYTHON version 2
106+
packed data size: 221546 byte
107+
dump : best: 0.02124800 <- median avg: 0.02136108 - average: 0.02141090 -> worst: 0.02174148
108+
loads: best: 0.02539373 <- median avg: 0.02556542 - average: 0.02576320 -> worst: 0.02921140
109+
110+
Batch 3# started
85111
pickle
86-
dump size: 97 byte
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
112+
packed data size: 274511 byte
113+
dump : best: 0.00087786 <- median avg: 0.00088081 - average: 0.00088287 -> worst: 0.00093917
114+
loads: best: 0.00098829 <- median avg: 0.00099558 - average: 0.00099822 -> worst: 0.00105612
89115
marshal
90-
dump size: 79 byte
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
93-
FSPacker
94-
dump size: 85 byte
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
116+
packed data size: 360267 byte
117+
dump : best: 0.00051608 <- median avg: 0.00051854 - average: 0.00051974 -> worst: 0.00054176
118+
loads: best: 0.00082532 <- median avg: 0.00082907 - average: 0.00083079 -> worst: 0.00085855
119+
FSPacker version 1
120+
packed data size: 414729 byte
121+
dump : best: 0.00300953 <- median avg: 0.00304670 - average: 0.00307232 -> worst: 0.00353145
122+
loads: best: 0.00209713 <- median avg: 0.00237827 - average: 0.00240026 -> worst: 0.00317319
123+
FSPacker PURE PYTHON version 1
124+
packed data size: 365886 byte
125+
dump : best: 0.06813255 <- median avg: 0.06852698 - average: 0.06859602 -> worst: 0.06918136
126+
loads: best: 0.06472549 <- median avg: 0.06544865 - average: 0.06549210 -> worst: 0.06638827
127+
FSPacker version 2
128+
packed data size: 381787 byte
129+
dump : best: 0.00308844 <- median avg: 0.00310415 - average: 0.00311051 -> worst: 0.00318295
130+
loads: best: 0.00206454 <- median avg: 0.00235557 - average: 0.00234600 -> worst: 0.00239830
131+
FSPacker PURE PYTHON version 2
132+
packed data size: 348954 byte
133+
dump : best: 0.06123471 <- median avg: 0.06176491 - average: 0.06173875 -> worst: 0.06195223
134+
loads: best: 0.07077837 <- median avg: 0.07135906 - average: 0.07146089 -> worst: 0.07298397
135+
136+
Batch 4# started
137+
pickle
138+
packed data size: 97 byte
139+
dump : best: 0.00055353 <- median avg: 0.00056210 - average: 0.00056276 -> worst: 0.00057487
140+
loads: best: 0.00064643 <- median avg: 0.00065715 - average: 0.00065725 -> worst: 0.00066493
141+
marshal
142+
packed data size: 79 byte
143+
dump : best: 0.00047902 <- median avg: 0.00048745 - average: 0.00048688 -> worst: 0.00049302
144+
loads: best: 0.00058795 <- median avg: 0.00059175 - average: 0.00059234 -> worst: 0.00060103
145+
FSPacker version 1
146+
packed data size: 85 byte
147+
dump : best: 0.00077319 <- median avg: 0.00078550 - average: 0.00078805 -> worst: 0.00080470
148+
loads: best: 0.00068854 <- median avg: 0.00076246 - average: 0.00075894 -> worst: 0.00078787
149+
FSPacker PURE PYTHON version 1
150+
packed data size: 85 byte
151+
dump : best: 0.01360374 <- median avg: 0.01369004 - average: 0.01373398 -> worst: 0.01407292
152+
loads: best: 0.01672420 <- median avg: 0.01689699 - average: 0.01688315 -> worst: 0.01702459
153+
FSPacker version 2
154+
packed data size: 74 byte
155+
dump : best: 0.00075289 <- median avg: 0.00076416 - average: 0.00076328 -> worst: 0.00077514
156+
loads: best: 0.00053710 <- median avg: 0.00055360 - average: 0.00055652 -> worst: 0.00058802
157+
FSPacker PURE PYTHON version 2
158+
packed data size: 74 byte
159+
dump : best: 0.00831298 <- median avg: 0.00834915 - average: 0.00835809 -> worst: 0.00851608
160+
loads: best: 0.01466914 <- median avg: 0.01474124 - average: 0.01477603 -> worst: 0.01507150
97161
```
98162
## Contribution
99163

benchmark/__main__.py

Lines changed: 52 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,63 @@
11
# Builtin modules
22
import timeit, zlib, pickle, marshal
33
from os.path import dirname
4+
from copy import deepcopy
45
from statistics import median
6+
from functools import partial
7+
from typing import Any
58
# Third party modules
69
import fsPacker
10+
from fsPacker.fallback import dumps as pyDumps, loads as pyLoads
711
# Local modules
812
# Program
9-
def benchmark(dumpFn, loadsFn, data, name, counter=1000, repeat=4) -> None:
13+
def benchmark(dumpFn:Any, loadsFn:Any, data:Any, name:str, counter:int=1000, repeat:int=4) -> None:
1014
print(" {}".format(name))
11-
q = dumpFn(data)
12-
print(" dump size: {:>9} byte".format(len(q)))
15+
packedData = dumpFn(data)
16+
print(" packed data size: {:>9} byte".format(len(packedData)))
1317
res = timeit.repeat("fn(data)", number=counter, repeat=repeat, globals={"fn":dumpFn, "data":data})
14-
print(" dump : best: {:.8F} <- median: {:.8F} - average: {:.8F} -> worst: {:.8F}".format( min(res), median(res), sum(res)/repeat, max(res)))
15-
res = timeit.repeat("fn(data)", number=counter, repeat=repeat, globals={"fn":loadsFn, "data":q})
16-
print(" loads: best: {:.8F} <- median: {:.8F} - average: {:.8F} -> worst: {:.8F}".format( min(res), median(res), sum(res)/repeat, max(res)))
17-
return None
18+
print(" dump : best: {:.8F} <- median avg: {:.8F} - average: {:.8F} -> worst: {:.8F}".format(
19+
min(res),
20+
median(res),
21+
sum(res)/repeat,
22+
max(res),
23+
))
24+
res = timeit.repeat("fn(data)", number=counter, repeat=repeat, globals={"fn":loadsFn, "data":packedData})
25+
print(" loads: best: {:.8F} <- median avg: {:.8F} - average: {:.8F} -> worst: {:.8F}".format(
26+
min(res),
27+
median(res),
28+
sum(res)/repeat,
29+
max(res),
30+
))
1831

19-
print("Test data one [1 times]")
20-
testData1 = pickle.loads(zlib.decompress(open("{}/testData1.dat".format(dirname(__file__)), 'rb').read()))
21-
benchmark(pickle.dumps, pickle.loads, testData1, "pickle", 1, 32)
22-
benchmark(marshal.dumps, marshal.loads, testData1, "marshal", 1, 32)
23-
benchmark(fsPacker.dumps, fsPacker.loads, testData1, "FSPacker", 1, 32)
24-
25-
print("Test data two [1 times]")
26-
testData2 = pickle.loads(zlib.decompress(open("{}/testData2.dat".format(dirname(__file__)), 'rb').read()))
27-
benchmark(pickle.dumps, pickle.loads, testData2, "pickle", 1, 32)
28-
benchmark(marshal.dumps, marshal.loads, testData2, "marshal", 1, 32)
29-
benchmark(fsPacker.dumps, fsPacker.loads, testData2, "FSPacker", 1, 32)
30-
31-
print("Test data three [1000 times]")
32-
testData2 = { "jsonrpc":"2.0", "id":"00000000000", "method":"sampleMethod", "params":[True, "Joe", 88] }
33-
benchmark(pickle.dumps, pickle.loads, testData2, "pickle", 1000, 32)
34-
benchmark(marshal.dumps, marshal.loads, testData2, "marshal", 1000, 32)
35-
benchmark(fsPacker.dumps, fsPacker.loads, testData2, "FSPacker", 1000, 32)
32+
def startBatch(i:int, data:Any, counter:int) -> None:
33+
print("Batch {}# started".format(i))
34+
benchmark(pickle.dumps, pickle.loads, data, "pickle", counter, 32)
35+
benchmark(marshal.dumps, marshal.loads, data, "marshal", counter, 32)
36+
for version in range(1, fsPacker.HIGHEST_VERSION + 1):
37+
benchmark(
38+
partial(fsPacker.dumps, version=version),
39+
fsPacker.loads,
40+
data,
41+
"FSPacker version {}".format(version),
42+
counter,
43+
32,
44+
)
45+
if fsPacker.ACCELERATION_IS_AVAILABLE:
46+
benchmark(
47+
partial(pyDumps, version=version),
48+
pyLoads,
49+
data,
50+
"FSPacker PURE PYTHON version {}".format(version),
51+
counter,
52+
32,
53+
)
54+
print("")
55+
56+
data = deepcopy(pickle.loads(zlib.decompress(open("{}/testData1.dat".format(dirname(__file__)), 'rb').read())))
57+
startBatch(1, data, 1)
58+
data = deepcopy(pickle.loads(zlib.decompress(open("{}/testData2.dat".format(dirname(__file__)), 'rb').read())))
59+
startBatch(2, data, 1)
60+
data = deepcopy([data, {"data":(data, data)}])
61+
startBatch(3, data, 1)
62+
data = { "jsonrpc":"2.0", "id":"00000000000", "method":"sampleMethod", "params":[True, "Joe", 88] }
63+
startBatch(4, data, 1000)

fsPacker/__init__.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "0.1.1"
1+
__version__ = "0.2.0"
22
__doc__ = """
33
FS Message Packer v{}
44
Copyright (C) 2021 Fusion Solutions KFT <contact@fusionsolutions.io>
@@ -16,8 +16,14 @@
1616
You should have received a copy of the GNU Lesser General Public License
1717
along with this program. If not, see <http://www.gnu.org/licenses/lgpl.txt>.
1818
""".format(__version__)
19-
from .fsPacker import (dump, dumps, load, loads, FSPackerError, UnsupportedType, UnsupportedVersion, InvalidOP,
20-
MaxDictProtection, MaxOPProtection, OutOfData, packMessage, unpackMessage)
19+
try:
20+
ACCELERATION_IS_AVAILABLE = True
21+
from ._fspacker import load, loads, dump, dumps, PackerError, PackingError, UnpackingError
22+
except ImportError:
23+
ACCELERATION_IS_AVAILABLE = False
24+
from .fallback import load, loads, dump, dumps, PackerError, PackingError, UnpackingError # type: ignore
2125

22-
__all__ = ("dump", "dumps", "load", "loads", "FSPackerError", "UnsupportedType", "UnsupportedVersion", "InvalidOP",
23-
"MaxDictProtection", "MaxOPProtection", "OutOfData", "packMessage", "unpackMessage")
26+
from .fallback import HIGHEST_VERSION
27+
28+
__all__ = ("dump", "dumps", "load", "loads", "PackerError", "PackingError", "UnpackingError", "HIGHEST_VERSION",
29+
"ACCELERATION_IS_AVAILABLE")

0 commit comments

Comments
 (0)