/* Implementation of a C object with the 'buffer' or 'memoryview' * interface at C-level (as approriate for the version of Python we're * compiling for), but only a minimal but *consistent* part of the * 'buffer' interface at application level. */ typedef struct { PyObject_HEAD char *mb_data; Py_ssize_t mb_size; PyObject *mb_keepalive; PyObject *mb_weakreflist; /* weakref support */ } MiniBufferObj; static Py_ssize_t mb_length(MiniBufferObj *self) { return self->mb_size; } static PyObject *mb_item(MiniBufferObj *self, Py_ssize_t idx) { if (idx < 0 || idx >= self->mb_size ) { PyErr_SetString(PyExc_IndexError, "buffer index out of range"); return NULL; } return PyBytes_FromStringAndSize(self->mb_data + idx, 1); } static PyObject *mb_slice(MiniBufferObj *self, Py_ssize_t left, Py_ssize_t right) { Py_ssize_t size = self->mb_size; if (left < 0) left = 0; if (right > size) right = size; if (left > right) left = right; return PyBytes_FromStringAndSize(self->mb_data + left, right - left); } static int mb_ass_item(MiniBufferObj *self, Py_ssize_t idx, PyObject *other) { if (idx < 0 || idx >= self->mb_size) { PyErr_SetString(PyExc_IndexError, "buffer assignment index out of range"); return -1; } if (PyBytes_Check(other) && PyBytes_GET_SIZE(other) == 1) { self->mb_data[idx] = PyBytes_AS_STRING(other)[0]; return 0; } else { PyErr_Format(PyExc_TypeError, "must assign a "STR_OR_BYTES " of length 1, not %.200s", Py_TYPE(other)->tp_name); return -1; } } /* forward: from _cffi_backend.c */ static int _fetch_as_buffer(PyObject *x, Py_buffer *view, int writable_only); static int mb_ass_slice(MiniBufferObj *self, Py_ssize_t left, Py_ssize_t right, PyObject *other) { Py_ssize_t count; Py_ssize_t size = self->mb_size; Py_buffer src_view; if (_fetch_as_buffer(other, &src_view, 0) < 0) return -1; if (left < 0) left = 0; if (right > size) right = size; if (left > right) left = right; count = right - left; if (count != src_view.len) { PyBuffer_Release(&src_view); PyErr_SetString(PyExc_ValueError, "right operand length must match slice length"); return -1; } memcpy(self->mb_data + left, src_view.buf, count); PyBuffer_Release(&src_view); return 0; } #if PY_MAJOR_VERSION < 3 static Py_ssize_t mb_getdata(MiniBufferObj *self, Py_ssize_t idx, void **pp) { *pp = self->mb_data; return self->mb_size; } static Py_ssize_t mb_getsegcount(MiniBufferObj *self, Py_ssize_t *lenp) { if (lenp) *lenp = self->mb_size; return 1; } static PyObject *mb_str(MiniBufferObj *self) { /* Python 2: we want str(buffer) to behave like buffer[:], because that's what bytes(buffer) does on Python 3 and there is no way we can prevent this. */ return PyString_FromStringAndSize(self->mb_data, self->mb_size); } #endif static int mb_getbuf(MiniBufferObj *self, Py_buffer *view, int flags) { return PyBuffer_FillInfo(view, (PyObject *)self, self->mb_data, self->mb_size, /*readonly=*/0, flags); } static PySequenceMethods mb_as_sequence = { (lenfunc)mb_length, /*sq_length*/ (binaryfunc)0, /*sq_concat*/ (ssizeargfunc)0, /*sq_repeat*/ (ssizeargfunc)mb_item, /*sq_item*/ (ssizessizeargfunc)mb_slice, /*sq_slice*/ (ssizeobjargproc)mb_ass_item, /*sq_ass_item*/ (ssizessizeobjargproc)mb_ass_slice, /*sq_ass_slice*/ }; static PyBufferProcs mb_as_buffer = { #if PY_MAJOR_VERSION < 3 (readbufferproc)mb_getdata, (writebufferproc)mb_getdata, (segcountproc)mb_getsegcount, (charbufferproc)mb_getdata, #endif (getbufferproc)mb_getbuf, (releasebufferproc)0, }; static void mb_dealloc(MiniBufferObj *ob) { PyObject_GC_UnTrack(ob); if (ob->mb_weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *)ob); Py_XDECREF(ob->mb_keepalive); Py_TYPE(ob)->tp_free((PyObject *)ob); } static int mb_traverse(MiniBufferObj *ob, visitproc visit, void *arg) { Py_VISIT(ob->mb_keepalive); return 0; } static int mb_clear(MiniBufferObj *ob) { Py_CLEAR(ob->mb_keepalive); return 0; } static PyObject * mb_richcompare(PyObject *self, PyObject *other, int op) { Py_ssize_t self_size, other_size; Py_buffer self_bytes, other_bytes; PyObject *res; Py_ssize_t minsize; int cmp, rc; /* Bytes can be compared to anything that supports the (binary) buffer API. Except that a comparison with Unicode is always an error, even if the comparison is for equality. */ rc = PyObject_IsInstance(self, (PyObject*)&PyUnicode_Type); if (!rc) rc = PyObject_IsInstance(other, (PyObject*)&PyUnicode_Type); if (rc < 0) return NULL; if (rc) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } if (PyObject_GetBuffer(self, &self_bytes, PyBUF_SIMPLE) != 0) { PyErr_Clear(); Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } self_size = self_bytes.len; if (PyObject_GetBuffer(other, &other_bytes, PyBUF_SIMPLE) != 0) { PyErr_Clear(); PyBuffer_Release(&self_bytes); Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } other_size = other_bytes.len; if (self_size != other_size && (op == Py_EQ || op == Py_NE)) { /* Shortcut: if the lengths differ, the objects differ */ cmp = (op == Py_NE); } else { minsize = self_size; if (other_size < minsize) minsize = other_size; cmp = memcmp(self_bytes.buf, other_bytes.buf, minsize); /* In ISO C, memcmp() guarantees to use unsigned bytes! */ if (cmp == 0) { if (self_size < other_size) cmp = -1; else if (self_size > other_size) cmp = 1; } switch (op) { case Py_LT: cmp = cmp < 0; break; case Py_LE: cmp = cmp <= 0; break; case Py_EQ: cmp = cmp == 0; break; case Py_NE: cmp = cmp != 0; break; case Py_GT: cmp = cmp > 0; break; case Py_GE: cmp = cmp >= 0; break; } } res = cmp ? Py_True : Py_False; PyBuffer_Release(&self_bytes); PyBuffer_Release(&other_bytes); Py_INCREF(res); return res; } #if PY_MAJOR_VERSION >= 3 /* pfffffffffffff pages of copy-paste from listobject.c */ /* pfffffffffffff#2: the PySlice_GetIndicesEx() *macro* should not be called, because C extension modules compiled with it differ on ABI between 3.6.0, 3.6.1 and 3.6.2. */ #if PY_VERSION_HEX < 0x03070000 && defined(PySlice_GetIndicesEx) && !defined(PYPY_VERSION) #undef PySlice_GetIndicesEx #endif static PyObject *mb_subscript(MiniBufferObj *self, PyObject *item) { if (PyIndex_Check(item)) { Py_ssize_t i; i = PyNumber_AsSsize_t(item, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) return NULL; if (i < 0) i += self->mb_size; return mb_item(self, i); } else if (PySlice_Check(item)) { Py_ssize_t start, stop, step, slicelength; if (PySlice_GetIndicesEx(item, self->mb_size, &start, &stop, &step, &slicelength) < 0) return NULL; if (step == 1) return mb_slice(self, start, stop); else { PyErr_SetString(PyExc_TypeError, "buffer doesn't support slicing with step != 1"); return NULL; } } else { PyErr_Format(PyExc_TypeError, "buffer indices must be integers, not %.200s", item->ob_type->tp_name); return NULL; } } static int mb_ass_subscript(MiniBufferObj* self, PyObject* item, PyObject* value) { if (PyIndex_Check(item)) { Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) return -1; if (i < 0) i += self->mb_size; return mb_ass_item(self, i, value); } else if (PySlice_Check(item)) { Py_ssize_t start, stop, step, slicelength; if (PySlice_GetIndicesEx(item, self->mb_size, &start, &stop, &step, &slicelength) < 0) { return -1; } if (step == 1) return mb_ass_slice(self, start, stop, value); else { PyErr_SetString(PyExc_TypeError, "buffer doesn't support slicing with step != 1"); return -1; } } else { PyErr_Format(PyExc_TypeError, "buffer indices must be integers, not %.200s", item->ob_type->tp_name); return -1; } } static PyMappingMethods mb_as_mapping = { (lenfunc)mb_length, /*mp_length*/ (binaryfunc)mb_subscript, /*mp_subscript*/ (objobjargproc)mb_ass_subscript, /*mp_ass_subscript*/ }; #endif #if PY_MAJOR_VERSION >= 3 # define MINIBUF_TPFLAGS 0 #else # define MINIBUF_TPFLAGS (Py_TPFLAGS_HAVE_GETCHARBUFFER | Py_TPFLAGS_HAVE_NEWBUFFER) #endif PyDoc_STRVAR(ffi_buffer_doc, "ffi.buffer(cdata[, byte_size]):\n" "Return a read-write buffer object that references the raw C data\n" "pointed to by the given 'cdata'. The 'cdata' must be a pointer or an\n" "array. Can be passed to functions expecting a buffer, or directly\n" "manipulated with:\n" "\n" " buf[:] get a copy of it in a regular string, or\n" " buf[idx] as a single character\n" " buf[:] = ...\n" " buf[idx] = ... change the content"); static PyObject * /* forward, implemented in _cffi_backend.c */ b_buffer_new(PyTypeObject *type, PyObject *args, PyObject *kwds); static PyTypeObject MiniBuffer_Type = { PyVarObject_HEAD_INIT(NULL, 0) "_cffi_backend.buffer", sizeof(MiniBufferObj), 0, (destructor)mb_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ &mb_as_sequence, /* tp_as_sequence */ #if PY_MAJOR_VERSION < 3 0, /* tp_as_mapping */ #else &mb_as_mapping, /* tp_as_mapping */ #endif 0, /* tp_hash */ 0, /* tp_call */ #if PY_MAJOR_VERSION < 3 (reprfunc)mb_str, /* tp_str */ #else 0, /* tp_str */ #endif PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ &mb_as_buffer, /* tp_as_buffer */ (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | MINIBUF_TPFLAGS), /* tp_flags */ ffi_buffer_doc, /* tp_doc */ (traverseproc)mb_traverse, /* tp_traverse */ (inquiry)mb_clear, /* tp_clear */ (richcmpfunc)mb_richcompare, /* tp_richcompare */ offsetof(MiniBufferObj, mb_weakreflist), /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ b_buffer_new, /* tp_new */ 0, /* tp_free */ }; static PyObject *minibuffer_new(char *data, Py_ssize_t size, PyObject *keepalive) { MiniBufferObj *ob = PyObject_GC_New(MiniBufferObj, &MiniBuffer_Type); if (ob != NULL) { ob->mb_data = data; ob->mb_size = size; ob->mb_keepalive = keepalive; Py_INCREF(keepalive); ob->mb_weakreflist = NULL; PyObject_GC_Track(ob); } return (PyObject *)ob; }