Skip to content

Commit 685026d

Browse files
committed
Split _msgpack.pyx (fix #34)
2 parents 171145e + 1c5b865 commit 685026d

File tree

6 files changed

+285
-261
lines changed

6 files changed

+285
-261
lines changed

.gitignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,5 @@ dist/*
77
*.so
88
*~
99
msgpack/__version__.py
10-
msgpack/_msgpack.c
11-
msgpack/_msgpack.cpp
10+
msgpack/*.cpp
1211
*.egg-info

msgpack/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# coding: utf-8
22
from msgpack._version import version
3-
from msgpack._msgpack import *
3+
from msgpack.exceptions import *
4+
from msgpack._packer import pack, packb, Packer
5+
from msgpack._unpacker import unpack, unpackb, Unpacker
46

57
# alias for compatibility to simplejson/marshal/pickle.
68
load = unpack

msgpack/_packer.pyx

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
# coding: utf-8
2+
#cython: embedsignature=True
3+
4+
from cpython cimport *
5+
cdef extern from "Python.h":
6+
ctypedef char* const_char_ptr "const char*"
7+
ctypedef char* const_void_ptr "const void*"
8+
ctypedef struct PyObject
9+
cdef int PyObject_AsReadBuffer(object o, const_void_ptr* buff, Py_ssize_t* buf_len) except -1
10+
11+
from libc.stdlib cimport *
12+
from libc.string cimport *
13+
from libc.limits cimport *
14+
15+
from msgpack.exceptions import PackValueError
16+
17+
cdef extern from "pack.h":
18+
struct msgpack_packer:
19+
char* buf
20+
size_t length
21+
size_t buf_size
22+
23+
int msgpack_pack_int(msgpack_packer* pk, int d)
24+
int msgpack_pack_nil(msgpack_packer* pk)
25+
int msgpack_pack_true(msgpack_packer* pk)
26+
int msgpack_pack_false(msgpack_packer* pk)
27+
int msgpack_pack_long(msgpack_packer* pk, long d)
28+
int msgpack_pack_long_long(msgpack_packer* pk, long long d)
29+
int msgpack_pack_unsigned_long_long(msgpack_packer* pk, unsigned long long d)
30+
int msgpack_pack_float(msgpack_packer* pk, float d)
31+
int msgpack_pack_double(msgpack_packer* pk, double d)
32+
int msgpack_pack_array(msgpack_packer* pk, size_t l)
33+
int msgpack_pack_map(msgpack_packer* pk, size_t l)
34+
int msgpack_pack_raw(msgpack_packer* pk, size_t l)
35+
int msgpack_pack_raw_body(msgpack_packer* pk, char* body, size_t l)
36+
37+
cdef int DEFAULT_RECURSE_LIMIT=511
38+
39+
40+
41+
cdef class Packer(object):
42+
"""MessagePack Packer
43+
44+
usage:
45+
46+
packer = Packer()
47+
astream.write(packer.pack(a))
48+
astream.write(packer.pack(b))
49+
50+
Packer's constructor has some keyword arguments:
51+
52+
* *defaut* - Convert user type to builtin type that Packer supports.
53+
See also simplejson's document.
54+
* *encoding* - Convert unicode to bytes with this encoding. (default: 'utf-8')
55+
* *unicode_erros* - Error handler for encoding unicode. (default: 'strict')
56+
* *use_single_float* - Use single precision float type for float. (default: False)
57+
* *autoreset* - Reset buffer after each pack and return it's content as `bytes`. (default: True).
58+
If set this to false, use `bytes()` to get content and `.reset()` to clear buffer.
59+
"""
60+
cdef msgpack_packer pk
61+
cdef object _default
62+
cdef object _bencoding
63+
cdef object _berrors
64+
cdef char *encoding
65+
cdef char *unicode_errors
66+
cdef bool use_float
67+
cdef bint autoreset
68+
69+
def __cinit__(self):
70+
cdef int buf_size = 1024*1024
71+
self.pk.buf = <char*> malloc(buf_size);
72+
if self.pk.buf == NULL:
73+
raise MemoryError("Unable to allocate internal buffer.")
74+
self.pk.buf_size = buf_size
75+
self.pk.length = 0
76+
77+
def __init__(self, default=None, encoding='utf-8', unicode_errors='strict', use_single_float=False, bint autoreset=1):
78+
self.use_float = use_single_float
79+
self.autoreset = autoreset
80+
if default is not None:
81+
if not PyCallable_Check(default):
82+
raise TypeError("default must be a callable.")
83+
self._default = default
84+
if encoding is None:
85+
self.encoding = NULL
86+
self.unicode_errors = NULL
87+
else:
88+
if isinstance(encoding, unicode):
89+
self._bencoding = encoding.encode('ascii')
90+
else:
91+
self._bencoding = encoding
92+
self.encoding = PyBytes_AsString(self._bencoding)
93+
if isinstance(unicode_errors, unicode):
94+
self._berrors = unicode_errors.encode('ascii')
95+
else:
96+
self._berrors = unicode_errors
97+
self.unicode_errors = PyBytes_AsString(self._berrors)
98+
99+
def __dealloc__(self):
100+
free(self.pk.buf);
101+
102+
cdef int _pack(self, object o, int nest_limit=DEFAULT_RECURSE_LIMIT) except -1:
103+
cdef long long llval
104+
cdef unsigned long long ullval
105+
cdef long longval
106+
cdef float fval
107+
cdef double dval
108+
cdef char* rawval
109+
cdef int ret
110+
cdef dict d
111+
112+
if nest_limit < 0:
113+
raise PackValueError("recursion limit exceeded.")
114+
115+
if o is None:
116+
ret = msgpack_pack_nil(&self.pk)
117+
elif isinstance(o, bool):
118+
if o:
119+
ret = msgpack_pack_true(&self.pk)
120+
else:
121+
ret = msgpack_pack_false(&self.pk)
122+
elif PyLong_Check(o):
123+
if o > 0:
124+
ullval = o
125+
ret = msgpack_pack_unsigned_long_long(&self.pk, ullval)
126+
else:
127+
llval = o
128+
ret = msgpack_pack_long_long(&self.pk, llval)
129+
elif PyInt_Check(o):
130+
longval = o
131+
ret = msgpack_pack_long(&self.pk, longval)
132+
elif PyFloat_Check(o):
133+
if self.use_float:
134+
fval = o
135+
ret = msgpack_pack_float(&self.pk, fval)
136+
else:
137+
dval = o
138+
ret = msgpack_pack_double(&self.pk, dval)
139+
elif PyBytes_Check(o):
140+
rawval = o
141+
ret = msgpack_pack_raw(&self.pk, len(o))
142+
if ret == 0:
143+
ret = msgpack_pack_raw_body(&self.pk, rawval, len(o))
144+
elif PyUnicode_Check(o):
145+
if not self.encoding:
146+
raise TypeError("Can't encode unicode string: no encoding is specified")
147+
o = PyUnicode_AsEncodedString(o, self.encoding, self.unicode_errors)
148+
rawval = o
149+
ret = msgpack_pack_raw(&self.pk, len(o))
150+
if ret == 0:
151+
ret = msgpack_pack_raw_body(&self.pk, rawval, len(o))
152+
elif PyDict_CheckExact(o):
153+
d = <dict>o
154+
ret = msgpack_pack_map(&self.pk, len(d))
155+
if ret == 0:
156+
for k, v in d.iteritems():
157+
ret = self._pack(k, nest_limit-1)
158+
if ret != 0: break
159+
ret = self._pack(v, nest_limit-1)
160+
if ret != 0: break
161+
elif PyDict_Check(o):
162+
ret = msgpack_pack_map(&self.pk, len(o))
163+
if ret == 0:
164+
for k, v in o.items():
165+
ret = self._pack(k, nest_limit-1)
166+
if ret != 0: break
167+
ret = self._pack(v, nest_limit-1)
168+
if ret != 0: break
169+
elif PyTuple_Check(o) or PyList_Check(o):
170+
ret = msgpack_pack_array(&self.pk, len(o))
171+
if ret == 0:
172+
for v in o:
173+
ret = self._pack(v, nest_limit-1)
174+
if ret != 0: break
175+
elif self._default:
176+
o = self._default(o)
177+
ret = self._pack(o, nest_limit-1)
178+
else:
179+
raise TypeError("can't serialize %r" % (o,))
180+
return ret
181+
182+
cpdef pack(self, object obj):
183+
cdef int ret
184+
ret = self._pack(obj, DEFAULT_RECURSE_LIMIT)
185+
if ret == -1:
186+
raise MemoryError
187+
elif ret: # should not happen.
188+
raise TypeError
189+
if self.autoreset:
190+
buf = PyBytes_FromStringAndSize(self.pk.buf, self.pk.length)
191+
self.pk.length = 0
192+
return buf
193+
194+
def pack_array_header(self, size_t size):
195+
cdef int ret = msgpack_pack_array(&self.pk, size)
196+
if ret == -1:
197+
raise MemoryError
198+
elif ret: # should not happen
199+
raise TypeError
200+
if self.autoreset:
201+
buf = PyBytes_FromStringAndSize(self.pk.buf, self.pk.length)
202+
self.pk.length = 0
203+
return buf
204+
205+
def pack_map_header(self, size_t size):
206+
cdef int ret = msgpack_pack_map(&self.pk, size)
207+
if ret == -1:
208+
raise MemoryError
209+
elif ret: # should not happen
210+
raise TypeError
211+
if self.autoreset:
212+
buf = PyBytes_FromStringAndSize(self.pk.buf, self.pk.length)
213+
self.pk.length = 0
214+
return buf
215+
216+
def pack_map_pairs(self, object pairs):
217+
"""
218+
Pack *pairs* as msgpack map type.
219+
220+
*pairs* should sequence of pair.
221+
(`len(pairs)` and `for k, v in *pairs*:` should be supported.)
222+
"""
223+
cdef int ret = msgpack_pack_map(&self.pk, len(pairs))
224+
if ret == 0:
225+
for k, v in pairs:
226+
ret = self._pack(k)
227+
if ret != 0: break
228+
ret = self._pack(v)
229+
if ret != 0: break
230+
if ret == -1:
231+
raise MemoryError
232+
elif ret: # should not happen
233+
raise TypeError
234+
if self.autoreset:
235+
buf = PyBytes_FromStringAndSize(self.pk.buf, self.pk.length)
236+
self.pk.length = 0
237+
return buf
238+
239+
def reset(self):
240+
"""Clear internal buffer."""
241+
self.pk.length = 0
242+
243+
def bytes(self):
244+
"""Return buffer content."""
245+
return PyBytes_FromStringAndSize(self.pk.buf, self.pk.length)
246+
247+
248+
def pack(object o, object stream, default=None, encoding='utf-8', unicode_errors='strict'):
249+
"""
250+
pack an object `o` and write it to stream)."""
251+
packer = Packer(default=default, encoding=encoding, unicode_errors=unicode_errors)
252+
stream.write(packer.pack(o))
253+
254+
def packb(object o, default=None, encoding='utf-8', unicode_errors='strict', use_single_float=False):
255+
"""
256+
pack o and return packed bytes."""
257+
packer = Packer(default=default, encoding=encoding, unicode_errors=unicode_errors,
258+
use_single_float=use_single_float)
259+
return packer.pack(o)

0 commit comments

Comments
 (0)