You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
409 lines
13 KiB
409 lines
13 KiB
|
|
/* 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;
|
|
}
|