@@ -106,6 +106,7 @@ cdef class Packer:
106106 cdef object _default
107107 cdef object _berrors
108108 cdef const char * unicode_errors
109+ cdef size_t exports # number of exported buffers
109110 cdef bint strict_types
110111 cdef bint use_float
111112 cdef bint autoreset
@@ -117,10 +118,16 @@ cdef class Packer:
117118 raise MemoryError (" Unable to allocate internal buffer." )
118119 self .pk.buf_size = buf_size
119120 self .pk.length = 0
121+ self .exports = 0
120122
121123 def __dealloc__ (self ):
122124 PyMem_Free(self .pk.buf)
123125 self .pk.buf = NULL
126+ assert self .exports == 0
127+
128+ cdef _check_exports(self ):
129+ if self .exports > 0 :
130+ raise BufferError(" Existing exports of data: Packer cannot be changed" )
124131
125132 def __init__ (self , *, default = None ,
126133 bint use_single_float = False , bint autoreset = True , bint use_bin_type = True ,
@@ -149,16 +156,16 @@ cdef class Packer:
149156 cdef unsigned long ulval
150157 cdef const char * rawval
151158 cdef Py_ssize_t L
152- cdef bool strict_types = self .strict_types
153159 cdef Py_buffer view
160+ cdef bint strict = self .strict_types
154161
155162 if o is None :
156163 msgpack_pack_nil(& self .pk)
157164 elif o is True :
158165 msgpack_pack_true(& self .pk)
159166 elif o is False :
160167 msgpack_pack_false(& self .pk)
161- elif PyLong_CheckExact(o) if strict_types else PyLong_Check(o):
168+ elif PyLong_CheckExact(o) if strict else PyLong_Check(o):
162169 try :
163170 if o > 0 :
164171 ullval = o
@@ -171,19 +178,19 @@ cdef class Packer:
171178 return - 2
172179 else :
173180 raise OverflowError (" Integer value out of range" )
174- elif PyFloat_CheckExact(o) if strict_types else PyFloat_Check(o):
181+ elif PyFloat_CheckExact(o) if strict else PyFloat_Check(o):
175182 if self .use_float:
176183 msgpack_pack_float(& self .pk, < float > o)
177184 else :
178185 msgpack_pack_double(& self .pk, < double > o)
179- elif PyBytesLike_CheckExact(o) if strict_types else PyBytesLike_Check(o):
186+ elif PyBytesLike_CheckExact(o) if strict else PyBytesLike_Check(o):
180187 L = Py_SIZE(o)
181188 if L > ITEM_LIMIT:
182189 PyErr_Format(ValueError , b" %.200s object is too large" , Py_TYPE(o).tp_name)
183190 rawval = o
184191 msgpack_pack_bin(& self .pk, L)
185192 msgpack_pack_raw_body(& self .pk, rawval, L)
186- elif PyUnicode_CheckExact(o) if strict_types else PyUnicode_Check(o):
193+ elif PyUnicode_CheckExact(o) if strict else PyUnicode_Check(o):
187194 if self .unicode_errors == NULL :
188195 rawval = PyUnicode_AsUTF8AndSize(o, & L)
189196 if L > ITEM_LIMIT:
@@ -196,15 +203,15 @@ cdef class Packer:
196203 rawval = o
197204 msgpack_pack_raw(& self .pk, L)
198205 msgpack_pack_raw_body(& self .pk, rawval, L)
199- elif PyDict_CheckExact(o) if strict_types else PyDict_Check(o):
206+ elif PyDict_CheckExact(o) if strict else PyDict_Check(o):
200207 L = len (o)
201208 if L > ITEM_LIMIT:
202209 raise ValueError (" dict is too large" )
203210 msgpack_pack_map(& self .pk, L)
204211 for k, v in o.items():
205212 self ._pack(k, nest_limit)
206213 self ._pack(v, nest_limit)
207- elif type (o) is ExtType if strict_types else isinstance (o, ExtType):
214+ elif type (o) is ExtType if strict else isinstance (o, ExtType):
208215 # This should be before Tuple because ExtType is namedtuple.
209216 rawval = o.data
210217 L = len (o.data)
@@ -216,7 +223,7 @@ cdef class Packer:
216223 llval = o.seconds
217224 ulval = o.nanoseconds
218225 msgpack_pack_timestamp(& self .pk, llval, ulval)
219- elif PyList_CheckExact(o) if strict_types else (PyTuple_Check(o) or PyList_Check(o)):
226+ elif PyList_CheckExact(o) if strict else (PyTuple_Check(o) or PyList_Check(o)):
220227 L = Py_SIZE(o)
221228 if L > ITEM_LIMIT:
222229 raise ValueError (" list is too large" )
@@ -264,6 +271,7 @@ cdef class Packer:
264271
265272 def pack (self , object obj ):
266273 cdef int ret
274+ self ._check_exports()
267275 try :
268276 ret = self ._pack(obj, DEFAULT_RECURSE_LIMIT)
269277 except :
@@ -277,21 +285,26 @@ cdef class Packer:
277285 return buf
278286
279287 def pack_ext_type (self , typecode , data ):
288+ self ._check_exports()
289+ if len (data) > ITEM_LIMIT:
290+ raise ValueError (" ext data too large" )
280291 msgpack_pack_ext(& self .pk, typecode, len (data))
281292 msgpack_pack_raw_body(& self .pk, data, len (data))
282293
283294 def pack_array_header (self , long long size ):
295+ self ._check_exports()
284296 if size > ITEM_LIMIT:
285- raise ValueError
297+ raise ValueError ( " array too large " )
286298 msgpack_pack_array(& self .pk, size)
287299 if self .autoreset:
288300 buf = PyBytes_FromStringAndSize(self .pk.buf, self .pk.length)
289301 self .pk.length = 0
290302 return buf
291303
292304 def pack_map_header (self , long long size ):
305+ self ._check_exports()
293306 if size > ITEM_LIMIT:
294- raise ValueError
307+ raise ValueError ( " map too learge " )
295308 msgpack_pack_map(& self .pk, size)
296309 if self .autoreset:
297310 buf = PyBytes_FromStringAndSize(self .pk.buf, self .pk.length)
@@ -305,7 +318,11 @@ cdef class Packer:
305318 *pairs* should be a sequence of pairs.
306319 (`len(pairs)` and `for k, v in pairs:` should be supported.)
307320 """
308- msgpack_pack_map(& self .pk, len (pairs))
321+ self ._check_exports()
322+ size = len (pairs)
323+ if size > ITEM_LIMIT:
324+ raise ValueError (" map too large" )
325+ msgpack_pack_map(& self .pk, size)
309326 for k, v in pairs:
310327 self ._pack(k)
311328 self ._pack(v)
@@ -319,18 +336,23 @@ cdef class Packer:
319336
320337 This method is useful only when autoreset=False.
321338 """
339+ self ._check_exports()
322340 self .pk.length = 0
323341
324342 def bytes (self ):
325343 """ Return internal buffer contents as bytes object"""
326344 return PyBytes_FromStringAndSize(self .pk.buf, self .pk.length)
327345
328346 def getbuffer (self ):
329- """ Return view of internal buffer."""
347+ """ Return memoryview of internal buffer.
348+
349+ Note: Packer now supports buffer protocol. You can use memoryview(packer).
350+ """
330351 return memoryview(self )
331352
332353 def __getbuffer__ (self , Py_buffer *buffer , int flags ):
333354 PyBuffer_FillInfo(buffer , self , self .pk.buf, self .pk.length, 1 , flags)
355+ self .exports += 1
334356
335357 def __releasebuffer__ (self , Py_buffer *buffer ):
336- pass
358+ self .exports -= 1
0 commit comments