diff options
Diffstat (limited to 'c/minibuffer.h')
-rw-r--r-- | c/minibuffer.h | 408 |
1 files changed, 408 insertions, 0 deletions
diff --git a/c/minibuffer.h b/c/minibuffer.h new file mode 100644 index 0000000..f3f5ca1 --- /dev/null +++ b/c/minibuffer.h @@ -0,0 +1,408 @@ + +/* 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; +} |