|
|
/* Buffer object implementation */
#include "Python.h"
typedef struct { PyObject_HEAD PyObject *b_base; void *b_ptr; Py_ssize_t b_size; Py_ssize_t b_offset; int b_readonly; long b_hash;} PyBufferObject;
enum buffer_t { READ_BUFFER, WRITE_BUFFER, CHAR_BUFFER, ANY_BUFFER};
static intget_buf(PyBufferObject *self, void **ptr, Py_ssize_t *size, enum buffer_t buffer_type){ if (self->b_base == NULL) { assert (ptr != NULL); *ptr = self->b_ptr; *size = self->b_size; } else { Py_ssize_t count, offset; readbufferproc proc = 0; PyBufferProcs *bp = self->b_base->ob_type->tp_as_buffer; if ((*bp->bf_getsegcount)(self->b_base, NULL) != 1) { PyErr_SetString(PyExc_TypeError, "single-segment buffer object expected"); return 0; } if ((buffer_type == READ_BUFFER) || ((buffer_type == ANY_BUFFER) && self->b_readonly)) proc = bp->bf_getreadbuffer; else if ((buffer_type == WRITE_BUFFER) || (buffer_type == ANY_BUFFER)) proc = (readbufferproc)bp->bf_getwritebuffer; else if (buffer_type == CHAR_BUFFER) { if (!PyType_HasFeature(self->ob_type, Py_TPFLAGS_HAVE_GETCHARBUFFER)) { PyErr_SetString(PyExc_TypeError, "Py_TPFLAGS_HAVE_GETCHARBUFFER needed"); return 0; } proc = (readbufferproc)bp->bf_getcharbuffer; } if (!proc) { char *buffer_type_name; switch (buffer_type) { case READ_BUFFER: buffer_type_name = "read"; break; case WRITE_BUFFER: buffer_type_name = "write"; break; case CHAR_BUFFER: buffer_type_name = "char"; break; default: buffer_type_name = "no"; break; } PyErr_Format(PyExc_TypeError, "%s buffer type not available", buffer_type_name); return 0; } if ((count = (*proc)(self->b_base, 0, ptr)) < 0) return 0; /* apply constraints to the start/end */ if (self->b_offset > count) offset = count; else offset = self->b_offset; *(char **)ptr = *(char **)ptr + offset; if (self->b_size == Py_END_OF_BUFFER) *size = count; else *size = self->b_size; if (offset + *size > count) *size = count - offset; } return 1;}
static PyObject *buffer_from_memory(PyObject *base, Py_ssize_t size, Py_ssize_t offset, void *ptr, int readonly){ PyBufferObject * b;
if (size < 0 && size != Py_END_OF_BUFFER) { PyErr_SetString(PyExc_ValueError, "size must be zero or positive"); return NULL; } if (offset < 0) { PyErr_SetString(PyExc_ValueError, "offset must be zero or positive"); return NULL; }
b = PyObject_NEW(PyBufferObject, &PyBuffer_Type); if ( b == NULL ) return NULL;
Py_XINCREF(base); b->b_base = base; b->b_ptr = ptr; b->b_size = size; b->b_offset = offset; b->b_readonly = readonly; b->b_hash = -1;
return (PyObject *) b;}
static PyObject *buffer_from_object(PyObject *base, Py_ssize_t size, Py_ssize_t offset, int readonly){ if (offset < 0) { PyErr_SetString(PyExc_ValueError, "offset must be zero or positive"); return NULL; } if ( PyBuffer_Check(base) && (((PyBufferObject *)base)->b_base) ) { /* another buffer, refer to the base object */ PyBufferObject *b = (PyBufferObject *)base; if (b->b_size != Py_END_OF_BUFFER) { Py_ssize_t base_size = b->b_size - offset; if (base_size < 0) base_size = 0; if (size == Py_END_OF_BUFFER || size > base_size) size = base_size; } offset += b->b_offset; base = b->b_base; } return buffer_from_memory(base, size, offset, NULL, readonly);}
PyObject *PyBuffer_FromObject(PyObject *base, Py_ssize_t offset, Py_ssize_t size){ PyBufferProcs *pb = base->ob_type->tp_as_buffer;
if ( pb == NULL || pb->bf_getreadbuffer == NULL || pb->bf_getsegcount == NULL ) { PyErr_SetString(PyExc_TypeError, "buffer object expected"); return NULL; }
return buffer_from_object(base, size, offset, 1);}
PyObject *PyBuffer_FromReadWriteObject(PyObject *base, Py_ssize_t offset, Py_ssize_t size){ PyBufferProcs *pb = base->ob_type->tp_as_buffer;
if ( pb == NULL || pb->bf_getwritebuffer == NULL || pb->bf_getsegcount == NULL ) { PyErr_SetString(PyExc_TypeError, "buffer object expected"); return NULL; }
return buffer_from_object(base, size, offset, 0);}
PyObject *PyBuffer_FromMemory(void *ptr, Py_ssize_t size){ return buffer_from_memory(NULL, size, 0, ptr, 1);}
PyObject *PyBuffer_FromReadWriteMemory(void *ptr, Py_ssize_t size){ return buffer_from_memory(NULL, size, 0, ptr, 0);}
PyObject *PyBuffer_New(Py_ssize_t size){ PyObject *o; PyBufferObject * b;
if (size < 0) { PyErr_SetString(PyExc_ValueError, "size must be zero or positive"); return NULL; } if (sizeof(*b) > PY_SSIZE_T_MAX - size) { /* unlikely */ return PyErr_NoMemory(); } /* Inline PyObject_New */ o = (PyObject *)PyObject_MALLOC(sizeof(*b) + size); if ( o == NULL ) return PyErr_NoMemory(); b = (PyBufferObject *) PyObject_INIT(o, &PyBuffer_Type);
b->b_base = NULL; b->b_ptr = (void *)(b + 1); b->b_size = size; b->b_offset = 0; b->b_readonly = 0; b->b_hash = -1;
return o;}
/* Methods */
static PyObject *buffer_new(PyTypeObject *type, PyObject *args, PyObject *kw){ PyObject *ob; Py_ssize_t offset = 0; Py_ssize_t size = Py_END_OF_BUFFER;
if (PyErr_WarnPy3k("buffer() not supported in 3.x", 1) < 0) return NULL;
if (!_PyArg_NoKeywords("buffer()", kw)) return NULL;
if (!PyArg_ParseTuple(args, "O|nn:buffer", &ob, &offset, &size)) return NULL; return PyBuffer_FromObject(ob, offset, size);}
PyDoc_STRVAR(buffer_doc,"buffer(object [, offset[, size]])\n\
\n\Create a new buffer object which references the given object.\n\The buffer will reference a slice of the target object from the\n\start of the object (or at the specified offset). The slice will\n\extend to the end of the target object (or with the specified size).");
static voidbuffer_dealloc(PyBufferObject *self){ Py_XDECREF(self->b_base); PyObject_DEL(self);}
static intbuffer_compare(PyBufferObject *self, PyBufferObject *other){ void *p1, *p2; Py_ssize_t len_self, len_other, min_len; int cmp;
if (!get_buf(self, &p1, &len_self, ANY_BUFFER)) return -1; if (!get_buf(other, &p2, &len_other, ANY_BUFFER)) return -1; min_len = (len_self < len_other) ? len_self : len_other; if (min_len > 0) { cmp = memcmp(p1, p2, min_len); if (cmp != 0) return cmp < 0 ? -1 : 1; } return (len_self < len_other) ? -1 : (len_self > len_other) ? 1 : 0;}
static PyObject *buffer_repr(PyBufferObject *self){ const char *status = self->b_readonly ? "read-only" : "read-write";
if ( self->b_base == NULL ) return PyString_FromFormat("<%s buffer ptr %p, size %zd at %p>", status, self->b_ptr, self->b_size, self); else return PyString_FromFormat( "<%s buffer for %p, size %zd, offset %zd at %p>", status, self->b_base, self->b_size, self->b_offset, self);}
static longbuffer_hash(PyBufferObject *self){ void *ptr; Py_ssize_t size; register Py_ssize_t len; register unsigned char *p; register long x;
if ( self->b_hash != -1 ) return self->b_hash;
/* XXX potential bugs here, a readonly buffer does not imply that the
* underlying memory is immutable. b_readonly is a necessary but not * sufficient condition for a buffer to be hashable. Perhaps it would * be better to only allow hashing if the underlying object is known to * be immutable (e.g. PyString_Check() is true). Another idea would * be to call tp_hash on the underlying object and see if it raises * an error. */ if ( !self->b_readonly ) { PyErr_SetString(PyExc_TypeError, "writable buffers are not hashable"); return -1; }
if (!get_buf(self, &ptr, &size, ANY_BUFFER)) return -1; p = (unsigned char *) ptr; len = size; /*
We make the hash of the empty buffer be 0, rather than using (prefix ^ suffix), since this slightly obfuscates the hash secret */ if (len == 0) { self->b_hash = 0; return 0; } x = _Py_HashSecret.prefix; x ^= *p << 7; while (--len >= 0) x = (1000003*x) ^ *p++; x ^= size; x ^= _Py_HashSecret.suffix; if (x == -1) x = -2; self->b_hash = x; return x;}
static PyObject *buffer_str(PyBufferObject *self){ void *ptr; Py_ssize_t size; if (!get_buf(self, &ptr, &size, ANY_BUFFER)) return NULL; return PyString_FromStringAndSize((const char *)ptr, size);}
/* Sequence methods */
static Py_ssize_tbuffer_length(PyBufferObject *self){ void *ptr; Py_ssize_t size; if (!get_buf(self, &ptr, &size, ANY_BUFFER)) return -1; return size;}
static PyObject *buffer_concat(PyBufferObject *self, PyObject *other){ PyBufferProcs *pb = other->ob_type->tp_as_buffer; void *ptr1, *ptr2; char *p; PyObject *ob; Py_ssize_t size, count;
if ( pb == NULL || pb->bf_getreadbuffer == NULL || pb->bf_getsegcount == NULL ) { PyErr_BadArgument(); return NULL; } if ( (*pb->bf_getsegcount)(other, NULL) != 1 ) { /* ### use a different exception type/message? */ PyErr_SetString(PyExc_TypeError, "single-segment buffer object expected"); return NULL; }
if (!get_buf(self, &ptr1, &size, ANY_BUFFER)) return NULL;
/* optimize special case */ if ( size == 0 ) { Py_INCREF(other); return other; }
if ( (count = (*pb->bf_getreadbuffer)(other, 0, &ptr2)) < 0 ) return NULL;
assert(count <= PY_SIZE_MAX - size);
ob = PyString_FromStringAndSize(NULL, size + count); if ( ob == NULL ) return NULL; p = PyString_AS_STRING(ob); memcpy(p, ptr1, size); memcpy(p + size, ptr2, count);
/* there is an extra byte in the string object, so this is safe */ p[size + count] = '\0';
return ob;}
static PyObject *buffer_repeat(PyBufferObject *self, Py_ssize_t count){ PyObject *ob; register char *p; void *ptr; Py_ssize_t size;
if ( count < 0 ) count = 0; if (!get_buf(self, &ptr, &size, ANY_BUFFER)) return NULL; if (count > PY_SSIZE_T_MAX / size) { PyErr_SetString(PyExc_MemoryError, "result too large"); return NULL; } ob = PyString_FromStringAndSize(NULL, size * count); if ( ob == NULL ) return NULL;
p = PyString_AS_STRING(ob); while ( count-- ) { memcpy(p, ptr, size); p += size; }
/* there is an extra byte in the string object, so this is safe */ *p = '\0';
return ob;}
static PyObject *buffer_item(PyBufferObject *self, Py_ssize_t idx){ void *ptr; Py_ssize_t size; if (!get_buf(self, &ptr, &size, ANY_BUFFER)) return NULL; if ( idx < 0 || idx >= size ) { PyErr_SetString(PyExc_IndexError, "buffer index out of range"); return NULL; } return PyString_FromStringAndSize((char *)ptr + idx, 1);}
static PyObject *buffer_slice(PyBufferObject *self, Py_ssize_t left, Py_ssize_t right){ void *ptr; Py_ssize_t size; if (!get_buf(self, &ptr, &size, ANY_BUFFER)) return NULL; if ( left < 0 ) left = 0; if ( right < 0 ) right = 0; if ( right > size ) right = size; if ( right < left ) right = left; return PyString_FromStringAndSize((char *)ptr + left, right - left);}
static PyObject *buffer_subscript(PyBufferObject *self, PyObject *item){ void *p; Py_ssize_t size;
if (!get_buf(self, &p, &size, ANY_BUFFER)) return NULL; if (PyIndex_Check(item)) { Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) return NULL; if (i < 0) i += size; return buffer_item(self, i); } else if (PySlice_Check(item)) { Py_ssize_t start, stop, step, slicelength, cur, i;
if (PySlice_GetIndicesEx((PySliceObject*)item, size, &start, &stop, &step, &slicelength) < 0) { return NULL; }
if (slicelength <= 0) return PyString_FromStringAndSize("", 0); else if (step == 1) return PyString_FromStringAndSize((char *)p + start, stop - start); else { PyObject *result; char *source_buf = (char *)p; char *result_buf = (char *)PyMem_Malloc(slicelength);
if (result_buf == NULL) return PyErr_NoMemory();
for (cur = start, i = 0; i < slicelength; cur += step, i++) { result_buf[i] = source_buf[cur]; }
result = PyString_FromStringAndSize(result_buf, slicelength); PyMem_Free(result_buf); return result; } } else { PyErr_SetString(PyExc_TypeError, "sequence index must be integer"); return NULL; }}
static intbuffer_ass_item(PyBufferObject *self, Py_ssize_t idx, PyObject *other){ PyBufferProcs *pb; void *ptr1, *ptr2; Py_ssize_t size; Py_ssize_t count;
if ( self->b_readonly ) { PyErr_SetString(PyExc_TypeError, "buffer is read-only"); return -1; }
if (!get_buf(self, &ptr1, &size, ANY_BUFFER)) return -1;
if (idx < 0 || idx >= size) { PyErr_SetString(PyExc_IndexError, "buffer assignment index out of range"); return -1; }
pb = other ? other->ob_type->tp_as_buffer : NULL; if ( pb == NULL || pb->bf_getreadbuffer == NULL || pb->bf_getsegcount == NULL ) { PyErr_BadArgument(); return -1; } if ( (*pb->bf_getsegcount)(other, NULL) != 1 ) { /* ### use a different exception type/message? */ PyErr_SetString(PyExc_TypeError, "single-segment buffer object expected"); return -1; }
if ( (count = (*pb->bf_getreadbuffer)(other, 0, &ptr2)) < 0 ) return -1; if ( count != 1 ) { PyErr_SetString(PyExc_TypeError, "right operand must be a single byte"); return -1; }
((char *)ptr1)[idx] = *(char *)ptr2; return 0;}
static intbuffer_ass_slice(PyBufferObject *self, Py_ssize_t left, Py_ssize_t right, PyObject *other){ PyBufferProcs *pb; void *ptr1, *ptr2; Py_ssize_t size; Py_ssize_t slice_len; Py_ssize_t count;
if ( self->b_readonly ) { PyErr_SetString(PyExc_TypeError, "buffer is read-only"); return -1; }
pb = other ? other->ob_type->tp_as_buffer : NULL; if ( pb == NULL || pb->bf_getreadbuffer == NULL || pb->bf_getsegcount == NULL ) { PyErr_BadArgument(); return -1; } if ( (*pb->bf_getsegcount)(other, NULL) != 1 ) { /* ### use a different exception type/message? */ PyErr_SetString(PyExc_TypeError, "single-segment buffer object expected"); return -1; } if (!get_buf(self, &ptr1, &size, ANY_BUFFER)) return -1; if ( (count = (*pb->bf_getreadbuffer)(other, 0, &ptr2)) < 0 ) return -1;
if ( left < 0 ) left = 0; else if ( left > size ) left = size; if ( right < left ) right = left; else if ( right > size ) right = size; slice_len = right - left;
if ( count != slice_len ) { PyErr_SetString( PyExc_TypeError, "right operand length must match slice length"); return -1; }
if ( slice_len ) memcpy((char *)ptr1 + left, ptr2, slice_len);
return 0;}
static intbuffer_ass_subscript(PyBufferObject *self, PyObject *item, PyObject *value){ PyBufferProcs *pb; void *ptr1, *ptr2; Py_ssize_t selfsize; Py_ssize_t othersize;
if ( self->b_readonly ) { PyErr_SetString(PyExc_TypeError, "buffer is read-only"); return -1; }
pb = value ? value->ob_type->tp_as_buffer : NULL; if ( pb == NULL || pb->bf_getreadbuffer == NULL || pb->bf_getsegcount == NULL ) { PyErr_BadArgument(); return -1; } if ( (*pb->bf_getsegcount)(value, NULL) != 1 ) { /* ### use a different exception type/message? */ PyErr_SetString(PyExc_TypeError, "single-segment buffer object expected"); return -1; } if (!get_buf(self, &ptr1, &selfsize, ANY_BUFFER)) return -1; 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 += selfsize; return buffer_ass_item(self, i, value); } else if (PySlice_Check(item)) { Py_ssize_t start, stop, step, slicelength;
if (PySlice_GetIndicesEx((PySliceObject *)item, selfsize, &start, &stop, &step, &slicelength) < 0) return -1;
if ((othersize = (*pb->bf_getreadbuffer)(value, 0, &ptr2)) < 0) return -1;
if (othersize != slicelength) { PyErr_SetString( PyExc_TypeError, "right operand length must match slice length"); return -1; }
if (slicelength == 0) return 0; else if (step == 1) { memcpy((char *)ptr1 + start, ptr2, slicelength); return 0; } else { Py_ssize_t cur, i;
for (cur = start, i = 0; i < slicelength; cur += step, i++) { ((char *)ptr1)[cur] = ((char *)ptr2)[i]; }
return 0; } } else { PyErr_SetString(PyExc_TypeError, "buffer indices must be integers"); return -1; }}
/* Buffer methods */
static Py_ssize_tbuffer_getreadbuf(PyBufferObject *self, Py_ssize_t idx, void **pp){ Py_ssize_t size; if ( idx != 0 ) { PyErr_SetString(PyExc_SystemError, "accessing non-existent buffer segment"); return -1; } if (!get_buf(self, pp, &size, READ_BUFFER)) return -1; return size;}
static Py_ssize_tbuffer_getwritebuf(PyBufferObject *self, Py_ssize_t idx, void **pp){ Py_ssize_t size;
if ( self->b_readonly ) { PyErr_SetString(PyExc_TypeError, "buffer is read-only"); return -1; }
if ( idx != 0 ) { PyErr_SetString(PyExc_SystemError, "accessing non-existent buffer segment"); return -1; } if (!get_buf(self, pp, &size, WRITE_BUFFER)) return -1; return size;}
static Py_ssize_tbuffer_getsegcount(PyBufferObject *self, Py_ssize_t *lenp){ void *ptr; Py_ssize_t size; if (!get_buf(self, &ptr, &size, ANY_BUFFER)) return -1; if (lenp) *lenp = size; return 1;}
static Py_ssize_tbuffer_getcharbuf(PyBufferObject *self, Py_ssize_t idx, const char **pp){ void *ptr; Py_ssize_t size; if ( idx != 0 ) { PyErr_SetString(PyExc_SystemError, "accessing non-existent buffer segment"); return -1; } if (!get_buf(self, &ptr, &size, CHAR_BUFFER)) return -1; *pp = (const char *)ptr; return size;}
static PySequenceMethods buffer_as_sequence = { (lenfunc)buffer_length, /*sq_length*/ (binaryfunc)buffer_concat, /*sq_concat*/ (ssizeargfunc)buffer_repeat, /*sq_repeat*/ (ssizeargfunc)buffer_item, /*sq_item*/ (ssizessizeargfunc)buffer_slice, /*sq_slice*/ (ssizeobjargproc)buffer_ass_item, /*sq_ass_item*/ (ssizessizeobjargproc)buffer_ass_slice, /*sq_ass_slice*/};
static PyMappingMethods buffer_as_mapping = { (lenfunc)buffer_length, (binaryfunc)buffer_subscript, (objobjargproc)buffer_ass_subscript,};
static PyBufferProcs buffer_as_buffer = { (readbufferproc)buffer_getreadbuf, (writebufferproc)buffer_getwritebuf, (segcountproc)buffer_getsegcount, (charbufferproc)buffer_getcharbuf,};
PyTypeObject PyBuffer_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "buffer", sizeof(PyBufferObject), 0, (destructor)buffer_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ (cmpfunc)buffer_compare, /* tp_compare */ (reprfunc)buffer_repr, /* tp_repr */ 0, /* tp_as_number */ &buffer_as_sequence, /* tp_as_sequence */ &buffer_as_mapping, /* tp_as_mapping */ (hashfunc)buffer_hash, /* tp_hash */ 0, /* tp_call */ (reprfunc)buffer_str, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ &buffer_as_buffer, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GETCHARBUFFER, /* tp_flags */ buffer_doc, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* 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 */ buffer_new, /* tp_new */};
|