diff options
author | INADA Naoki <songofacandy@gmail.com> | 2010-11-03 03:11:00 +0900 |
---|---|---|
committer | INADA Naoki <songofacandy@gmail.com> | 2010-11-03 03:11:00 +0900 |
commit | a09c85ff9c846384cf681fa955ce931010995055 (patch) | |
tree | eb84339700ef0d5892adc12d9a47dc4531c0e708 | |
parent | 3903979a84c4d450acc55faceb3f816ff7a32398 (diff) | |
download | msgpack-python-a09c85ff9c846384cf681fa955ce931010995055.tar.gz |
python: Support old buffer protocol when unpack. (experimental)
-rw-r--r-- | msgpack/_msgpack.pyx | 160 | ||||
-rw-r--r-- | test/test_buffer.py | 6 | ||||
-rw-r--r-- | test/test_sequnpack.py | 16 |
3 files changed, 67 insertions, 115 deletions
diff --git a/msgpack/_msgpack.pyx b/msgpack/_msgpack.pyx index e9d6c7b..5394055 100644 --- a/msgpack/_msgpack.pyx +++ b/msgpack/_msgpack.pyx @@ -1,37 +1,14 @@ # coding: utf-8 +from cpython cimport * cdef extern from "Python.h": ctypedef char* const_char_ptr "const char*" + ctypedef char* const_void_ptr "const void*" ctypedef struct PyObject + cdef int PyObject_AsReadBuffer(object o, const_void_ptr* buff, Py_ssize_t* buf_len) except -1 - cdef object PyBytes_FromStringAndSize(const_char_ptr b, Py_ssize_t len) - cdef PyObject* Py_True - cdef PyObject* Py_False - cdef object PyUnicode_AsUTF8String(object) - - cdef long long PyLong_AsLongLong(object o) - cdef unsigned long long PyLong_AsUnsignedLongLong(object o) - - cdef bint PyBool_Check(object o) - cdef bint PyDict_Check(object o) - cdef bint PySequence_Check(object o) - cdef bint PyLong_Check(object o) - cdef bint PyInt_Check(object o) - cdef bint PyFloat_Check(object o) - cdef bint PyBytes_Check(object o) - cdef bint PyUnicode_Check(object o) - cdef bint PyCallable_Check(object o) - cdef void Py_INCREF(object o) - cdef void Py_DECREF(object o) - -cdef extern from "stdlib.h": - void* malloc(size_t) - void* realloc(void*, size_t) - void free(void*) - -cdef extern from "string.h": - void* memcpy(char* dst, char* src, size_t size) - void* memmove(char* dst, char* src, size_t size) +from libc.stdlib cimport * +from libc.string cimport * cdef extern from "pack.h": struct msgpack_packer: @@ -104,10 +81,10 @@ cdef class Packer(object): ret = msgpack_pack_false(&self.pk) elif PyLong_Check(o): if o > 0: - ullval = PyLong_AsUnsignedLongLong(o) + ullval = o ret = msgpack_pack_unsigned_long_long(&self.pk, ullval) else: - llval = PyLong_AsLongLong(o) + llval = o ret = msgpack_pack_long_long(&self.pk, llval) elif PyInt_Check(o): longval = o @@ -160,7 +137,7 @@ cdef class Packer(object): def pack(object o, object stream, default=None): """pack an object `o` and write it to stream).""" - packer = Packer(default) + packer = Packer(default=default) stream.write(packer.pack(o)) def packb(object o, default=None): @@ -184,17 +161,21 @@ cdef extern from "unpack.h": PyObject* key int template_execute(template_context* ctx, const_char_ptr data, - size_t len, size_t* off) + size_t len, size_t* off) void template_init(template_context* ctx) object template_data(template_context* ctx) -def unpackb(bytes packed_bytes, object object_hook=None, object list_hook=None): +def unpackb(object packed, object object_hook=None, object list_hook=None): """Unpack packed_bytes to object. Returns an unpacked object.""" - cdef const_char_ptr p = packed_bytes cdef template_context ctx cdef size_t off = 0 cdef int ret + + cdef char* buf + cdef Py_ssize_t buf_len + PyObject_AsReadBuffer(packed, <const_void_ptr*>&buf, &buf_len) + template_init(&ctx) ctx.user.use_list = 0 ctx.user.object_hook = ctx.user.list_hook = NULL @@ -206,7 +187,7 @@ def unpackb(bytes packed_bytes, object object_hook=None, object list_hook=None): if not PyCallable_Check(list_hook): raise TypeError("list_hook must be a callable.") ctx.user.list_hook = <PyObject*>list_hook - ret = template_execute(&ctx, p, len(packed_bytes), &off) + ret = template_execute(&ctx, buf, buf_len, &off) if ret == 1: return template_data(&ctx) else: @@ -216,8 +197,8 @@ unpacks = unpackb def unpack(object stream, object object_hook=None, object list_hook=None): """unpack an object from stream.""" - packed = stream.read() - return unpackb(packed, object_hook=object_hook, list_hook=list_hook) + return unpackb(stream.read(), + object_hook=object_hook, list_hook=list_hook) cdef class UnpackIterator(object): cdef object unpacker @@ -232,21 +213,12 @@ cdef class UnpackIterator(object): return self cdef class Unpacker(object): - """Unpacker(file_like=None, read_size=1024*1024) + """Unpacker(read_size=1024*1024) Streaming unpacker. - file_like must have read(n) method. read_size is used like file_like.read(read_size) - If file_like is None, you can ``feed()`` bytes. ``feed()`` is - useful for unpacking from non-blocking stream. - - exsample 1: - unpacker = Unpacker(afile) - for o in unpacker: - do_something(o) - - example 2: + example: unpacker = Unpacker() while 1: buf = astream.read() @@ -254,13 +226,11 @@ cdef class Unpacker(object): for o in unpacker: do_something(o) """ - cdef template_context ctx cdef char* buf cdef size_t buf_size, buf_head, buf_tail cdef object file_like cdef int read_size - cdef object waiting_bytes cdef bint use_list cdef object object_hook @@ -268,8 +238,7 @@ cdef class Unpacker(object): self.buf = NULL def __dealloc__(self): - if self.buf: - free(self.buf); + free(self.buf); def __init__(self, file_like=None, int read_size=0, bint use_list=0, object object_hook=None, object list_hook=None): @@ -278,7 +247,6 @@ cdef class Unpacker(object): self.use_list = use_list self.file_like = file_like self.read_size = read_size - self.waiting_bytes = [] self.buf = <char*>malloc(read_size) self.buf_size = read_size self.buf_head = 0 @@ -295,65 +263,49 @@ cdef class Unpacker(object): raise TypeError("object_hook must be a callable.") self.ctx.user.list_hook = <PyObject*>list_hook - def feed(self, bytes next_bytes): - self.waiting_bytes.append(next_bytes) - - cdef append_buffer(self): - cdef char* buf = self.buf - cdef Py_ssize_t tail = self.buf_tail - cdef Py_ssize_t l - cdef bytes b - - for b in self.waiting_bytes: - l = len(b) - memcpy(buf + tail, <char*>(b), l) - tail += l - self.buf_tail = tail - del self.waiting_bytes[:] + def feed(self, object next_bytes): + cdef char* buf + cdef Py_ssize_t buf_len + PyObject_AsReadBuffer(next_bytes, <const_void_ptr*>&buf, &buf_len) + self.append_buffer(buf, buf_len) + + cdef append_buffer(self, void* _buf, Py_ssize_t _buf_len): + cdef: + char* buf = self.buf + size_t head = self.buf_head + size_t tail = self.buf_tail + size_t buf_size = self.buf_size + size_t new_size + + if tail + _buf_len > buf_size: + if ((tail - head) + _buf_len)*2 < buf_size: + # move to front. + memmove(buf, buf + head, tail - head) + tail -= head + head = 0 + else: + # expand buffer. + new_size = tail + _buf_len + if new_size < buf_size*2: + new_size = buf_size*2 + buf = <char*>realloc(buf, new_size) + buf_size = new_size + + memcpy(buf + tail, <char*>(_buf), _buf_len) + self.buf_head = head + self.buf_size = buf_size + self.buf_tail = tail + _buf_len - # prepare self.buf + # prepare self.buf from file_like cdef fill_buffer(self): - cdef Py_ssize_t add_size - if self.file_like is not None: next_bytes = self.file_like.read(self.read_size) if next_bytes: - self.waiting_bytes.append(next_bytes) + self.append_buffer(PyBytes_AsString(next_bytes), + PyBytes_Size(next_bytes)) else: self.file_like = None - if not self.waiting_bytes: - return - - add_size = 0 - for b in self.waiting_bytes: - add_size += len(b) - - cdef char* buf = self.buf - cdef size_t head = self.buf_head - cdef size_t tail = self.buf_tail - cdef size_t size = self.buf_size - - if self.buf_tail + add_size <= self.buf_size: - # do nothing. - pass - if self.buf_tail - self.buf_head + add_size < self.buf_size: - # move to front. - memmove(buf, buf + head, tail - head) - tail -= head - head = 0 - else: - # expand buffer - size = tail + add_size - buf = <char*>realloc(<void*>buf, size) - - self.buf = buf - self.buf_head = head - self.buf_tail = tail - self.buf_size = size - - self.append_buffer() - cpdef unpack(self): """unpack one object""" cdef int ret diff --git a/test/test_buffer.py b/test/test_buffer.py index 436b554..ce7a72d 100644 --- a/test/test_buffer.py +++ b/test/test_buffer.py @@ -7,10 +7,10 @@ from msgpack import packb, unpackb def test_unpack_buffer(): from array import array - buf = array('b') - buf.fromstring(packb(['foo', 'bar'])) + buf = array('c') + buf.fromstring(packb(('foo', 'bar'))) obj = unpackb(buf) - assert_equal(['foo', 'bar'], obj) + assert_equal(('foo', 'bar'), obj) if __name__ == '__main__': main() diff --git a/test/test_sequnpack.py b/test/test_sequnpack.py index 789ccd2..df6e308 100644 --- a/test/test_sequnpack.py +++ b/test/test_sequnpack.py @@ -1,22 +1,22 @@ #!/usr/bin/env python # coding: utf-8 -from __future__ import unicode_literals, print_function +from __future__ import unicode_literals from msgpack import Unpacker def test_foobar(): unpacker = Unpacker(read_size=3) unpacker.feed(b'foobar') - assert unpacker.unpack() == ord('f') - assert unpacker.unpack() == ord('o') - assert unpacker.unpack() == ord('o') - assert unpacker.unpack() == ord('b') - assert unpacker.unpack() == ord('a') - assert unpacker.unpack() == ord('r') + assert unpacker.unpack() == ord(b'f') + assert unpacker.unpack() == ord(b'o') + assert unpacker.unpack() == ord(b'o') + assert unpacker.unpack() == ord(b'b') + assert unpacker.unpack() == ord(b'a') + assert unpacker.unpack() == ord(b'r') try: o = unpacker.unpack() - print("Oops!", o) + print "Oops!", o assert 0 except StopIteration: assert 1 |