11# coding: utf-8
22# cython: embedsignature=True
33
4+ import warnings
5+
46from cpython cimport *
57cdef extern from " Python.h" :
68 ctypedef char * const_char_ptr " const char*"
79 ctypedef char * const_void_ptr " const void*"
810 ctypedef struct PyObject
911 cdef int PyObject_AsReadBuffer(object o, const_void_ptr* buff, Py_ssize_t* buf_len) except - 1
12+ char * __FILE__
13+ int __LINE__
1014
1115from libc.stdlib cimport *
1216from libc.string cimport *
@@ -139,11 +143,19 @@ cdef class Packer(object):
139143 ret = msgpack_pack_raw(& self .pk, len (o))
140144 if ret == 0 :
141145 ret = msgpack_pack_raw_body(& self .pk, rawval, len (o))
142- elif PyDict_Check (o):
146+ elif PyDict_CheckExact (o):
143147 d = < dict > o
144148 ret = msgpack_pack_map(& self .pk, len (d))
145149 if ret == 0 :
146- for k,v in d.iteritems():
150+ for k, v in d.iteritems():
151+ ret = self ._pack(k, nest_limit- 1 )
152+ if ret != 0 : break
153+ ret = self ._pack(v, nest_limit- 1 )
154+ if ret != 0 : break
155+ elif PyDict_Check(o):
156+ ret = msgpack_pack_map(& self .pk, len (o))
157+ if ret == 0 :
158+ for k, v in o.items():
147159 ret = self ._pack(k, nest_limit- 1 )
148160 if ret != 0 : break
149161 ret = self ._pack(v, nest_limit- 1 )
@@ -170,6 +182,17 @@ cdef class Packer(object):
170182 self .pk.length = 0
171183 return buf
172184
185+ cpdef pack_array_header(self , size_t size):
186+ msgpack_pack_array(& self .pk, size)
187+ buf = PyBytes_FromStringAndSize(self .pk.buf, self .pk.length)
188+ self .pk.length = 0
189+ return buf
190+
191+ cpdef pack_map_header(self , size_t size):
192+ msgpack_pack_map(& self .pk, size)
193+ buf = PyBytes_FromStringAndSize(self .pk.buf, self .pk.length)
194+ self .pk.length = 0
195+ return buf
173196
174197def pack (object o , object stream , default = None , encoding = ' utf-8' , unicode_errors = ' strict' ):
175198 """
@@ -187,8 +210,9 @@ def packb(object o, default=None, encoding='utf-8', unicode_errors='strict', use
187210
188211cdef extern from " unpack.h" :
189212 ctypedef struct msgpack_user:
190- int use_list
213+ bint use_list
191214 PyObject* object_hook
215+ bint has_pairs_hook # call object_hook with k-v pairs
192216 PyObject* list_hook
193217 char * encoding
194218 char * unicode_errors
@@ -200,71 +224,98 @@ cdef extern from "unpack.h":
200224 unsigned int ct
201225 PyObject* key
202226
203- int template_execute(template_context* ctx, const_char_ptr data,
204- size_t len , size_t* off) except - 1
227+ ctypedef int (* execute_fn)(template_context* ctx, const_char_ptr data,
228+ size_t len , size_t* off) except - 1
229+ execute_fn template_construct
230+ execute_fn template_skip
231+ execute_fn read_array_header
232+ execute_fn read_map_header
205233 void template_init(template_context* ctx)
206234 object template_data(template_context* ctx)
207235
236+ cdef inline init_ctx(template_context * ctx, object object_hook, object object_pairs_hook, object list_hook, bint use_list, encoding, unicode_errors):
237+ template_init(ctx)
238+ ctx.user.use_list = use_list
239+ ctx.user.object_hook = ctx.user.list_hook = < PyObject* > NULL
240+
241+ if object_hook is not None and object_pairs_hook is not None :
242+ raise ValueError (" object_pairs_hook and object_hook are mutually exclusive." )
243+
244+ if object_hook is not None :
245+ if not PyCallable_Check(object_hook):
246+ raise TypeError (" object_hook must be a callable." )
247+ ctx.user.object_hook = < PyObject* > object_hook
248+
249+ if object_pairs_hook is None :
250+ ctx.user.has_pairs_hook = False
251+ else :
252+ if not PyCallable_Check(object_pairs_hook):
253+ raise TypeError (" object_pairs_hook must be a callable." )
254+ ctx.user.object_hook = < PyObject* > object_pairs_hook
255+ ctx.user.has_pairs_hook = True
256+
257+ if list_hook is not None :
258+ if not PyCallable_Check(list_hook):
259+ raise TypeError (" list_hook must be a callable." )
260+ ctx.user.list_hook = < PyObject* > list_hook
261+
262+ if encoding is None :
263+ ctx.user.encoding = NULL
264+ ctx.user.unicode_errors = NULL
265+ else :
266+ if isinstance (encoding, unicode ):
267+ _bencoding = encoding.encode(' ascii' )
268+ else :
269+ _bencoding = encoding
270+ ctx.user.encoding = PyBytes_AsString(_bencoding)
271+ if isinstance (unicode_errors, unicode ):
272+ _berrors = unicode_errors.encode(' ascii' )
273+ else :
274+ _berrors = unicode_errors
275+ ctx.user.unicode_errors = PyBytes_AsString(_berrors)
208276
209277def unpackb (object packed , object object_hook = None , object list_hook = None ,
210- bint use_list = 0 , encoding = None , unicode_errors = " strict" ,
278+ bint use_list = 1 , encoding = None , unicode_errors = " strict" ,
279+ object_pairs_hook = None ,
211280 ):
281+ """ Unpack packed_bytes to object. Returns an unpacked object.
282+
283+ Raises `ValueError` when `packed` contains extra bytes.
212284 """
213- Unpack packed_bytes to object. Returns an unpacked object."""
214285 cdef template_context ctx
215286 cdef size_t off = 0
216287 cdef int ret
217288
218289 cdef char * buf
219290 cdef Py_ssize_t buf_len
220- PyObject_AsReadBuffer(packed, < const_void_ptr* > & buf, & buf_len)
221291
222- if encoding is None :
223- enc = NULL
224- err = NULL
225- else :
226- if isinstance (encoding, unicode ):
227- bencoding = encoding.encode(' ascii' )
228- else :
229- bencoding = encoding
230- if isinstance (unicode_errors, unicode ):
231- berrors = unicode_errors.encode(' ascii' )
232- else :
233- berrors = unicode_errors
234- enc = PyBytes_AsString(bencoding)
235- err = PyBytes_AsString(berrors)
292+ PyObject_AsReadBuffer(packed, < const_void_ptr* > & buf, & buf_len)
236293
237- template_init(& ctx)
238- ctx.user.use_list = use_list
239- ctx.user.object_hook = ctx.user.list_hook = NULL
240- ctx.user.encoding = < const_char_ptr> enc
241- ctx.user.unicode_errors = < const_char_ptr> err
242- if object_hook is not None :
243- if not PyCallable_Check(object_hook):
244- raise TypeError (" object_hook must be a callable." )
245- ctx.user.object_hook = < PyObject* > object_hook
246- if list_hook is not None :
247- if not PyCallable_Check(list_hook):
248- raise TypeError (" list_hook must be a callable." )
249- ctx.user.list_hook = < PyObject* > list_hook
250- ret = template_execute(& ctx, buf, buf_len, & off)
294+ init_ctx(& ctx, object_hook, object_pairs_hook, list_hook, use_list, encoding, unicode_errors)
295+ ret = template_construct(& ctx, buf, buf_len, & off)
251296 if ret == 1 :
252- return template_data(& ctx)
297+ obj = template_data(& ctx)
298+ if off < buf_len:
299+ raise ValueError (" Extra data." )
300+ return obj
253301 else :
254302 return None
255303
256304
257305def unpack (object stream , object object_hook = None , object list_hook = None ,
258- bint use_list = 0 , encoding = None , unicode_errors = " strict" ,
306+ bint use_list = 1 , encoding = None , unicode_errors = " strict" ,
307+ object_pairs_hook = None ,
259308 ):
260- """
261- unpack an object from stream.
309+ """ Unpack an object from `stream`.
310+
311+ Raises `ValueError` when `stream` has extra bytes.
262312 """
263313 return unpackb(stream.read(), use_list = use_list,
264- object_hook = object_hook, list_hook = list_hook,
314+ object_hook = object_hook, object_pairs_hook = object_pairs_hook, list_hook = list_hook,
265315 encoding = encoding, unicode_errors = unicode_errors,
266316 )
267317
318+
268319cdef class Unpacker(object ):
269320 """
270321 Streaming unpacker.
@@ -277,10 +328,13 @@ cdef class Unpacker(object):
277328 (default: min(1024**2, max_buffer_size))
278329
279330 If `use_list` is true, msgpack list is deserialized to Python list.
280- Otherwise, it is deserialized to Python tuple. (default: False)
331+ Otherwise, it is deserialized to Python tuple.
281332
282333 `object_hook` is same to simplejson. If it is not None, it should be callable
283- and Unpacker calls it when deserializing key-value.
334+ and Unpacker calls it with a dict argument after deserializing a map.
335+
336+ `object_pairs_hook` is same to simplejson. If it is not None, it should be callable
337+ and Unpacker calls it with a list of key-value pairs after deserializing a map.
284338
285339 `encoding` is encoding used for decoding msgpack bytes. If it is None (default),
286340 msgpack bytes is deserialized to Python bytes.
@@ -315,7 +369,6 @@ cdef class Unpacker(object):
315369 cdef object file_like
316370 cdef object file_like_read
317371 cdef Py_ssize_t read_size
318- cdef bint use_list
319372 cdef object object_hook
320373 cdef object _bencoding
321374 cdef object _berrors
@@ -330,10 +383,10 @@ cdef class Unpacker(object):
330383 free(self .buf)
331384 self .buf = NULL
332385
333- def __init__ (self , file_like = None , Py_ssize_t read_size = 0 , bint use_list = 0 ,
334- object object_hook = None , object list_hook = None ,
335- encoding = None , unicode_errors = ' strict' , int max_buffer_size = 0 ):
336- self .use_list = use_list
386+ def __init__ (self , file_like = None , Py_ssize_t read_size = 0 , bint use_list = 1 ,
387+ object object_hook = None , object object_pairs_hook = None , object list_hook = None ,
388+ encoding = None , unicode_errors = ' strict' , int max_buffer_size = 0 ,
389+ ):
337390 self .file_like = file_like
338391 if file_like:
339392 self .file_like_read = file_like.read
@@ -353,31 +406,7 @@ cdef class Unpacker(object):
353406 self .buf_size = read_size
354407 self .buf_head = 0
355408 self .buf_tail = 0
356- template_init(& self .ctx)
357- self .ctx.user.use_list = use_list
358- self .ctx.user.object_hook = self .ctx.user.list_hook = < PyObject* > NULL
359- if object_hook is not None :
360- if not PyCallable_Check(object_hook):
361- raise TypeError (" object_hook must be a callable." )
362- self .ctx.user.object_hook = < PyObject* > object_hook
363- if list_hook is not None :
364- if not PyCallable_Check(list_hook):
365- raise TypeError (" list_hook must be a callable." )
366- self .ctx.user.list_hook = < PyObject* > list_hook
367- if encoding is None :
368- self .ctx.user.encoding = NULL
369- self .ctx.user.unicode_errors = NULL
370- else :
371- if isinstance (encoding, unicode ):
372- self ._bencoding = encoding.encode(' ascii' )
373- else :
374- self ._bencoding = encoding
375- self .ctx.user.encoding = PyBytes_AsString(self ._bencoding)
376- if isinstance (unicode_errors, unicode ):
377- self ._berrors = unicode_errors.encode(' ascii' )
378- else :
379- self ._berrors = unicode_errors
380- self .ctx.user.unicode_errors = PyBytes_AsString(self ._berrors)
409+ init_ctx(& self .ctx, object_hook, object_pairs_hook, list_hook, use_list, encoding, unicode_errors)
381410
382411 def feed (self , object next_bytes ):
383412 cdef char * buf
@@ -438,20 +467,20 @@ cdef class Unpacker(object):
438467 else :
439468 self .file_like = None
440469
441- cpdef unpack(self ):
442- """ unpack one object"""
470+ cdef object _unpack(self , execute_fn execute):
443471 cdef int ret
472+ cdef object obj
444473 while 1 :
445- ret = template_execute (& self .ctx, self .buf, self .buf_tail, & self .buf_head)
474+ ret = execute (& self .ctx, self .buf, self .buf_tail, & self .buf_head)
446475 if ret == 1 :
447- o = template_data(& self .ctx)
476+ obj = template_data(& self .ctx)
448477 template_init(& self .ctx)
449- return o
478+ return obj
450479 elif ret == 0 :
451480 if self .file_like is not None :
452481 self .read_from_file()
453482 continue
454- raise StopIteration (" No more unpack data." )
483+ raise StopIteration (" No more data to unpack ." )
455484 else :
456485 raise ValueError (" Unpack failed: error = %d " % (ret,))
457486
@@ -465,11 +494,27 @@ cdef class Unpacker(object):
465494 ret += self .file_like.read(nbytes - len (ret))
466495 return ret
467496
497+ def unpack (self ):
498+ """ unpack one object"""
499+ return self ._unpack(template_construct)
500+
501+ def skip (self ):
502+ """ read and ignore one object, returning None"""
503+ return self ._unpack(template_skip)
504+
505+ def read_array_header (self ):
506+ """ assuming the next object is an array, return its size n, such that the next n unpack() calls will iterate over its contents."""
507+ return self ._unpack(read_array_header)
508+
509+ def read_map_header (self ):
510+ """ assuming the next object is a map, return its size n, such that the next n * 2 unpack() calls will iterate over its key-value pairs."""
511+ return self ._unpack(read_map_header)
512+
468513 def __iter__ (self ):
469514 return self
470515
471516 def __next__ (self ):
472- return self .unpack( )
517+ return self ._unpack(template_construct )
473518
474519 # for debug.
475520 # def _buf(self):
0 commit comments