Index: Objects/bufferobject.c =================================================================== --- Objects/bufferobject.c (revision 53047) +++ Objects/bufferobject.c (working copy) @@ -472,6 +472,61 @@ 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, + "string indices must be integers"); + return NULL; + } +} + static int buffer_ass_item(PyBufferObject *self, Py_ssize_t idx, PyObject *other) { @@ -581,6 +636,98 @@ return 0; } +static int +buffer_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; + + 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 ((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_t @@ -656,6 +803,12 @@ (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, @@ -677,7 +830,7 @@ (reprfunc)buffer_repr, /* tp_repr */ 0, /* tp_as_number */ &buffer_as_sequence, /* tp_as_sequence */ - 0, /* tp_as_mapping */ + &buffer_as_mapping, /* tp_as_mapping */ (hashfunc)buffer_hash, /* tp_hash */ 0, /* tp_call */ (reprfunc)buffer_str, /* tp_str */ Index: Lib/test/test_buffer.py =================================================================== --- Lib/test/test_buffer.py (.../trunk) (revision 0) +++ Lib/test/test_buffer.py (.../p3yk-noslice) (revision 53047) @@ -0,0 +58,58 @@ +"""Unit tests for buffer objects. + +For now, tests just new or changed functionality. + +""" + +import unittest +from test import test_support + +class BufferTests(unittest.TestCase): + + def test_comparison(self): + a = buffer("a.b.c") + b = buffer("a.b" + ".c") + self.assert_(a == b) + self.assert_(a <= b) + self.assert_(a >= b) + self.assert_(a == "a.b.c") + self.assert_(a <= "a.b.c") + self.assert_(a >= "a.b.c") + b = buffer("a.b.c.d") + self.assert_(a != b) + self.assert_(a <= b) + self.assert_(a < b) + self.assert_(a != "a.b.c.d") + self.assert_(a < "a.b.c.d") + self.assert_(a <= "a.b.c.d") + b = buffer("a.b") + self.assert_(a != b) + self.assert_(a >= b) + self.assert_(a > b) + self.assert_(a != "a.b") + self.assert_(a > "a.b") + self.assert_(a >= "a.b") + b = object() + self.assert_(a != b) + self.failIf(a == b) + self.assertRaises(TypeError, lambda: a < b) + + def test_extended_getslice(self): + # Test extended slicing by comparing with list slicing. + s = "".join(chr(c) for c in list(range(255, -1, -1))) + b = bytes(s) + indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300) + for start in indices: + for stop in indices: + # Skip step 0 (invalid) + for step in indices[1:]: + self.assertEqual(b[start:stop:step], + buffer(s[start:stop:step])) + + + +def test_main(): + test_support.run_unittest(BufferTests) + +if __name__ == "__main__": + test_main()