├── .gitignore ├── flynn ├── data.py ├── __init__.py ├── utils.py ├── tool.py ├── decoder.py └── encoder.py ├── setup.py ├── COPYING ├── README.rst └── test.py /.gitignore: -------------------------------------------------------------------------------- 1 | .*.sw? 2 | .sw? 3 | __pycache__/ 4 | *.pyc 5 | -------------------------------------------------------------------------------- /flynn/data.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import collections 4 | 5 | Tagging = collections.namedtuple("Tagging", ["tag", "object"]) 6 | 7 | class _Undefined(object): 8 | _instance = None 9 | 10 | def __new__(cls, *args, **kwargs): 11 | if not isinstance(cls._instance, cls): 12 | cls._instance = object.__new__(cls, *args, **kwargs) 13 | return cls._instance 14 | 15 | def __str__(self): 16 | return "Undefined" 17 | 18 | def __repr__(self): 19 | return "Undefined" 20 | 21 | Undefined = _Undefined() 22 | -------------------------------------------------------------------------------- /flynn/__init__.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import base64 4 | 5 | import flynn.decoder 6 | import flynn.encoder 7 | import flynn.data 8 | 9 | __all__ = [ 10 | "decoder", 11 | "encoder", 12 | "dump", 13 | "dumps", 14 | "dumph", 15 | "load", 16 | "loads", 17 | "loadh", 18 | "Tagging", 19 | "Undefined" 20 | ] 21 | 22 | dump = flynn.encoder.dump 23 | dumps = flynn.encoder.dumps 24 | 25 | load = flynn.decoder.load 26 | loads = flynn.decoder.loads 27 | 28 | Tagging = flynn.data.Tagging 29 | Undefined = flynn.data.Undefined 30 | 31 | def dumph(*args, **kwargs): 32 | return base64.b16encode(dumps(*args, **kwargs)).decode("utf-8") 33 | 34 | def loadh(data, *args, **kwargs): 35 | return loads(base64.b16decode(data), *args, **kwargs) 36 | 37 | -------------------------------------------------------------------------------- /flynn/utils.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import sys 4 | 5 | if sys.version_info[0] == 2: 6 | import struct 7 | 8 | def _build_fmt(length, endianess): 9 | format_ = "" 10 | if endianess == "big": 11 | format_ += ">" 12 | elif endianess == "little": 13 | format_ += "<" 14 | if length == 1: 15 | format_ += "B" 16 | elif length == 2: 17 | format_ += "H" 18 | elif length == 4: 19 | format_ += "I" 20 | elif length == 8: 21 | format_ += "Q" 22 | return format_ 23 | 24 | def to_bytes(val, length, endianess): 25 | return struct.pack(_build_fmt(length, endianess), val) 26 | 27 | def from_bytes(val, length, endianess): 28 | return struct.unpack(_build_fmt(length, endianess), val)[0] 29 | else: 30 | def from_bytes(val, length, endianess): 31 | return int.from_bytes(val, length, endianess) 32 | 33 | def to_bytes(val, length, endianess): 34 | return val.to_bytes(length, endianess) 35 | 36 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import setuptools 4 | 5 | long_description = """Flynn is a simple decoder and encoder for the CBOR binary 6 | object format described in RFC7049. It features a full CBOR base support and 7 | also a simple streaming interface for networking applications.""" 8 | 9 | setuptools.setup( 10 | name="flynn", 11 | version="1.0.0b2", 12 | packages=[ 13 | "flynn", 14 | ], 15 | author="Fritz Grimpen", 16 | author_email="fritz@grimpen.net", 17 | url="https://github.com/fritz0705/flynn.git", 18 | license="http://opensource.org/licenses/MIT", 19 | description="Simple decoder and encoder for CBOR", 20 | classifiers=[ 21 | "Development Status :: 4 - Beta", 22 | "Environment :: Web Environment", 23 | "License :: OSI Approved :: MIT License", 24 | "Programming Language :: Python :: 2.7", 25 | "Programming Language :: Python :: 3", 26 | "Topic :: System :: Networking", 27 | ], 28 | long_description=__doc__ 29 | ) 30 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Fritz Grimpen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | 9 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Flynn - Simple CBOR En/Decoder 2 | ############################## 3 | 4 | Flynn is also a Python library providing CBOR [RFC7049] encoding and decoding with a 5 | traditional buffered and a streaming interface. 6 | 7 | Usage 8 | ===== 9 | 10 | The Flynn API is really simple and inspired by existing Python serialisation 11 | modules like json and pickle. The flynn module has four methods called dumps, 12 | dump, loads and load, where dumps will return the serialised input as bytes 13 | string, while dump will write the serialised input to a file descriptor. The 14 | same applies to loads and load. 15 | 16 | >>> flynn.dumps([1, [2, 3]]) 17 | b'\x82\x01\x82\x02\x03' 18 | >>> flynn.loads(b'\x82\x01\x82\x02\x03') 19 | [1, [2, 3]] 20 | 21 | Furthermore, Flynn supports generators and other iterables as input for 22 | streaming support: 23 | 24 | >>> flynn.dumps(range(5)) 25 | b'\x9f\x00\x01\x02\x03\x04\xff' 26 | >>> flynn.loads(b'\x9f\x00\x01\x02\x03\x04\xff') 27 | [0, 1, 2, 3, 4] 28 | 29 | Or to generate a map using an iterable: 30 | 31 | >>> flynn.dumps((((a, a) for a in range(5)), "map")) 32 | b'\xbf\x00\x00\x01\x01\x02\x02\x03\x03\x04\x04\xff' 33 | >>> flynn.loads(b'\xbf\x00\x00\x01\x01\x02\x02\x03\x03\x04\x04\xff') 34 | {0: 0, 1: 1, 2: 2, 3: 3, 4: 4} 35 | 36 | Copyright / License 37 | =================== 38 | 39 | © 2013 Fritz Conrad Grimpen 40 | 41 | The code is licensed under the MIT license, provided in the COPYING file of the 42 | Flynn distribution. 43 | 44 | -------------------------------------------------------------------------------- /flynn/tool.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import sys 4 | import argparse 5 | 6 | import flynn 7 | import json 8 | 9 | def main(args=sys.argv[1:]): 10 | formats = {"json", "cbor", "cbori", "cborh", "cborhi"} 11 | argparser = argparse.ArgumentParser() 12 | argparser.add_argument("-i", "--input-format", choices=formats, default="cbor") 13 | argparser.add_argument("-o", "--output-format", choices=formats, default="cbor") 14 | args = argparser.parse_args(args) 15 | if args.input_format in {"cbor", "cbori"}: 16 | input_format = "cbor" 17 | else: 18 | input_format = args.input_format 19 | output_format = args.output_format 20 | 21 | intermediate = None 22 | if input_format in {"cbor", "cbori"}: 23 | intermediate = flynn.load(sys.stdin.buffer.raw) 24 | elif input_format in {"cborh", "cborhi"}: 25 | intermediate = flynn.loadh(sys.stdin.read()) 26 | elif input_format == "json": 27 | intermediate = json.load(sys.stdin) 28 | 29 | if output_format == "cbor": 30 | flynn.dump(intermediate, sys.stdout.buffer.raw) 31 | elif output_format == "cbori": 32 | flynn.dump(intermediate, sys.stdout.buffer.raw, cls=flynn.encoder.InfiniteEncoder) 33 | elif output_format == "cborh": 34 | sys.stdout.write(flynn.dumph(intermediate)) 35 | sys.stdout.write("\n") 36 | elif output_format == "cborhi": 37 | sys.stdout.write(flynn.dumph(intermediate, cls=flynn.encoder.InfiniteEncoder)) 38 | sys.stdout.write("\n") 39 | elif output_format == "json": 40 | json.dump(intermediate, sys.stdout) 41 | sys.stdout.write("\n") 42 | 43 | if __name__ == "__main__": 44 | main() 45 | 46 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import flynn 4 | import collections 5 | 6 | # Abbrevations 7 | T = flynn.data.Tagging 8 | Undef = flynn.data.Undefined 9 | # OrderedDict is required due to non-deterministic hashing salt 10 | O = collections.OrderedDict 11 | 12 | expectations = [ 13 | (0, "00", None), 14 | (1, "01", None), 15 | (10, "0A", None), 16 | (23, "17", None), 17 | (24, "1818", None), 18 | (25, "1819", None), 19 | (100, "1864", None), 20 | (1000, "1903E8", None), 21 | (1000000, "1A000F4240", None), 22 | (1000000000000, "1B000000E8D4A51000", None), 23 | (18446744073709551615, "1BFFFFFFFFFFFFFFFF", None), 24 | # FIXME Will fail due to missing Bignum support 25 | #(18446744073709551616, "C249010000000000000000"), 26 | (-18446744073709551616, "3BFFFFFFFFFFFFFFFF", None), 27 | # FIXME Will fail due to missing Bignum support 28 | #(-18446744073709551617, "C349010000000000000000"), 29 | (-1, "20", None), 30 | (-10, "29", None), 31 | (-100, "3863", None), 32 | (-1000, "3903E7", None), 33 | (False, "F4", None), 34 | (True, "F5", None), 35 | (None, "F6", None), 36 | (flynn.data.Undefined, "F7", None), 37 | (T(0, "2013-03-21T20:04:00Z"), "C074323031332D30332D32315432303A30343A30305A", 38 | None), 39 | (T(1, 1363896240), "C11A514B67B0", None), 40 | (b"", "40", None), 41 | (b"\x01\x02\x03\x04", "4401020304", None), 42 | ("", "60", None), 43 | ("a", "6161", None), 44 | ("IETF", "6449455446", None), 45 | ("\"\\", "62225C", None), 46 | ("\u00fc", "62C3BC", None), 47 | ("\u6c34", "63E6B0B4", None), 48 | # FIXME This test will fail due to invalid unicode codepoints 49 | #("\ud800\udd51", "64F0908591"), 50 | ([], "80", None), 51 | ([1, 2, 3], "83010203", None), 52 | ([1, [2, 3], [4, 5]], "8301820203820405", None), 53 | ({}, "A0", None), 54 | (O([(1, 2), (3, 4)]), "A201020304", None), 55 | (O([("a", 1), ("b", [2, 3])]), "A26161016162820203", None), 56 | (["a", {"b": "c"}], "826161A161626163", None), 57 | (range(6), "9F000102030405FF", [0, 1, 2, 3, 4, 5]), 58 | (("A", "B", "C"), "9F614161426143FF", ["A", "B", "C"]), 59 | ] 60 | 61 | def test_encode(): 62 | for raw, encoded, decoded in expectations: 63 | print((raw, encoded)) 64 | assert flynn.dumph(raw) == encoded 65 | 66 | def test_decode(): 67 | for raw, encoded, decoded in expectations: 68 | if decoded is None: 69 | decoded = raw 70 | elif decoded is False: 71 | continue 72 | print((raw, encoded)) 73 | assert flynn.loadh(encoded) == decoded 74 | 75 | -------------------------------------------------------------------------------- /flynn/decoder.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import io 4 | import struct 5 | import math 6 | 7 | import flynn.data 8 | 9 | class InvalidCborError(Exception): 10 | pass 11 | 12 | class _Break(InvalidCborError): 13 | def __init__(self): 14 | InvalidCborError.__init__(self, "Invalid BREAK code occurred") 15 | 16 | class Decoder(object): 17 | def __init__(self, input): 18 | self._jump_table = [ 19 | lambda *args: self.decode_integer(*args, sign=False), 20 | lambda *args: self.decode_integer(*args, sign=True), 21 | self.decode_bytestring, 22 | self.decode_textstring, 23 | self.decode_list, 24 | self.decode_dict, 25 | self.decode_tagging, 26 | self.decode_other 27 | ] 28 | self.input = input 29 | 30 | def decode(self): 31 | mtype, ainfo = self._decode_ibyte() 32 | try: 33 | decoder = self._jump_table[mtype] 34 | except KeyError: 35 | raise InvalidCborError("Invalid major type {}".format(mtype)) 36 | return decoder(mtype, ainfo) 37 | 38 | def decode_integer(self, mtype, ainfo, sign=False): 39 | res = self._decode_length(mtype, ainfo) 40 | if sign is True: 41 | return -1 - res 42 | else: 43 | return res 44 | 45 | def decode_bytestring(self, mtype, ainfo): 46 | length = self._decode_length(mtype, ainfo) 47 | if length is None: 48 | res = bytearray() 49 | while True: 50 | mtype_, ainfo_ = self._decode_ibyte() 51 | if (mtype_, ainfo_) == (7, 31): 52 | break 53 | if mtype_ != 2: 54 | pass 55 | res.extend(self.decode_bytestring(mtype_, ainfo_)) 56 | return bytes(res) 57 | else: 58 | return self._read(length) 59 | 60 | def decode_textstring(self, mtype, ainfo): 61 | length = self._decode_length(mtype, ainfo) 62 | if length is None: 63 | res = bytearray() 64 | while True: 65 | mtype_, ainfo_ = self._decode_ibyte() 66 | if (mtype_, ainfo_) == (7, 31): 67 | break 68 | if mtype_ != 3: 69 | pass 70 | res.extend(self.decode_bytestring(mtype_, ainfo_)) 71 | return res.decode("utf-8") 72 | else: 73 | return self._read(length).decode("utf-8") 74 | 75 | def decode_list(self, mtype, ainfo): 76 | length = self._decode_length(mtype, ainfo) 77 | if length is None: 78 | res = [] 79 | while True: 80 | try: 81 | res.append(self.decode()) 82 | except _Break: 83 | break 84 | return res 85 | else: 86 | res = [None for _ in range(length)] 87 | for n in range(length): 88 | res[n] = self.decode() 89 | return res 90 | 91 | def decode_dict(self, mtype, ainfo): 92 | length = self._decode_length(mtype, ainfo) 93 | if length is None: 94 | res = {} 95 | try: 96 | while True: 97 | key = self.decode() 98 | value = self.decode() 99 | res[key] = value 100 | except _Break: 101 | pass 102 | return res 103 | else: 104 | res = {} 105 | for n in range(length): 106 | key, value = self.decode(), self.decode() 107 | res[key] = value 108 | return res 109 | 110 | def decode_tagging(self, mtype, ainfo): 111 | length = self._decode_length(mtype, ainfo) 112 | return flynn.data.Tagging(length, self.decode()) 113 | 114 | def decode_half_float(self, mtype, ainfo): 115 | half = struct.unpack(">H", self._read(2))[0] 116 | valu = (half & 0x7fff) << 13 | (half & 0x8000) << 16 117 | if ((half & 0x7c00) != 0x7c00): 118 | return math.ldexp(struct.unpack("!f", struct.pack("!I", valu))[0], 112) 119 | return struct.unpack("!f", struct.pack("!I", valu | 0x7f800000))[0] 120 | 121 | def decode_single_float(self, mtype, ainfo): 122 | return struct.unpack(">f", self._read(4))[0] 123 | 124 | def decode_double_float(self, mtype, ainfo): 125 | return struct.unpack(">d", self._read(8))[0] 126 | 127 | def decode_other(self, mtype, ainfo): 128 | if ainfo == 20: 129 | return False 130 | elif ainfo == 21: 131 | return True 132 | elif ainfo == 22: 133 | return None 134 | elif ainfo == 23: 135 | return flynn.data.Undefined 136 | elif ainfo == 25: 137 | return self.decode_half_float(mtype, ainfo) 138 | elif ainfo == 26: 139 | return self.decode_single_float(mtype, ainfo) 140 | elif ainfo == 27: 141 | return self.decode_double_float(mtype, ainfo) 142 | elif ainfo == 31: 143 | raise _Break() 144 | 145 | def _decode_ibyte(self): 146 | byte = self._read(1)[0] 147 | if isinstance(byte, str): 148 | byte = ord(byte) 149 | return (byte & 0b11100000) >> 5, byte & 0b00011111 150 | 151 | def _decode_length(self, mtype, ainfo): 152 | if ainfo < 24: 153 | return ainfo 154 | elif ainfo == 24: 155 | return int.from_bytes(self._read(1), "big") 156 | elif ainfo == 25: 157 | return int.from_bytes(self._read(2), "big") 158 | elif ainfo == 26: 159 | return int.from_bytes(self._read(4), "big") 160 | elif ainfo == 27: 161 | return int.from_bytes(self._read(8), "big") 162 | elif ainfo == 31: 163 | return None 164 | raise InvalidCborError("Invalid additional information {}".format(ainfo)) 165 | 166 | def _read(self, n): 167 | m = self.input.read(n) 168 | if len(m) != n: 169 | raise InvalidCborError("Expected {} bytes, got {} bytes instead".format(n, len(m))) 170 | return m 171 | 172 | class StandardDecoder(Decoder): 173 | def __init__(self, input, tagging_hooks=None): 174 | Decoder.__init__(self, input) 175 | if tagging_hooks is None: 176 | tagging_hooks = {} 177 | self.tagging_hooks = tagging_hooks 178 | 179 | def decode_tagging(self, mtype, ainfo): 180 | tagged = Decoder.decode_tagging(self, mtype, ainfo) 181 | if tagged.tag in self.tagging_hooks: 182 | return self.tagging_hooks[tagged.tag](tagged.tag, tagged.object) 183 | else: 184 | return tagged 185 | 186 | def register_tagging(self, tag_id, hook): 187 | self.tagging_hooks[tag_id] = hook 188 | 189 | def unregister_tagging(self, tag_id): 190 | del self.tagging_hooks[tag_id] 191 | 192 | def load(fp, cls=Decoder, *args, **kwargs): 193 | return cls(fp, *args, **kwargs).decode() 194 | 195 | def loads(data, cls=Decoder, *args, **kwargs): 196 | return cls(io.BytesIO(data), *args, **kwargs).decode() 197 | 198 | __all__ = ["InvalidCborError", "Decoder", "load", "loads"] 199 | 200 | -------------------------------------------------------------------------------- /flynn/encoder.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import io 4 | import sys 5 | import struct 6 | 7 | if sys.version_info[0] == 2: 8 | import collections as abc 9 | _integer_types = (int, long) 10 | else: 11 | import collections.abc as abc 12 | _integer_types = (int, ) 13 | 14 | _str_type = type(u"") 15 | _bytes_type = type(b"") 16 | 17 | import flynn.data 18 | from flynn.utils import to_bytes 19 | 20 | class EncoderError(Exception): 21 | pass 22 | 23 | class Encoder(object): 24 | def __init__(self, output): 25 | self.output = output 26 | 27 | def encode(self, object): 28 | if isinstance(object, list): 29 | self.encode_list(object) 30 | elif isinstance(object, dict): 31 | self.encode_dict(object) 32 | elif isinstance(object, _bytes_type): 33 | self.encode_bytestring(object) 34 | elif isinstance(object, _str_type): 35 | self.encode_textstring(object) 36 | elif isinstance(object, float): 37 | self.encode_float(object) 38 | elif isinstance(object, bool): 39 | self.encode_boolean(object) 40 | elif isinstance(object, _integer_types): 41 | self.encode_integer(object) 42 | elif isinstance(object, flynn.data.Tagging): 43 | self.encode_tagging(object) 44 | elif object is flynn.data.Undefined: 45 | self.encode_undefined() 46 | elif object is None: 47 | self.encode_null() 48 | elif isinstance(object, abc.Iterable): 49 | self.encode_infinite_list(object) 50 | else: 51 | raise EncoderError("Object of type {} is not serializable".format(type(object))) 52 | 53 | def encode_list(self, list): 54 | self._write(_encode_ibyte(4, len(list))) 55 | for elem in list: 56 | self.encode(elem) 57 | 58 | def encode_dict(self, dict): 59 | self._write(_encode_ibyte(5, len(dict))) 60 | for key, value in dict.items(): 61 | self.encode(key) 62 | self.encode(value) 63 | 64 | def encode_bytestring(self, bytestring): 65 | self._write(_encode_ibyte(2, len(bytestring))) 66 | self._write(bytestring) 67 | 68 | def encode_textstring(self, textstring): 69 | string_ = textstring.encode("utf-8") 70 | self._write(_encode_ibyte(3, len(string_))) 71 | self._write(string_) 72 | 73 | def encode_float(self, float): 74 | self._write(b"\xfb") 75 | self._write(struct.pack(">d", float)) 76 | 77 | def encode_integer(self, integer): 78 | if integer < 0: 79 | integer = -integer - 1 80 | try: 81 | self._write(_encode_ibyte(1, integer)) 82 | except TypeError: 83 | raise EncoderError("Encoding integers lower than -18446744073709551616 is not supported") 84 | else: 85 | try: 86 | self._write(_encode_ibyte(0, integer)) 87 | except TypeError: 88 | raise EncoderError("Encoding integers larger than 18446744073709551615 is not supported") 89 | 90 | def encode_tagging(self, tagging): 91 | try: 92 | self._write(_encode_ibyte(6, tagging[0])) 93 | except TypeError: 94 | raise EncoderError("Encoding tag larger than 18446744073709551615 is not supported") 95 | self.encode(tagging[1]) 96 | 97 | def encode_boolean(self, boolean): 98 | if boolean is True: 99 | self._write(_encode_ibyte(7, 21)) 100 | elif boolean is False: 101 | self._write(_encode_ibyte(7, 20)) 102 | 103 | def encode_null(self): 104 | self._write(_encode_ibyte(7, 22)) 105 | 106 | def encode_undefined(self): 107 | self._write(b"\xf7") 108 | 109 | def encode_infinite_textstring(self, iterable): 110 | self._write(b"\x7f") 111 | for elem in iterable: 112 | if not isinstance(elem, _str_type): 113 | raise EncoderError("Object of type {} is not valid in infinite textstring".format(type(elem))) 114 | self.encode(elem) 115 | self._write(b"\xff") 116 | 117 | def encode_infinite_bytestring(self, iterable): 118 | self._write(b"\x5f") 119 | for elem in iterable: 120 | if not isinstance(elem, _bytes_type): 121 | raise EncoderError("Object of type {} is not valid in infinite bytestring".format(type(elem))) 122 | self.encode(elem) 123 | self._write(b"\xff") 124 | 125 | def encode_infinite_list(self, iterable): 126 | self._write(b"\x9f") 127 | for elem in iterable: 128 | self.encode(elem) 129 | self._write(b"\xff") 130 | 131 | def encode_infinite_dict(self, iterable): 132 | self._write(b"\xbf") 133 | for key, value in iterable: 134 | self.encode(key) 135 | self.encode(value) 136 | self._write(b"\xff") 137 | 138 | def _write(self, val): 139 | self.output.write(val) 140 | 141 | class InfiniteEncoder(Encoder): 142 | chunksize = 8 143 | 144 | def __init__(self, output, chunksize=None): 145 | Encoder.__init__(self, output) 146 | if chunksize is not None: 147 | self.chunksize = chunksize 148 | 149 | def encode_list(self, object): 150 | self.encode_infinite_list(object) 151 | 152 | def encode_dict(self, object): 153 | self.encode_infinite_dict(object.items()) 154 | 155 | def encode_textstring(self, object): 156 | if len(object) <= self.chunksize: 157 | Encoder.encode_textstring(self, object) 158 | else: 159 | chunks = int((len(object) - 1) / self.chunksize) + 1 160 | generator = (object[n*self.chunksize:(n+1)*self.chunksize] for n in range(chunks)) 161 | self.encode_infinite_textstring(generator) 162 | 163 | def encode_bytestring(self, object): 164 | if len(object) <= self.chunksize: 165 | Encoder.encode_bytestring(self, object) 166 | else: 167 | chunks = int((len(object) - 1) / self.chunksize) + 1 168 | generator = (object[n*self.chunksize:(n+1)*self.chunksize] for n in range(chunks)) 169 | self.encode_infinite_bytestring(generator) 170 | 171 | def dump(obj, io, cls=Encoder, *args, **kwargs): 172 | cls(io, *args, **kwargs).encode(obj) 173 | 174 | def dumps(obj, *args, **kwargs): 175 | buf = io.BytesIO() 176 | dump(obj, buf, *args, **kwargs) 177 | return buf.getvalue() 178 | 179 | def _encode_ibyte(major, length): 180 | if length < 24: 181 | return to_bytes((major << 5) | length, 1, "big") 182 | elif length < 256: 183 | return to_bytes((major << 5) | 24, 1, "big") + to_bytes(length, 1, "big") 184 | elif length < 65536: 185 | return to_bytes((major << 5) | 25, 1, "big") + to_bytes(length, 2, "big") 186 | elif length < 4294967296: 187 | return to_bytes((major << 5) | 26, 1, "big") + to_bytes(length, 4, "big") 188 | elif length < 18446744073709551616: 189 | return to_bytes((major << 5) | 27, 1, "big") + to_bytes(length, 8, "big") 190 | else: 191 | return None 192 | 193 | __all__ = ["Encoder", "InfiniteEncoder", "EncoderError", "dump", "dumps"] 194 | 195 | --------------------------------------------------------------------------------