@@ -8,16 +8,23 @@ from cpython.bytes cimport (
88)
99from cpython.buffer cimport (
1010 Py_buffer,
11- PyBuffer_Release ,
11+ PyObject_CheckBuffer ,
1212 PyObject_GetBuffer,
13+ PyBuffer_Release,
14+ PyBuffer_IsContiguous,
15+ PyBUF_READ,
1316 PyBUF_SIMPLE,
17+ PyBUF_FULL_RO,
1418)
1519from cpython.mem cimport PyMem_Malloc, PyMem_Free
1620from cpython.object cimport PyCallable_Check
21+ from cpython.ref cimport Py_DECREF
22+ from cpython.exc cimport PyErr_WarnEx
1723
1824cdef extern from " Python.h" :
1925 ctypedef struct PyObject
2026 cdef int PyObject_AsReadBuffer(object o, const void ** buff, Py_ssize_t* buf_len) except - 1
27+ object PyMemoryView_GetContiguous(object obj, int buffertype, char order)
2128
2229from libc.stdlib cimport *
2330from libc.string cimport *
@@ -110,6 +117,42 @@ cdef inline init_ctx(unpack_context *ctx,
110117def default_read_extended_type (typecode , data ):
111118 raise NotImplementedError (" Cannot decode extended type with typecode=%d " % typecode)
112119
120+ cdef inline int get_data_from_buffer(object obj,
121+ Py_buffer * view,
122+ char ** buf,
123+ Py_ssize_t * buffer_len,
124+ int * new_protocol) except 0 :
125+ cdef object contiguous
126+ cdef Py_buffer tmp
127+ if PyObject_CheckBuffer(obj):
128+ new_protocol[0 ] = 1
129+ if PyObject_GetBuffer(obj, view, PyBUF_FULL_RO) == - 1 :
130+ raise
131+ if view.itemsize != 1 :
132+ PyBuffer_Release(view)
133+ raise BufferError(" cannot unpack from multi-byte object" )
134+ if PyBuffer_IsContiguous(view, ' A' ) == 0 :
135+ PyBuffer_Release(view)
136+ # create a contiguous copy and get buffer
137+ contiguous = PyMemoryView_GetContiguous(obj, PyBUF_READ, ' C' )
138+ PyObject_GetBuffer(contiguous, view, PyBUF_SIMPLE)
139+ # view must hold the only reference to contiguous,
140+ # so memory is freed when view is released
141+ Py_DECREF(contiguous)
142+ buffer_len[0 ] = view.len
143+ buf[0 ] = < char * > view.buf
144+ return 1
145+ else :
146+ new_protocol[0 ] = 0
147+ if PyObject_AsReadBuffer(obj, < const void ** > buf, buffer_len) == - 1 :
148+ raise BufferError(" could not get memoryview" )
149+ PyErr_WarnEx(RuntimeWarning ,
150+ " using old buffer interface to unpack %s ; "
151+ " this leads to unpacking errors if slicing is used and "
152+ " will be removed in a future version" % type (obj),
153+ 1 )
154+ return 1
155+
113156def unpackb (object packed , object object_hook = None , object list_hook = None ,
114157 bint use_list = 1 , encoding = None , unicode_errors = " strict" ,
115158 object_pairs_hook = None , ext_hook = ExtType,
@@ -129,27 +172,34 @@ def unpackb(object packed, object object_hook=None, object list_hook=None,
129172 cdef Py_ssize_t off = 0
130173 cdef int ret
131174
132- cdef char * buf
175+ cdef Py_buffer view
176+ cdef char * buf = NULL
133177 cdef Py_ssize_t buf_len
134178 cdef char * cenc = NULL
135179 cdef char * cerr = NULL
180+ cdef int new_protocol = 0
181+
182+ get_data_from_buffer(packed, & view, & buf, & buf_len, & new_protocol)
136183
137- PyObject_AsReadBuffer(packed, < const void ** > & buf, & buf_len)
184+ try :
185+ if encoding is not None :
186+ if isinstance (encoding, unicode ):
187+ encoding = encoding.encode(' ascii' )
188+ cenc = PyBytes_AsString(encoding)
138189
139- if encoding is not None :
140- if isinstance (encoding , unicode ):
141- encoding = encoding .encode(' ascii' )
142- cenc = PyBytes_AsString(encoding )
190+ if unicode_errors is not None :
191+ if isinstance (unicode_errors , unicode ):
192+ unicode_errors = unicode_errors .encode(' ascii' )
193+ cerr = PyBytes_AsString(unicode_errors )
143194
144- if unicode_errors is not None :
145- if isinstance (unicode_errors, unicode ):
146- unicode_errors = unicode_errors.encode(' ascii' )
147- cerr = PyBytes_AsString(unicode_errors)
195+ init_ctx(& ctx, object_hook, object_pairs_hook, list_hook, ext_hook,
196+ use_list, cenc, cerr,
197+ max_str_len, max_bin_len, max_array_len, max_map_len, max_ext_len)
198+ ret = unpack_construct(& ctx, buf, buf_len, & off)
199+ finally :
200+ if new_protocol:
201+ PyBuffer_Release(& view);
148202
149- init_ctx(& ctx, object_hook, object_pairs_hook, list_hook, ext_hook,
150- use_list, cenc, cerr,
151- max_str_len, max_bin_len, max_array_len, max_map_len, max_ext_len)
152- ret = unpack_construct(& ctx, buf, buf_len, & off)
153203 if ret == 1 :
154204 obj = unpack_data(& ctx)
155205 if off < buf_len:
@@ -335,14 +385,20 @@ cdef class Unpacker(object):
335385 def feed (self , object next_bytes ):
336386 """ Append `next_bytes` to internal buffer."""
337387 cdef Py_buffer pybuff
388+ cdef int new_protocol = 0
389+ cdef char * buf
390+ cdef Py_ssize_t buf_len
391+
338392 if self .file_like is not None :
339393 raise AssertionError (
340394 " unpacker.feed() is not be able to use with `file_like`." )
341- PyObject_GetBuffer(next_bytes, & pybuff, PyBUF_SIMPLE)
395+
396+ get_data_from_buffer(next_bytes, & pybuff, & buf, & buf_len, & new_protocol)
342397 try :
343- self .append_buffer(< char * > pybuff. buf, pybuff.len )
398+ self .append_buffer(buf, buf_len )
344399 finally :
345- PyBuffer_Release(& pybuff)
400+ if new_protocol:
401+ PyBuffer_Release(& pybuff)
346402
347403 cdef append_buffer(self , void * _buf, Py_ssize_t _buf_len):
348404 cdef:
0 commit comments