123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408 |
- /* 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;
- }
|