summaryrefslogtreecommitdiff
path: root/c/minibuffer.h
diff options
context:
space:
mode:
Diffstat (limited to 'c/minibuffer.h')
-rw-r--r--c/minibuffer.h408
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;
+}