• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 /* Implementation of a C object with the 'buffer' or 'memoryview'
3  * interface at C-level (as approriate for the version of Python we're
4  * compiling for), but only a minimal but *consistent* part of the
5  * 'buffer' interface at application level.
6  */
7 
8 typedef struct {
9     PyObject_HEAD
10     char      *mb_data;
11     Py_ssize_t mb_size;
12     PyObject  *mb_keepalive;
13     PyObject  *mb_weakreflist;    /* weakref support */
14 } MiniBufferObj;
15 
mb_length(MiniBufferObj * self)16 static Py_ssize_t mb_length(MiniBufferObj *self)
17 {
18     return self->mb_size;
19 }
20 
mb_item(MiniBufferObj * self,Py_ssize_t idx)21 static PyObject *mb_item(MiniBufferObj *self, Py_ssize_t idx)
22 {
23     if (idx < 0 || idx >= self->mb_size ) {
24         PyErr_SetString(PyExc_IndexError, "buffer index out of range");
25         return NULL;
26     }
27     return PyBytes_FromStringAndSize(self->mb_data + idx, 1);
28 }
29 
mb_slice(MiniBufferObj * self,Py_ssize_t left,Py_ssize_t right)30 static PyObject *mb_slice(MiniBufferObj *self,
31                           Py_ssize_t left, Py_ssize_t right)
32 {
33     Py_ssize_t size = self->mb_size;
34     if (left < 0)     left = 0;
35     if (right > size) right = size;
36     if (left > right) left = right;
37     return PyBytes_FromStringAndSize(self->mb_data + left, right - left);
38 }
39 
mb_ass_item(MiniBufferObj * self,Py_ssize_t idx,PyObject * other)40 static int mb_ass_item(MiniBufferObj *self, Py_ssize_t idx, PyObject *other)
41 {
42     if (idx < 0 || idx >= self->mb_size) {
43         PyErr_SetString(PyExc_IndexError,
44                         "buffer assignment index out of range");
45         return -1;
46     }
47     if (PyBytes_Check(other) && PyBytes_GET_SIZE(other) == 1) {
48         self->mb_data[idx] = PyBytes_AS_STRING(other)[0];
49         return 0;
50     }
51     else {
52         PyErr_Format(PyExc_TypeError,
53                      "must assign a "STR_OR_BYTES
54                      " of length 1, not %.200s", Py_TYPE(other)->tp_name);
55         return -1;
56     }
57 }
58 
59 /* forward: from _cffi_backend.c */
60 static int _fetch_as_buffer(PyObject *x, Py_buffer *view, int writable_only);
61 
mb_ass_slice(MiniBufferObj * self,Py_ssize_t left,Py_ssize_t right,PyObject * other)62 static int mb_ass_slice(MiniBufferObj *self,
63                         Py_ssize_t left, Py_ssize_t right, PyObject *other)
64 {
65     Py_ssize_t count;
66     Py_ssize_t size = self->mb_size;
67     Py_buffer src_view;
68 
69     if (_fetch_as_buffer(other, &src_view, 0) < 0)
70         return -1;
71 
72     if (left < 0)     left = 0;
73     if (right > size) right = size;
74     if (left > right) left = right;
75 
76     count = right - left;
77     if (count != src_view.len) {
78         PyBuffer_Release(&src_view);
79         PyErr_SetString(PyExc_ValueError,
80                         "right operand length must match slice length");
81         return -1;
82     }
83     memcpy(self->mb_data + left, src_view.buf, count);
84     PyBuffer_Release(&src_view);
85     return 0;
86 }
87 
88 #if PY_MAJOR_VERSION < 3
mb_getdata(MiniBufferObj * self,Py_ssize_t idx,void ** pp)89 static Py_ssize_t mb_getdata(MiniBufferObj *self, Py_ssize_t idx, void **pp)
90 {
91     *pp = self->mb_data;
92     return self->mb_size;
93 }
94 
mb_getsegcount(MiniBufferObj * self,Py_ssize_t * lenp)95 static Py_ssize_t mb_getsegcount(MiniBufferObj *self, Py_ssize_t *lenp)
96 {
97     if (lenp)
98         *lenp = self->mb_size;
99     return 1;
100 }
101 
mb_str(MiniBufferObj * self)102 static PyObject *mb_str(MiniBufferObj *self)
103 {
104     /* Python 2: we want str(buffer) to behave like buffer[:], because
105        that's what bytes(buffer) does on Python 3 and there is no way
106        we can prevent this. */
107     return PyString_FromStringAndSize(self->mb_data, self->mb_size);
108 }
109 #endif
110 
mb_getbuf(MiniBufferObj * self,Py_buffer * view,int flags)111 static int mb_getbuf(MiniBufferObj *self, Py_buffer *view, int flags)
112 {
113     return PyBuffer_FillInfo(view, (PyObject *)self,
114                              self->mb_data, self->mb_size,
115                              /*readonly=*/0, flags);
116 }
117 
118 static PySequenceMethods mb_as_sequence = {
119     (lenfunc)mb_length, /*sq_length*/
120     (binaryfunc)0, /*sq_concat*/
121     (ssizeargfunc)0, /*sq_repeat*/
122     (ssizeargfunc)mb_item, /*sq_item*/
123     (ssizessizeargfunc)mb_slice, /*sq_slice*/
124     (ssizeobjargproc)mb_ass_item, /*sq_ass_item*/
125     (ssizessizeobjargproc)mb_ass_slice, /*sq_ass_slice*/
126 };
127 
128 static PyBufferProcs mb_as_buffer = {
129 #if PY_MAJOR_VERSION < 3
130     (readbufferproc)mb_getdata,
131     (writebufferproc)mb_getdata,
132     (segcountproc)mb_getsegcount,
133     (charbufferproc)mb_getdata,
134 #endif
135     (getbufferproc)mb_getbuf,
136     (releasebufferproc)0,
137 };
138 
139 static void
mb_dealloc(MiniBufferObj * ob)140 mb_dealloc(MiniBufferObj *ob)
141 {
142     PyObject_GC_UnTrack(ob);
143     if (ob->mb_weakreflist != NULL)
144         PyObject_ClearWeakRefs((PyObject *)ob);
145     Py_XDECREF(ob->mb_keepalive);
146     Py_TYPE(ob)->tp_free((PyObject *)ob);
147 }
148 
149 static int
mb_traverse(MiniBufferObj * ob,visitproc visit,void * arg)150 mb_traverse(MiniBufferObj *ob, visitproc visit, void *arg)
151 {
152     Py_VISIT(ob->mb_keepalive);
153     return 0;
154 }
155 
156 static int
mb_clear(MiniBufferObj * ob)157 mb_clear(MiniBufferObj *ob)
158 {
159     Py_CLEAR(ob->mb_keepalive);
160     return 0;
161 }
162 
163 static PyObject *
mb_richcompare(PyObject * self,PyObject * other,int op)164 mb_richcompare(PyObject *self, PyObject *other, int op)
165 {
166     Py_ssize_t self_size, other_size;
167     Py_buffer self_bytes, other_bytes;
168     PyObject *res;
169     Py_ssize_t minsize;
170     int cmp, rc;
171 
172     /* Bytes can be compared to anything that supports the (binary)
173        buffer API.  Except that a comparison with Unicode is always an
174        error, even if the comparison is for equality. */
175     rc = PyObject_IsInstance(self, (PyObject*)&PyUnicode_Type);
176     if (!rc)
177         rc = PyObject_IsInstance(other, (PyObject*)&PyUnicode_Type);
178     if (rc < 0)
179         return NULL;
180     if (rc) {
181         Py_INCREF(Py_NotImplemented);
182         return Py_NotImplemented;
183     }
184 
185     if (PyObject_GetBuffer(self, &self_bytes, PyBUF_SIMPLE) != 0) {
186         PyErr_Clear();
187         Py_INCREF(Py_NotImplemented);
188         return Py_NotImplemented;
189 
190     }
191     self_size = self_bytes.len;
192 
193     if (PyObject_GetBuffer(other, &other_bytes, PyBUF_SIMPLE) != 0) {
194         PyErr_Clear();
195         PyBuffer_Release(&self_bytes);
196         Py_INCREF(Py_NotImplemented);
197         return Py_NotImplemented;
198 
199     }
200     other_size = other_bytes.len;
201 
202     if (self_size != other_size && (op == Py_EQ || op == Py_NE)) {
203         /* Shortcut: if the lengths differ, the objects differ */
204         cmp = (op == Py_NE);
205     }
206     else {
207         minsize = self_size;
208         if (other_size < minsize)
209             minsize = other_size;
210 
211         cmp = memcmp(self_bytes.buf, other_bytes.buf, minsize);
212         /* In ISO C, memcmp() guarantees to use unsigned bytes! */
213 
214         if (cmp == 0) {
215             if (self_size < other_size)
216                 cmp = -1;
217             else if (self_size > other_size)
218                 cmp = 1;
219         }
220 
221         switch (op) {
222         case Py_LT: cmp = cmp <  0; break;
223         case Py_LE: cmp = cmp <= 0; break;
224         case Py_EQ: cmp = cmp == 0; break;
225         case Py_NE: cmp = cmp != 0; break;
226         case Py_GT: cmp = cmp >  0; break;
227         case Py_GE: cmp = cmp >= 0; break;
228         }
229     }
230 
231     res = cmp ? Py_True : Py_False;
232     PyBuffer_Release(&self_bytes);
233     PyBuffer_Release(&other_bytes);
234     Py_INCREF(res);
235     return res;
236 }
237 
238 #if PY_MAJOR_VERSION >= 3
239 /* pfffffffffffff pages of copy-paste from listobject.c */
240 
241 /* pfffffffffffff#2: the PySlice_GetIndicesEx() *macro* should not
242    be called, because C extension modules compiled with it differ
243    on ABI between 3.6.0, 3.6.1 and 3.6.2. */
244 #if PY_VERSION_HEX < 0x03070000 && defined(PySlice_GetIndicesEx) && !defined(PYPY_VERSION)
245 #undef PySlice_GetIndicesEx
246 #endif
247 
mb_subscript(MiniBufferObj * self,PyObject * item)248 static PyObject *mb_subscript(MiniBufferObj *self, PyObject *item)
249 {
250     if (PyIndex_Check(item)) {
251         Py_ssize_t i;
252         i = PyNumber_AsSsize_t(item, PyExc_IndexError);
253         if (i == -1 && PyErr_Occurred())
254             return NULL;
255         if (i < 0)
256             i += self->mb_size;
257         return mb_item(self, i);
258     }
259     else if (PySlice_Check(item)) {
260         Py_ssize_t start, stop, step, slicelength;
261 
262         if (PySlice_GetIndicesEx(item, self->mb_size,
263                          &start, &stop, &step, &slicelength) < 0)
264             return NULL;
265 
266         if (step == 1)
267             return mb_slice(self, start, stop);
268         else {
269             PyErr_SetString(PyExc_TypeError,
270                             "buffer doesn't support slicing with step != 1");
271             return NULL;
272         }
273     }
274     else {
275         PyErr_Format(PyExc_TypeError,
276                      "buffer indices must be integers, not %.200s",
277                      item->ob_type->tp_name);
278         return NULL;
279     }
280 }
281 static int
mb_ass_subscript(MiniBufferObj * self,PyObject * item,PyObject * value)282 mb_ass_subscript(MiniBufferObj* self, PyObject* item, PyObject* value)
283 {
284     if (PyIndex_Check(item)) {
285         Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
286         if (i == -1 && PyErr_Occurred())
287             return -1;
288         if (i < 0)
289             i += self->mb_size;
290         return mb_ass_item(self, i, value);
291     }
292     else if (PySlice_Check(item)) {
293         Py_ssize_t start, stop, step, slicelength;
294 
295         if (PySlice_GetIndicesEx(item, self->mb_size,
296                          &start, &stop, &step, &slicelength) < 0) {
297             return -1;
298         }
299 
300         if (step == 1)
301             return mb_ass_slice(self, start, stop, value);
302         else {
303             PyErr_SetString(PyExc_TypeError,
304                             "buffer doesn't support slicing with step != 1");
305             return -1;
306         }
307     }
308     else {
309         PyErr_Format(PyExc_TypeError,
310                      "buffer indices must be integers, not %.200s",
311                      item->ob_type->tp_name);
312         return -1;
313     }
314 }
315 
316 static PyMappingMethods mb_as_mapping = {
317     (lenfunc)mb_length, /*mp_length*/
318     (binaryfunc)mb_subscript, /*mp_subscript*/
319     (objobjargproc)mb_ass_subscript, /*mp_ass_subscript*/
320 };
321 #endif
322 
323 #if PY_MAJOR_VERSION >= 3
324 # define MINIBUF_TPFLAGS 0
325 #else
326 # define MINIBUF_TPFLAGS (Py_TPFLAGS_HAVE_GETCHARBUFFER | Py_TPFLAGS_HAVE_NEWBUFFER)
327 #endif
328 
329 PyDoc_STRVAR(ffi_buffer_doc,
330 "ffi.buffer(cdata[, byte_size]):\n"
331 "Return a read-write buffer object that references the raw C data\n"
332 "pointed to by the given 'cdata'.  The 'cdata' must be a pointer or an\n"
333 "array.  Can be passed to functions expecting a buffer, or directly\n"
334 "manipulated with:\n"
335 "\n"
336 "    buf[:]          get a copy of it in a regular string, or\n"
337 "    buf[idx]        as a single character\n"
338 "    buf[:] = ...\n"
339 "    buf[idx] = ...  change the content");
340 
341 static PyObject *            /* forward, implemented in _cffi_backend.c */
342 b_buffer_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
343 
344 
345 static PyTypeObject MiniBuffer_Type = {
346     PyVarObject_HEAD_INIT(NULL, 0)
347     "_cffi_backend.buffer",
348     sizeof(MiniBufferObj),
349     0,
350     (destructor)mb_dealloc,                     /* tp_dealloc */
351     0,                                          /* tp_print */
352     0,                                          /* tp_getattr */
353     0,                                          /* tp_setattr */
354     0,                                          /* tp_compare */
355     0,                                          /* tp_repr */
356     0,                                          /* tp_as_number */
357     &mb_as_sequence,                            /* tp_as_sequence */
358 #if PY_MAJOR_VERSION < 3
359     0,                                          /* tp_as_mapping */
360 #else
361     &mb_as_mapping,                             /* tp_as_mapping */
362 #endif
363     0,                                          /* tp_hash */
364     0,                                          /* tp_call */
365 #if PY_MAJOR_VERSION < 3
366     (reprfunc)mb_str,                           /* tp_str */
367 #else
368     0,                                          /* tp_str */
369 #endif
370     PyObject_GenericGetAttr,                    /* tp_getattro */
371     0,                                          /* tp_setattro */
372     &mb_as_buffer,                              /* tp_as_buffer */
373     (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
374         MINIBUF_TPFLAGS),                       /* tp_flags */
375     ffi_buffer_doc,                             /* tp_doc */
376     (traverseproc)mb_traverse,                  /* tp_traverse */
377     (inquiry)mb_clear,                          /* tp_clear */
378     (richcmpfunc)mb_richcompare,                /* tp_richcompare */
379     offsetof(MiniBufferObj, mb_weakreflist),    /* tp_weaklistoffset */
380     0,                                          /* tp_iter */
381     0,                                          /* tp_iternext */
382     0,                                          /* tp_methods */
383     0,                                          /* tp_members */
384     0,                                          /* tp_getset */
385     0,                                          /* tp_base */
386     0,                                          /* tp_dict */
387     0,                                          /* tp_descr_get */
388     0,                                          /* tp_descr_set */
389     0,                                          /* tp_dictoffset */
390     0,                                          /* tp_init */
391     0,                                          /* tp_alloc */
392     b_buffer_new,                               /* tp_new */
393     0,                                          /* tp_free */
394 };
395 
minibuffer_new(char * data,Py_ssize_t size,PyObject * keepalive)396 static PyObject *minibuffer_new(char *data, Py_ssize_t size,
397                                 PyObject *keepalive)
398 {
399     MiniBufferObj *ob = PyObject_GC_New(MiniBufferObj, &MiniBuffer_Type);
400     if (ob != NULL) {
401         ob->mb_data = data;
402         ob->mb_size = size;
403         ob->mb_keepalive = keepalive; Py_INCREF(keepalive);
404         ob->mb_weakreflist = NULL;
405         PyObject_GC_Track(ob);
406     }
407     return (PyObject *)ob;
408 }
409