Skip to content

Commit a77dd09

Browse files
committed
use memoryview in fallback and fix memory unpacking
1 parent a5c8baf commit a77dd09

File tree

3 files changed

+54
-28
lines changed

3 files changed

+54
-28
lines changed

msgpack/_unpacker.pyx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ from cpython.bytes cimport (
88
)
99
from cpython.buffer cimport (
1010
Py_buffer,
11+
PyObject_CheckBuffer,
1112
PyBuffer_Release,
1213
PyObject_GetBuffer,
1314
PyBUF_SIMPLE,
@@ -129,12 +130,20 @@ def unpackb(object packed, object object_hook=None, object list_hook=None,
129130
cdef Py_ssize_t off = 0
130131
cdef int ret
131132

132-
cdef char* buf
133+
cdef Py_buffer view
134+
cdef char* buf = NULL
133135
cdef Py_ssize_t buf_len
134136
cdef char* cenc = NULL
135137
cdef char* cerr = NULL
138+
cdef char buffer_supported = 0
136139

137-
PyObject_AsReadBuffer(packed, <const void**>&buf, &buf_len)
140+
if PyObject_CheckBuffer(packed):
141+
buffer_supported = 1
142+
if PyObject_GetBuffer(packed, &view, PyBUF_SIMPLE) == 0:
143+
buf = <char*> view.buf
144+
buf_len = view.len
145+
else:
146+
PyObject_AsReadBuffer(packed, <const void**>&buf, &buf_len)
138147

139148
if encoding is not None:
140149
if isinstance(encoding, unicode):
@@ -150,6 +159,8 @@ def unpackb(object packed, object object_hook=None, object list_hook=None,
150159
use_list, cenc, cerr,
151160
max_str_len, max_bin_len, max_array_len, max_map_len, max_ext_len)
152161
ret = unpack_construct(&ctx, buf, buf_len, &off)
162+
if buffer_supported:
163+
PyBuffer_Release(&view);
153164
if ret == 1:
154165
obj = unpack_data(&ctx)
155166
if off < buf_len:

msgpack/fallback.py

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -246,16 +246,16 @@ def __init__(self, file_like=None, read_size=0, use_list=True,
246246
raise TypeError("`ext_hook` is not callable")
247247

248248
def feed(self, next_bytes):
249+
assert self._fb_feeding
249250
if isinstance(next_bytes, array.array):
250251
next_bytes = next_bytes.tostring()
251-
elif isinstance(next_bytes, bytearray):
252-
next_bytes = bytes(next_bytes)
253-
assert self._fb_feeding
254-
if (self._fb_buf_n + len(next_bytes) - self._fb_sloppiness
255-
> self._max_buffer_size):
252+
view = memoryview(next_bytes)
253+
assert view.itemsize == 1
254+
L = len(view)
255+
if self._fb_buf_n + L - self._fb_sloppiness > self._max_buffer_size:
256256
raise BufferFull
257-
self._fb_buf_n += len(next_bytes)
258-
self._fb_buffers.append(next_bytes)
257+
self._fb_buf_n += L
258+
self._fb_buffers.append(view)
259259

260260
def _fb_sloppy_consume(self):
261261
""" Gets rid of some of the used parts of the buffer. """
@@ -322,9 +322,10 @@ def _fb_read(self, n, write_bytes=None):
322322
return buffs[self._fb_buf_i][self._fb_buf_o - n:self._fb_buf_o]
323323

324324
# The remaining cases.
325-
ret = b''
326-
while len(ret) != n:
327-
sliced = n - len(ret)
325+
ret = []
326+
n_read = 0
327+
while n_read != n:
328+
sliced = n - n_read
328329
if self._fb_buf_i == len(buffs):
329330
if self._fb_feeding:
330331
break
@@ -334,34 +335,39 @@ def _fb_read(self, n, write_bytes=None):
334335
tmp = self.file_like.read(to_read)
335336
if not tmp:
336337
break
337-
buffs.append(tmp)
338-
self._fb_buf_n += len(tmp)
338+
tmpview = memoryview(tmp)
339+
assert tmpview.itemsize == 1
340+
buffs.append(tmpview)
341+
self._fb_buf_n += len(tmpview)
339342
continue
340-
ret += buffs[self._fb_buf_i][self._fb_buf_o:self._fb_buf_o + sliced]
343+
to_append = buffs[self._fb_buf_i][self._fb_buf_o:self._fb_buf_o + sliced]
344+
n_read += len(to_append)
345+
ret.append(to_append)
341346
self._fb_buf_o += sliced
342347
if self._fb_buf_o >= len(buffs[self._fb_buf_i]):
343348
self._fb_buf_o = 0
344349
self._fb_buf_i += 1
350+
ret = b''.join([view.tobytes() for view in ret])
345351
if len(ret) != n:
346352
self._fb_rollback()
347353
raise OutOfData
348354
if write_bytes is not None:
349355
write_bytes(ret)
350-
return ret
356+
return memoryview(ret)
351357

352358
def _read_header(self, execute=EX_CONSTRUCT, write_bytes=None):
353359
typ = TYPE_IMMEDIATE
354360
n = 0
355361
obj = None
356-
c = self._fb_read(1, write_bytes)
362+
c = self._fb_read(1, write_bytes).tobytes()
357363
b = ord(c)
358-
if b & 0b10000000 == 0:
364+
if b & 0b10000000 == 0:
359365
obj = b
360366
elif b & 0b11100000 == 0b11100000:
361367
obj = struct.unpack("b", c)[0]
362368
elif b & 0b11100000 == 0b10100000:
363369
n = b & 0b00011111
364-
obj = self._fb_read(n, write_bytes)
370+
obj = self._fb_read(n, write_bytes).tobytes()
365371
typ = TYPE_RAW
366372
if n > self._max_str_len:
367373
raise UnpackValueError("%s exceeds max_str_len(%s)", n, self._max_str_len)
@@ -386,37 +392,37 @@ def _read_header(self, execute=EX_CONSTRUCT, write_bytes=None):
386392
n = struct.unpack("B", self._fb_read(1, write_bytes))[0]
387393
if n > self._max_bin_len:
388394
raise UnpackValueError("%s exceeds max_bin_len(%s)" % (n, self._max_bin_len))
389-
obj = self._fb_read(n, write_bytes)
395+
obj = self._fb_read(n, write_bytes).tobytes()
390396
elif b == 0xc5:
391397
typ = TYPE_BIN
392398
n = struct.unpack(">H", self._fb_read(2, write_bytes))[0]
393399
if n > self._max_bin_len:
394400
raise UnpackValueError("%s exceeds max_bin_len(%s)" % (n, self._max_bin_len))
395-
obj = self._fb_read(n, write_bytes)
401+
obj = self._fb_read(n, write_bytes).tobytes()
396402
elif b == 0xc6:
397403
typ = TYPE_BIN
398404
n = struct.unpack(">I", self._fb_read(4, write_bytes))[0]
399405
if n > self._max_bin_len:
400406
raise UnpackValueError("%s exceeds max_bin_len(%s)" % (n, self._max_bin_len))
401-
obj = self._fb_read(n, write_bytes)
407+
obj = self._fb_read(n, write_bytes).tobytes()
402408
elif b == 0xc7: # ext 8
403409
typ = TYPE_EXT
404410
L, n = struct.unpack('Bb', self._fb_read(2, write_bytes))
405411
if L > self._max_ext_len:
406412
raise UnpackValueError("%s exceeds max_ext_len(%s)" % (L, self._max_ext_len))
407-
obj = self._fb_read(L, write_bytes)
413+
obj = self._fb_read(L, write_bytes).tobytes()
408414
elif b == 0xc8: # ext 16
409415
typ = TYPE_EXT
410416
L, n = struct.unpack('>Hb', self._fb_read(3, write_bytes))
411417
if L > self._max_ext_len:
412418
raise UnpackValueError("%s exceeds max_ext_len(%s)" % (L, self._max_ext_len))
413-
obj = self._fb_read(L, write_bytes)
419+
obj = self._fb_read(L, write_bytes).tobytes()
414420
elif b == 0xc9: # ext 32
415421
typ = TYPE_EXT
416422
L, n = struct.unpack('>Ib', self._fb_read(5, write_bytes))
417423
if L > self._max_ext_len:
418424
raise UnpackValueError("%s exceeds max_ext_len(%s)" % (L, self._max_ext_len))
419-
obj = self._fb_read(L, write_bytes)
425+
obj = self._fb_read(L, write_bytes).tobytes()
420426
elif b == 0xca:
421427
obj = struct.unpack(">f", self._fb_read(4, write_bytes))[0]
422428
elif b == 0xcb:
@@ -467,19 +473,19 @@ def _read_header(self, execute=EX_CONSTRUCT, write_bytes=None):
467473
n = struct.unpack("B", self._fb_read(1, write_bytes))[0]
468474
if n > self._max_str_len:
469475
raise UnpackValueError("%s exceeds max_str_len(%s)", n, self._max_str_len)
470-
obj = self._fb_read(n, write_bytes)
476+
obj = self._fb_read(n, write_bytes).tobytes()
471477
elif b == 0xda:
472478
typ = TYPE_RAW
473479
n = struct.unpack(">H", self._fb_read(2, write_bytes))[0]
474480
if n > self._max_str_len:
475481
raise UnpackValueError("%s exceeds max_str_len(%s)", n, self._max_str_len)
476-
obj = self._fb_read(n, write_bytes)
482+
obj = self._fb_read(n, write_bytes).tobytes()
477483
elif b == 0xdb:
478484
typ = TYPE_RAW
479485
n = struct.unpack(">I", self._fb_read(4, write_bytes))[0]
480486
if n > self._max_str_len:
481487
raise UnpackValueError("%s exceeds max_str_len(%s)", n, self._max_str_len)
482-
obj = self._fb_read(n, write_bytes)
488+
obj = self._fb_read(n, write_bytes).tobytes()
483489
elif b == 0xdc:
484490
n = struct.unpack(">H", self._fb_read(2, write_bytes))[0]
485491
if n > self._max_array_len:

test/test_buffer.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,12 @@ def test_unpack_bytearray():
1818
assert [b'foo', b'bar'] == obj
1919
expected_type = bytes
2020
assert all(type(s) == expected_type for s in obj)
21+
22+
23+
def test_unpack_memoryview():
24+
buf = bytearray(packb(('foo', 'bar')))
25+
view = memoryview(buf)
26+
obj = unpackb(view, use_list=1)
27+
assert [b'foo', b'bar'] == obj
28+
expected_type = bytes
29+
assert all(type(s) == expected_type for s in obj)

0 commit comments

Comments
 (0)