Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 27 additions & 11 deletions msgpack/_packer.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ cdef class Packer:
cdef object _berrors
cdef const char *unicode_errors
cdef size_t exports # number of exported buffers
cdef size_t packing # nesting depth of pack calls that can run Python code
cdef bint strict_types
cdef bint use_float
cdef bint autoreset
Expand All @@ -118,6 +119,7 @@ cdef class Packer:
self.pk.buf_size = buf_size
self.pk.length = 0
self.exports = 0
self.packing = 0

def __dealloc__(self):
PyMem_Free(self.pk.buf)
Expand Down Expand Up @@ -273,11 +275,14 @@ cdef class Packer:
def pack(self, object obj):
cdef int ret
self._check_exports()
self.packing += 1
try:
ret = self._pack(obj, DEFAULT_RECURSE_LIMIT)
except:
self.pk.length = 0
raise
finally:
self.packing -= 1
if ret: # should not happen.
raise RuntimeError("internal error")
if self.autoreset:
Expand All @@ -288,10 +293,14 @@ cdef class Packer:
@cython.critical_section
def pack_ext_type(self, typecode, data):
self._check_exports()
if len(data) > ITEM_LIMIT:
raise ValueError("ext data too large")
msgpack_pack_ext(&self.pk, typecode, len(data))
msgpack_pack_raw_body(&self.pk, data, len(data))
self.packing += 1
try:
if len(data) > ITEM_LIMIT:
raise ValueError("ext data too large")
msgpack_pack_ext(&self.pk, typecode, len(data))
msgpack_pack_raw_body(&self.pk, data, len(data))
finally:
self.packing -= 1
if self.autoreset:
buf = PyBytes_FromStringAndSize(self.pk.buf, self.pk.length)
self.pk.length = 0
Expand Down Expand Up @@ -332,13 +341,17 @@ cdef class Packer:
(`len(pairs)` and `for k, v in pairs:` should be supported.)
"""
self._check_exports()
size = len(pairs)
if size > ITEM_LIMIT:
raise ValueError("map too large")
msgpack_pack_map(&self.pk, size)
for k, v in pairs:
self._pack(k)
self._pack(v)
self.packing += 1
try:
size = len(pairs)
if size > ITEM_LIMIT:
raise ValueError("map too large")
msgpack_pack_map(&self.pk, size)
for k, v in pairs:
self._pack(k)
self._pack(v)
finally:
self.packing -= 1
if self.autoreset:
buf = PyBytes_FromStringAndSize(self.pk.buf, self.pk.length)
self.pk.length = 0
Expand Down Expand Up @@ -367,6 +380,9 @@ cdef class Packer:

@cython.critical_section
def __getbuffer__(self, Py_buffer *buffer, int flags):
# Packing may reallocate pk.buf, which would leave the export dangling.
if self.packing:
raise BufferError("Packer cannot export its buffer while packing")
PyBuffer_FillInfo(buffer, self, self.pk.buf, self.pk.length, 1, flags)
self.exports += 1

Expand Down
14 changes: 14 additions & 0 deletions test/test_buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,17 @@ def test_packer_getbuffer():
buffer.release()
packer.pack(42)
assert bytes(packer) == b"\x92*\xa5hello*"


def test_packer_getbuffer_while_packing():
views = []

def default(obj):
views.append(packer.getbuffer())
return b"x" * 1024 # large enough to grow the internal buffer

packer = Packer(autoreset=False, buf_size=16, default=default)
with raises(BufferError):
packer.pack([object()])
with raises(BufferError):
packer.pack_map_pairs([(object(), 1)])
Loading