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