From 804095ecb351651ef65ae9aea8e1ce6a23368fb8 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Sat, 20 Oct 2012 16:13:12 -0500 Subject: [PATCH] py3k: Use new buffer protocol Other ports have taken advantage of the fact that Python 3 has wrappers for the old buffer protocol, but there's a significant disadvantage: you can't let the buffered object know when you're done with it. Since Python 2.6 supports the new protocol, we just go ahead and move to it. --- _imaging.c | 64 ++++++++++++++++++++---------------------------------- map.c | 20 ++++++++++------- path.c | 26 ++++++++++++---------- 3 files changed, 50 insertions(+), 60 deletions(-) diff --git a/_imaging.c b/_imaging.c index dfe0cf10d..f599e124e 100644 --- a/_imaging.c +++ b/_imaging.c @@ -103,19 +103,6 @@ #define L16(p, i) ((((int)p[(i)+1]) << 8) + p[(i)]) #define S16(v) ((v) < 32768 ? (v) : ((v) - 65536)) -#if PY_VERSION_HEX < 0x01060000 -#define PyObject_New PyObject_NEW -#define PyObject_Del PyMem_DEL -#endif - -#if PY_VERSION_HEX < 0x02050000 -#define Py_ssize_t int -#define ssizeargfunc intargfunc -#define ssizessizeargfunc intintargfunc -#define ssizeobjargproc intobjargproc -#define ssizessizeobjargproc intintobjargproc -#endif - /* -------------------------------------------------------------------- */ /* OBJECT ADMINISTRATION */ /* -------------------------------------------------------------------- */ @@ -243,43 +230,38 @@ void ImagingSectionLeave(ImagingSectionCookie* cookie) /* -------------------------------------------------------------------- */ /* Python compatibility API */ -#if PY_VERSION_HEX < 0x02020000 - -int PyImaging_CheckBuffer(PyObject *buffer) -{ - PyBufferProcs *procs = buffer->ob_type->tp_as_buffer; - if (procs && procs->bf_getreadbuffer && procs->bf_getsegcount && - procs->bf_getsegcount(buffer, NULL) == 1) - return 1; - return 0; -} - -int PyImaging_ReadBuffer(PyObject* buffer, const void** ptr) -{ - PyBufferProcs *procs = buffer->ob_type->tp_as_buffer; - return procs->bf_getreadbuffer(buffer, 0, ptr); -} - -#else - int PyImaging_CheckBuffer(PyObject* buffer) { - return PyObject_CheckReadBuffer(buffer); +#if PY_VERSION_HEX >= 0x03000000 + return PyObject_CheckBuffer(buffer); +#else + return PyObject_CheckBuffer(buffer) || PyObject_CheckReadBuffer(buffer); +#endif } -int PyImaging_ReadBuffer(PyObject* buffer, const void** ptr) +int PyImaging_GetBuffer(PyObject* buffer, Py_buffer *view) { /* must call check_buffer first! */ -#if PY_VERSION_HEX < 0x02050000 - int n = 0; +#if PY_VERSION_HEX >= 0x03000000 + return PyObject_GetBuffer(buffer, view, PyBUF_SIMPLE); #else - Py_ssize_t n = 0; -#endif - PyObject_AsReadBuffer(buffer, ptr, &n); - return (int) n; -} + /* Use new buffer protocol if available + (mmap doesn't support this in 2.7, go figure) */ + if (PyObject_CheckBuffer(buffer)) { + return PyObject_GetBuffer(buffer, view, PyBUF_SIMPLE); + } + /* Pretend we support the new protocol; PyBuffer_Release happily ignores + calling bf_releasebuffer on objects that don't support it */ + *view = (Py_buffer) {0}; + view->readonly = 1; + + Py_INCREF(buffer); + view->obj = buffer; + + return PyObject_AsReadBuffer(buffer, (void *) &view->buf, &view->len); #endif +} /* -------------------------------------------------------------------- */ /* EXCEPTION REROUTING */ diff --git a/map.c b/map.c index 493dae2f7..1041d6121 100644 --- a/map.c +++ b/map.c @@ -41,7 +41,7 @@ /* compatibility wrappers (defined in _imaging.c) */ extern int PyImaging_CheckBuffer(PyObject* buffer); -extern int PyImaging_ReadBuffer(PyObject* buffer, const void** ptr); +extern int PyImaging_GetBuffer(PyObject* buffer, Py_buffer *view); /* -------------------------------------------------------------------- */ /* Standard mapper */ @@ -311,6 +311,7 @@ PyImaging_Mapper(PyObject* self, PyObject* args) typedef struct ImagingBufferInstance { struct ImagingMemoryInstance im; PyObject* target; + Py_buffer view; } ImagingBufferInstance; static void @@ -318,6 +319,7 @@ mapping_destroy_buffer(Imaging im) { ImagingBufferInstance* buffer = (ImagingBufferInstance*) im; + PyBuffer_Release(&buffer->view); Py_XDECREF(buffer->target); } @@ -326,10 +328,9 @@ PyImaging_MapBuffer(PyObject* self, PyObject* args) { int y, size; Imaging im; - char* ptr; - int bytes; PyObject* target; + Py_buffer view; char* mode; char* codec; PyObject* bbox; @@ -359,12 +360,14 @@ PyImaging_MapBuffer(PyObject* self, PyObject* args) size = ysize * stride; /* check buffer size */ - bytes = PyImaging_ReadBuffer(target, (const void**) &ptr); - if (bytes < 0) { + if (PyImaging_GetBuffer(target, &view) < 0) + return NULL; + + if (view.len < 0) { PyErr_SetString(PyExc_ValueError, "buffer has negative size"); return NULL; } - if (offset + size > bytes) { + if (offset + size > view.len) { PyErr_SetString(PyExc_ValueError, "buffer is not large enough"); return NULL; } @@ -378,15 +381,16 @@ PyImaging_MapBuffer(PyObject* self, PyObject* args) /* setup file pointers */ if (ystep > 0) for (y = 0; y < ysize; y++) - im->image[y] = ptr + offset + y * stride; + im->image[y] = view.buf + offset + y * stride; else for (y = 0; y < ysize; y++) - im->image[ysize-y-1] = ptr + offset + y * stride; + im->image[ysize-y-1] = view.buf + offset + y * stride; im->destroy = mapping_destroy_buffer; Py_INCREF(target); ((ImagingBufferInstance*) im)->target = target; + ((ImagingBufferInstance*) im)->view = view; if (!ImagingNewEpilogue(im)) return NULL; diff --git a/path.c b/path.c index e9cb74a76..b29f9b4d9 100644 --- a/path.c +++ b/path.c @@ -46,7 +46,7 @@ /* compatibility wrappers (defined in _imaging.c) */ extern int PyImaging_CheckBuffer(PyObject* buffer); -extern int PyImaging_ReadBuffer(PyObject* buffer, const void** ptr); +extern int PyImaging_GetBuffer(PyObject* buffer, Py_buffer *view); /* -------------------------------------------------------------------- */ /* Class */ @@ -134,16 +134,20 @@ PyPath_Flatten(PyObject* data, double **pxy) if (PyImaging_CheckBuffer(data)) { /* Assume the buffer contains floats */ - float* ptr; - int n = PyImaging_ReadBuffer(data, (const void**) &ptr); - n /= 2 * sizeof(float); - xy = alloc_array(n); - if (!xy) - return -1; - for (i = 0; i < n+n; i++) - xy[i] = ptr[i]; - *pxy = xy; - return n; + Py_buffer buffer; + if (PyImaging_GetBuffer(data, &buffer) == 0) { + int n = buffer.len / (2 * sizeof(float)); + float *ptr = (float*) buffer.buf; + xy = alloc_array(n); + if (!xy) + return -1; + for (i = 0; i < n+n; i++) + xy[i] = ptr[i]; + *pxy = xy; + PyBuffer_Release(&buffer); + return n; + } + PyErr_Clear(); } if (!PySequence_Check(data)) {