Merged in py3k-buffer branch to main line. All objects now use the buffer protocol in PEP 3118.
diff --git a/Objects/bufferobject.c b/Objects/bufferobject.c
index 64b11a9..a8bbd15 100644
--- a/Objects/bufferobject.c
+++ b/Objects/bufferobject.c
@@ -15,80 +15,58 @@
} PyBufferObject;
-enum buffer_t {
- READ_BUFFER,
- WRITE_BUFFER,
- CHAR_BUFFER,
- ANY_BUFFER
-};
-
static int
-get_buf(PyBufferObject *self, void **ptr, Py_ssize_t *size,
- enum buffer_t buffer_type)
+get_buf(PyBufferObject *self, PyBuffer *view, int flags)
{
if (self->b_base == NULL) {
- assert (ptr != NULL);
- *ptr = self->b_ptr;
- *size = self->b_size;
+ view->buf = self->b_ptr;
+ view->len = 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) {
- 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;
+ if ((*bp->bf_getbuffer)(self->b_base, view, flags) < 0) return 0;
+ count = view->len;
/* apply constraints to the start/end */
if (self->b_offset > count)
offset = count;
else
offset = self->b_offset;
- *(char **)ptr = *(char **)ptr + offset;
+ view->buf = (char*)view->buf + offset;
if (self->b_size == Py_END_OF_BUFFER)
- *size = count;
+ view->len = count;
else
- *size = self->b_size;
- if (offset + *size > count)
- *size = count - offset;
+ view->len = self->b_size;
+ if (offset + view->len > count)
+ view->len = count - offset;
}
return 1;
}
+static int
+buffer_getbuf(PyBufferObject *self, PyBuffer *view, int flags)
+{
+ if (view == NULL) return 0;
+ if (!get_buf(self, view, flags))
+ return -1;
+ return PyBuffer_FillInfo(view, view->buf, view->len, self->b_readonly, flags);
+}
+
+
+static void
+buffer_releasebuf(PyBufferObject *self, PyBuffer *view)
+{
+ /* No-op if there is no self->b_base */
+ if (self->b_base != NULL) {
+ PyBufferProcs *bp = self->b_base->ob_type->tp_as_buffer;
+ if (bp->bf_releasebuffer != NULL) {
+ (*bp->bf_releasebuffer)(self->b_base, view);
+ }
+ }
+ return;
+}
+
static PyObject *
buffer_from_memory(PyObject *base, Py_ssize_t size, Py_ssize_t offset, void *ptr,
int readonly)
@@ -152,10 +130,8 @@
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");
+ pb->bf_getbuffer == NULL) {
+ PyErr_SetString(PyExc_TypeError, "buffer object expected");
return NULL;
}
@@ -168,9 +144,7 @@
PyBufferProcs *pb = base->ob_type->tp_as_buffer;
if ( pb == NULL ||
- pb->bf_getwritebuffer == NULL ||
- pb->bf_getsegcount == NULL )
- {
+ pb->bf_getbuffer == NULL) {
PyErr_SetString(PyExc_TypeError, "buffer object expected");
return NULL;
}
@@ -252,12 +226,12 @@
}
static int
-get_bufx(PyObject *obj, void **ptr, Py_ssize_t *size)
+get_bufx(PyObject *obj, PyBuffer *view, int flags)
{
PyBufferProcs *bp;
if (PyBuffer_Check(obj)) {
- if (!get_buf((PyBufferObject *)obj, ptr, size, ANY_BUFFER)) {
+ if (!get_buf((PyBufferObject *)obj, view, flags)) {
PyErr_Clear();
return 0;
}
@@ -266,17 +240,11 @@
}
bp = obj->ob_type->tp_as_buffer;
if (bp == NULL ||
- bp->bf_getreadbuffer == NULL ||
- bp->bf_getsegcount == NULL)
+ bp->bf_getbuffer == NULL)
return 0;
- if ((*bp->bf_getsegcount)(obj, NULL) != 1)
+ if ((*bp->bf_getbuffer)(obj, view, PyBUF_SIMPLE) < 0)
return 0;
- *size = (*bp->bf_getreadbuffer)(obj, 0, ptr);
- if (*size < 0) {
- PyErr_Clear();
- return 0;
- }
- return 1;
+ return 1;
}
static PyObject *
@@ -285,12 +253,15 @@
void *p1, *p2;
Py_ssize_t len1, len2, min_len;
int cmp, ok;
+ PyBuffer v1, v2;
ok = 1;
- if (!get_bufx(self, &p1, &len1))
+ if (!get_bufx(self, &v1, PyBUF_SIMPLE))
ok = 0;
- if (!get_bufx(other, &p2, &len2))
+ if (!get_bufx(other, &v2, PyBUF_SIMPLE)) {
+ if (ok) PyObject_ReleaseBuffer((PyObject *)self, &v1);
ok = 0;
+ }
if (!ok) {
/* If we can't get the buffers,
== and != are still defined
@@ -305,11 +276,17 @@
Py_INCREF(result);
return result;
}
+ len1 = v1.len;
+ len2 = v2.len;
+ p1 = v1.buf;
+ p2 = v2.buf;
min_len = (len1 < len2) ? len1 : len2;
cmp = memcmp(p1, p2, min_len);
if (cmp == 0)
cmp = (len1 < len2) ? -1 :
(len1 > len2) ? 1 : 0;
+ PyObject_ReleaseBuffer((PyObject *)self, &v1);
+ PyObject_ReleaseBuffer(other, &v2);
return Py_CmpToRich(op, cmp);
}
@@ -337,8 +314,7 @@
static long
buffer_hash(PyBufferObject *self)
{
- void *ptr;
- Py_ssize_t size;
+ PyBuffer view;
register Py_ssize_t len;
register unsigned char *p;
register long x;
@@ -346,42 +322,39 @@
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");
+ if (!get_buf(self, &view, PyBUF_SIMPLE))
+ return -1;
+ if (!(self->b_readonly)) {
+ PyErr_SetString(PyExc_TypeError,
+ "writable buffers are not hashable");
+ PyObject_ReleaseBuffer((PyObject *)self, &view);
return -1;
}
-
- if (!get_buf(self, &ptr, &size, ANY_BUFFER))
- return -1;
- p = (unsigned char *) ptr;
- len = size;
+
+ p = (unsigned char *) view.buf;
+ len = view.len;
x = *p << 7;
while (--len >= 0)
x = (1000003*x) ^ *p++;
- x ^= size;
+ x ^= view.len;
if (x == -1)
x = -2;
self->b_hash = x;
+ PyObject_ReleaseBuffer((PyObject *)self, &view);
return x;
}
static PyObject *
buffer_str(PyBufferObject *self)
{
- void *ptr;
- Py_ssize_t size;
- if (!get_buf(self, &ptr, &size, ANY_BUFFER))
+ PyBuffer view;
+ PyObject *res;
+
+ if (!get_buf(self, &view, PyBUF_SIMPLE))
return NULL;
- return PyString_FromStringAndSize((const char *)ptr, size);
+ res = PyString_FromStringAndSize((const char *)view.buf, view.len);
+ PyObject_ReleaseBuffer((PyObject *)self, &view);
+ return res;
}
/* Sequence methods */
@@ -389,71 +362,58 @@
static Py_ssize_t
buffer_length(PyBufferObject *self)
{
- void *ptr;
- Py_ssize_t size;
- if (!get_buf(self, &ptr, &size, ANY_BUFFER))
+ PyBuffer view;
+
+ if (!get_buf(self, &view, PyBUF_SIMPLE))
return -1;
- return size;
+ PyObject_ReleaseBuffer((PyObject *)self, &view);
+ return view.len;
}
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;
+ PyBuffer view, view2;
if ( pb == NULL ||
- pb->bf_getreadbuffer == NULL ||
- pb->bf_getsegcount == NULL )
+ pb->bf_getbuffer == 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))
+ if (!get_buf(self, &view, PyBUF_SIMPLE))
return NULL;
/* optimize special case */
/* XXX bad idea type-wise */
- if ( size == 0 )
- {
- Py_INCREF(other);
- return other;
+ if ( view.len == 0 ) {
+ PyObject_ReleaseBuffer((PyObject *)self, &view);
+ Py_INCREF(other);
+ return other;
}
- if (PyUnicode_Check(other)) {
- /* XXX HACK */
- if ( (count = (*pb->bf_getcharbuffer)(other, 0,
- (char **)&ptr2)) < 0 )
- return NULL;
- }
- else {
- if ( (count = (*pb->bf_getreadbuffer)(other, 0, &ptr2)) < 0 )
- return NULL;
- }
+ if (PyObject_GetBuffer((PyObject *)other, &view2, PyBUF_SIMPLE) < 0) {
+ PyObject_ReleaseBuffer((PyObject *)self, &view);
+ return NULL;
+ }
- /* XXX Should return a bytes object, really */
- ob = PyString_FromStringAndSize(NULL, size + count);
- if ( ob == NULL )
+ ob = PyBytes_FromStringAndSize(NULL, view.len+view2.len);
+ if ( ob == NULL ) {
+ PyObject_ReleaseBuffer((PyObject *)self, &view);
+ PyObject_ReleaseBuffer(other, &view2);
return NULL;
- p = PyString_AS_STRING(ob);
- memcpy(p, ptr1, size);
- memcpy(p + size, ptr2, count);
+ }
+ p = PyBytes_AS_STRING(ob);
+ memcpy(p, view.buf, view.len);
+ memcpy(p + view.len, view2.buf, view2.len);
- /* there is an extra byte in the string object, so this is safe */
- p[size + count] = '\0';
-
- return ob;
+ PyObject_ReleaseBuffer((PyObject *)self, &view);
+ PyObject_ReleaseBuffer(other, &view2);
+ return ob;
}
static PyObject *
@@ -461,81 +421,83 @@
{
PyObject *ob;
register char *p;
- void *ptr;
- Py_ssize_t size;
+ PyBuffer view;
if ( count < 0 )
count = 0;
- if (!get_buf(self, &ptr, &size, ANY_BUFFER))
+ if (!get_buf(self, &view, PyBUF_SIMPLE))
return NULL;
- ob = PyString_FromStringAndSize(NULL, size * count);
+ ob = PyBytes_FromStringAndSize(NULL, view.len * count);
if ( ob == NULL )
return NULL;
- p = PyString_AS_STRING(ob);
+ p = PyBytes_AS_STRING(ob);
while ( count-- )
{
- memcpy(p, ptr, size);
- p += size;
+ memcpy(p, view.buf, view.len);
+ p += view.len;
}
- /* there is an extra byte in the string object, so this is safe */
- *p = '\0';
-
+ PyObject_ReleaseBuffer((PyObject *)self, &view);
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))
+ PyBuffer view;
+ PyObject *ob;
+
+ if (!get_buf(self, &view, PyBUF_SIMPLE))
return NULL;
- if ( idx < 0 || idx >= size ) {
+ if ( idx < 0 || idx >= view.len ) {
PyErr_SetString(PyExc_IndexError, "buffer index out of range");
return NULL;
}
- return PyString_FromStringAndSize((char *)ptr + idx, 1);
+ ob = PyBytes_FromStringAndSize((char *)view.buf + idx, 1);
+ PyObject_ReleaseBuffer((PyObject *)self, &view);
+ return ob;
}
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))
+ PyObject *ob;
+ PyBuffer view;
+ if (!get_buf(self, &view, PyBUF_SIMPLE))
return NULL;
if ( left < 0 )
left = 0;
if ( right < 0 )
right = 0;
- if ( right > size )
- right = size;
+ if ( right > view.len )
+ right = view.len;
if ( right < left )
right = left;
- return PyString_FromStringAndSize((char *)ptr + left,
- right - left);
+ ob = PyBytes_FromStringAndSize((char *)view.buf + left,
+ right - left);
+ PyObject_ReleaseBuffer((PyObject *)self, &view);
+ return ob;
}
static int
buffer_ass_item(PyBufferObject *self, Py_ssize_t idx, PyObject *other)
{
PyBufferProcs *pb;
- void *ptr1, *ptr2;
- Py_ssize_t size;
- Py_ssize_t count;
+ PyBuffer view, view2;
- if ( self->b_readonly ) {
+ if (!get_buf(self, &view, PyBUF_SIMPLE))
+ return -1;
+
+ if ( self->b_readonly || view.readonly ) {
PyErr_SetString(PyExc_TypeError,
"buffer is read-only");
+ PyObject_ReleaseBuffer((PyObject *)self, &view);
return -1;
}
- if (!get_buf(self, &ptr1, &size, ANY_BUFFER))
- return -1;
-
- if (idx < 0 || idx >= size) {
+ if (idx < 0 || idx >= view.len) {
+ PyObject_ReleaseBuffer((PyObject *)self, &view);
PyErr_SetString(PyExc_IndexError,
"buffer assignment index out of range");
return -1;
@@ -543,29 +505,27 @@
pb = other ? other->ob_type->tp_as_buffer : NULL;
if ( pb == NULL ||
- pb->bf_getreadbuffer == NULL ||
- pb->bf_getsegcount == NULL )
- {
+ pb->bf_getbuffer == NULL) {
PyErr_BadArgument();
+ PyObject_ReleaseBuffer((PyObject *)self, &view);
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 ) {
+
+ if (PyObject_GetBuffer(other, &view2, PyBUF_SIMPLE) < 0) {
+ PyObject_ReleaseBuffer((PyObject *)self, &view);
+ return -1;
+ }
+ if ( view.len != 1 ) {
+ PyObject_ReleaseBuffer((PyObject *)self, &view);
+ PyObject_ReleaseBuffer(other, &view2);
PyErr_SetString(PyExc_TypeError,
"right operand must be a single byte");
return -1;
}
- ((char *)ptr1)[idx] = *(char *)ptr2;
+ ((char *)(view.buf))[idx] = *((char *)(view2.buf));
+ PyObject_ReleaseBuffer((PyObject *)self, &view);
+ PyObject_ReleaseBuffer(other, &view2);
return 0;
}
@@ -573,125 +533,60 @@
buffer_ass_slice(PyBufferObject *self, Py_ssize_t left, Py_ssize_t right, PyObject *other)
{
PyBufferProcs *pb;
- void *ptr1, *ptr2;
- Py_ssize_t size;
+ PyBuffer v1, v2;
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 )
+ pb->bf_getbuffer == NULL)
{
PyErr_BadArgument();
return -1;
}
- if ( (*pb->bf_getsegcount)(other, NULL) != 1 )
- {
- /* ### use a different exception type/message? */
+ if (!get_buf(self, &v1, PyBUF_SIMPLE))
+ return -1;
+
+ if ( self->b_readonly || v1.readonly) {
PyErr_SetString(PyExc_TypeError,
- "single-segment buffer object expected");
+ "buffer is read-only");
+ PyObject_ReleaseBuffer((PyObject *)self, &v1);
return -1;
}
- if (!get_buf(self, &ptr1, &size, ANY_BUFFER))
- return -1;
- if ( (count = (*pb->bf_getreadbuffer)(other, 0, &ptr2)) < 0 )
- return -1;
+
+ if ((*pb->bf_getbuffer)(other, &v2, PyBUF_SIMPLE) < 0) {
+ PyObject_ReleaseBuffer((PyObject *)self, &v1);
+ return -1;
+ }
if ( left < 0 )
left = 0;
- else if ( left > size )
- left = size;
+ else if ( left > v1.len )
+ left = v1.len;
if ( right < left )
right = left;
- else if ( right > size )
- right = size;
+ else if ( right > v1.len )
+ right = v1.len;
slice_len = right - left;
- if ( count != slice_len ) {
+ if ( v2.len != slice_len ) {
PyErr_SetString(
PyExc_TypeError,
"right operand length must match slice length");
+ PyObject_ReleaseBuffer((PyObject *)self, &v1);
+ PyObject_ReleaseBuffer(other, &v2);
return -1;
}
if ( slice_len )
- memcpy((char *)ptr1 + left, ptr2, slice_len);
+ memcpy((char *)v1.buf + left, v2.buf, slice_len);
+ PyObject_ReleaseBuffer((PyObject *)self, &v1);
+ PyObject_ReleaseBuffer(other, &v2);
return 0;
}
/* Buffer methods */
-static Py_ssize_t
-buffer_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_t
-buffer_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_t
-buffer_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_t
-buffer_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*/
@@ -703,10 +598,8 @@
};
static PyBufferProcs buffer_as_buffer = {
- (readbufferproc)buffer_getreadbuf,
- (writebufferproc)buffer_getwritebuf,
- (segcountproc)buffer_getsegcount,
- (charbufferproc)buffer_getcharbuf,
+ (getbufferproc)buffer_getbuf,
+ (releasebufferproc)buffer_releasebuf,
};
PyTypeObject PyBuffer_Type = {