From 3781aef8f8f992f78b31c7029004fa7b1ada7304 Mon Sep 17 00:00:00 2001 From: "Travis E. Oliphant" Date: Tue, 18 Mar 2008 04:44:57 +0000 Subject: [PATCH] Finish backporting new buffer API to Python 2.6. Left to do: memoryview object and structmodule. But, these need to be finished in Python 3.0 first. No objects support the new buffer API in Python 2.6 as of yet, and except for the memoryview object, I don't think they will. --- Include/Python.h | 1 + Include/abstract.h | 7 +- Objects/abstract.c | 369 +++++++++++++++++++++++++++++++++++++++++++ Objects/exceptions.c | 5 + Python/bltinmodule.c | 1 + 5 files changed, 380 insertions(+), 3 deletions(-) diff --git a/Include/Python.h b/Include/Python.h index 56d20abe852..763b14484eb 100644 --- a/Include/Python.h +++ b/Include/Python.h @@ -90,6 +90,7 @@ #endif #include "rangeobject.h" #include "stringobject.h" +/* #include "memoryobject.h" */ #include "bufferobject.h" #include "tupleobject.h" #include "listobject.h" diff --git a/Include/abstract.h b/Include/abstract.h index 159e67d7f21..3e5cf129f8d 100644 --- a/Include/abstract.h +++ b/Include/abstract.h @@ -532,9 +532,10 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ /* new buffer API */ #define PyObject_CheckBuffer(obj) \ - (((obj)->ob_type->tp_as_buffer != NULL) && \ - ((obj)->ob_type->tp_as_buffer->bf_getbuffer != NULL)) - + (((obj)->ob_type->tp_as_buffer != NULL) && \ + (PyType_HasFeature((obj)->ob_type, Py_TPFLAGS_HAVE_NEWBUFFER)) && \ + ((obj)->ob_type->tp_as_buffer->bf_getbuffer != NULL)) + /* Return 1 if the getbuffer function is available, otherwise return 0 */ diff --git a/Objects/abstract.c b/Objects/abstract.c index 071cbdcca27..0ea26d6e4b7 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -348,6 +348,375 @@ int PyObject_AsWriteBuffer(PyObject *obj, return 0; } +/* Buffer C-API for Python 3.0 */ + +int +PyObject_GetBuffer(PyObject *obj, Py_buffer *view, int flags) +{ + if (!PyObject_CheckBuffer(obj)) { + PyErr_Format(PyExc_TypeError, + "'%100s' does not have the buffer interface", + Py_TYPE(obj)->tp_name); + return -1; + } + return (*(obj->ob_type->tp_as_buffer->bf_getbuffer))(obj, view, flags); +} + +void +PyObject_ReleaseBuffer(PyObject *obj, Py_buffer *view) +{ + if (obj->ob_type->tp_as_buffer != NULL && + obj->ob_type->tp_as_buffer->bf_releasebuffer != NULL) { + (*(obj->ob_type->tp_as_buffer->bf_releasebuffer))(obj, view); + } +} + + +static int +_IsFortranContiguous(Py_buffer *view) +{ + Py_ssize_t sd, dim; + int i; + + if (view->ndim == 0) return 1; + if (view->strides == NULL) return (view->ndim == 1); + + sd = view->itemsize; + if (view->ndim == 1) return (view->shape[0] == 1 || + sd == view->strides[0]); + for (i=0; indim; i++) { + dim = view->shape[i]; + if (dim == 0) return 1; + if (view->strides[i] != sd) return 0; + sd *= dim; + } + return 1; +} + +static int +_IsCContiguous(Py_buffer *view) +{ + Py_ssize_t sd, dim; + int i; + + if (view->ndim == 0) return 1; + if (view->strides == NULL) return 1; + + sd = view->itemsize; + if (view->ndim == 1) return (view->shape[0] == 1 || + sd == view->strides[0]); + for (i=view->ndim-1; i>=0; i--) { + dim = view->shape[i]; + if (dim == 0) return 1; + if (view->strides[i] != sd) return 0; + sd *= dim; + } + return 1; +} + +int +PyBuffer_IsContiguous(Py_buffer *view, char fort) +{ + + if (view->suboffsets != NULL) return 0; + + if (fort == 'C') + return _IsCContiguous(view); + else if (fort == 'F') + return _IsFortranContiguous(view); + else if (fort == 'A') + return (_IsCContiguous(view) || _IsFortranContiguous(view)); + return 0; +} + + +void* +PyBuffer_GetPointer(Py_buffer *view, Py_ssize_t *indices) +{ + char* pointer; + int i; + pointer = (char *)view->buf; + for (i = 0; i < view->ndim; i++) { + pointer += view->strides[i]*indices[i]; + if ((view->suboffsets != NULL) && (view->suboffsets[i] >= 0)) { + pointer = *((char**)pointer) + view->suboffsets[i]; + } + } + return (void*)pointer; +} + + +void +_add_one_to_index_F(int nd, Py_ssize_t *index, Py_ssize_t *shape) +{ + int k; + + for (k=0; k=0; k--) { + if (index[k] < shape[k]-1) { + index[k]++; + break; + } + else { + index[k] = 0; + } + } +} + + /* view is not checked for consistency in either of these. It is + assumed that the size of the buffer is view->len in + view->len / view->itemsize elements. + */ + +int +PyBuffer_ToContiguous(void *buf, Py_buffer *view, Py_ssize_t len, char fort) +{ + int k; + void (*addone)(int, Py_ssize_t *, Py_ssize_t *); + Py_ssize_t *indices, elements; + char *dest, *ptr; + + if (len > view->len) { + len = view->len; + } + + if (PyBuffer_IsContiguous(view, fort)) { + /* simplest copy is all that is needed */ + memcpy(buf, view->buf, len); + return 0; + } + + /* Otherwise a more elaborate scheme is needed */ + + /* XXX(nnorwitz): need to check for overflow! */ + indices = (Py_ssize_t *)PyMem_Malloc(sizeof(Py_ssize_t)*(view->ndim)); + if (indices == NULL) { + PyErr_NoMemory(); + return -1; + } + for (k=0; kndim;k++) { + indices[k] = 0; + } + + if (fort == 'F') { + addone = _add_one_to_index_F; + } + else { + addone = _add_one_to_index_C; + } + dest = buf; + /* XXX : This is not going to be the fastest code in the world + several optimizations are possible. + */ + elements = len / view->itemsize; + while (elements--) { + addone(view->ndim, indices, view->shape); + ptr = PyBuffer_GetPointer(view, indices); + memcpy(dest, ptr, view->itemsize); + dest += view->itemsize; + } + PyMem_Free(indices); + return 0; +} + +int +PyBuffer_FromContiguous(Py_buffer *view, void *buf, Py_ssize_t len, char fort) +{ + int k; + void (*addone)(int, Py_ssize_t *, Py_ssize_t *); + Py_ssize_t *indices, elements; + char *src, *ptr; + + if (len > view->len) { + len = view->len; + } + + if (PyBuffer_IsContiguous(view, fort)) { + /* simplest copy is all that is needed */ + memcpy(view->buf, buf, len); + return 0; + } + + /* Otherwise a more elaborate scheme is needed */ + + /* XXX(nnorwitz): need to check for overflow! */ + indices = (Py_ssize_t *)PyMem_Malloc(sizeof(Py_ssize_t)*(view->ndim)); + if (indices == NULL) { + PyErr_NoMemory(); + return -1; + } + for (k=0; kndim;k++) { + indices[k] = 0; + } + + if (fort == 'F') { + addone = _add_one_to_index_F; + } + else { + addone = _add_one_to_index_C; + } + src = buf; + /* XXX : This is not going to be the fastest code in the world + several optimizations are possible. + */ + elements = len / view->itemsize; + while (elements--) { + addone(view->ndim, indices, view->shape); + ptr = PyBuffer_GetPointer(view, indices); + memcpy(ptr, src, view->itemsize); + src += view->itemsize; + } + + PyMem_Free(indices); + return 0; +} + +int PyObject_CopyData(PyObject *dest, PyObject *src) +{ + Py_buffer view_dest, view_src; + int k; + Py_ssize_t *indices, elements; + char *dptr, *sptr; + + if (!PyObject_CheckBuffer(dest) || + !PyObject_CheckBuffer(src)) { + PyErr_SetString(PyExc_TypeError, + "both destination and source must have the "\ + "buffer interface"); + return -1; + } + + if (PyObject_GetBuffer(dest, &view_dest, PyBUF_FULL) != 0) return -1; + if (PyObject_GetBuffer(src, &view_src, PyBUF_FULL_RO) != 0) { + PyObject_ReleaseBuffer(dest, &view_dest); + return -1; + } + + if (view_dest.len < view_src.len) { + PyErr_SetString(PyExc_BufferError, + "destination is too small to receive data from source"); + PyObject_ReleaseBuffer(dest, &view_dest); + PyObject_ReleaseBuffer(src, &view_src); + return -1; + } + + if ((PyBuffer_IsContiguous(&view_dest, 'C') && + PyBuffer_IsContiguous(&view_src, 'C')) || + (PyBuffer_IsContiguous(&view_dest, 'F') && + PyBuffer_IsContiguous(&view_src, 'F'))) { + /* simplest copy is all that is needed */ + memcpy(view_dest.buf, view_src.buf, view_src.len); + PyObject_ReleaseBuffer(dest, &view_dest); + PyObject_ReleaseBuffer(src, &view_src); + return 0; + } + + /* Otherwise a more elaborate copy scheme is needed */ + + /* XXX(nnorwitz): need to check for overflow! */ + indices = (Py_ssize_t *)PyMem_Malloc(sizeof(Py_ssize_t)*view_src.ndim); + if (indices == NULL) { + PyErr_NoMemory(); + PyObject_ReleaseBuffer(dest, &view_dest); + PyObject_ReleaseBuffer(src, &view_src); + return -1; + } + for (k=0; k=0; k--) { + strides[k] = sd; + sd *= shape[k]; + } + } + return; +} + +int +PyBuffer_FillInfo(Py_buffer *view, void *buf, Py_ssize_t len, + int readonly, int flags) +{ + if (view == NULL) return 0; + if (((flags & PyBUF_LOCK) == PyBUF_LOCK) && + readonly != 0) { + PyErr_SetString(PyExc_BufferError, + "Cannot lock this object."); + return -1; + } + if (((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) && + (readonly == 1)) { + PyErr_SetString(PyExc_BufferError, + "Object is not writable."); + return -1; + } + + view->buf = buf; + view->len = len; + view->readonly = readonly; + view->itemsize = 1; + view->format = NULL; + if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) + view->format = "B"; + view->ndim = 1; + view->shape = NULL; + if ((flags & PyBUF_ND) == PyBUF_ND) + view->shape = &(view->len); + view->strides = NULL; + if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) + view->strides = &(view->itemsize); + view->suboffsets = NULL; + view->internal = NULL; + return 0; +} + PyObject * PyObject_Format(PyObject* obj, PyObject *format_spec) { diff --git a/Objects/exceptions.c b/Objects/exceptions.c index aa9f5169216..7fecb35cbf4 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -1850,6 +1850,11 @@ SimpleExtendsException(PyExc_StandardError, ReferenceError, */ SimpleExtendsException(PyExc_StandardError, MemoryError, "Out of memory."); +/* + * BufferError extends StandardError + */ +SimpleExtendsException(PyExc_StandardError, BufferError, "Buffer error."); + /* Warning category docstrings */ diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index fe5cb42cca6..8491ed410a5 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -2474,6 +2474,7 @@ _PyBuiltin_Init(void) SETBUILTIN("True", Py_True); SETBUILTIN("basestring", &PyBaseString_Type); SETBUILTIN("bool", &PyBool_Type); + /* SETBUILTIN("memoryview", &PyMemoryView_Type); */ SETBUILTIN("bytes", &PyString_Type); SETBUILTIN("buffer", &PyBuffer_Type); SETBUILTIN("classmethod", &PyClassMethod_Type);