Commits

Lenard Lindstrom  committed 3c19154

Add old buffer interface to BufferProxy

For backward compatibiliy, BufferProxy once again exports the old
buffer protocol. Preprocessor flags which enable old or new buffer
support are consolidated and made consistent. Though currently
disable, is should be possible to enable both old and new buffer
protocols in the same build. Bugs uncovered by old buffer related unit
tests of Surface.get_buffer() were fixed. More unit tests are
needed. Also, some code cleanup is desirable.

  • Participants
  • Parent commits eaa04c2

Comments (0)

Files changed (6)

     Py_ssize_t imem[1];
 } ViewInternals;
 
+/* Custom exceptions */
+static PyObject* PgExc_BufferError = NULL;
+
 /* Only one instance of the state per process. */
 static PyObject* quitfunctions = NULL;
 static int sdl_was_init = 0;
         MODINIT_ERROR;
     }
 
+#if PG_ENABLE_NEWBUF
+    PgExc_BufferError = PyErr_NewException ("pygame.BufferError",
+                                            PyExc_BufferError, NULL);
+#else
+    PgExc_BufferError = PyErr_NewException ("pygame.BufferError",
+                                            PyExc_RuntimeError, NULL);
+#endif
+    if (PyExc_SDLError == NULL) {
+        Py_XDECREF (atexit_register);
+        DECREF_MOD (module);
+        MODINIT_ERROR;
+    }
+    ecode = PyDict_SetItemString (dict, "BufferError", PyExc_SDLError);
+    if (ecode) {
+        Py_DECREF (PgExc_BufferError);
+        Py_XDECREF (atexit_register);
+        DECREF_MOD (module);
+        MODINIT_ERROR;
+    }
+
     /* export the c api */
-#if PYGAMEAPI_BASE_NUMSLOTS != 20
+#if PYGAMEAPI_BASE_NUMSLOTS != 21
 #warning export slot count mismatch
 #endif
     c_api[0] = PyExc_SDLError;
     c_api[17] = PgObject_GetBuffer;
     c_api[18] = PgBuffer_Release;
     c_api[19] = PgDict_AsBuffer;
+    c_api[20] = PgExc_BufferError;
     apiobj = encapsulate_api (c_api, "base");
     if (apiobj == NULL) {
         Py_XDECREF (atexit_register);
+        Py_DECREF (PgExc_BufferError);
         DECREF_MOD (module);
         MODINIT_ERROR;
     }
     Py_DECREF (apiobj);
     if (ecode) {
         Py_XDECREF (atexit_register);
+        Py_DECREF (PgExc_BufferError);
         DECREF_MOD (module);
         MODINIT_ERROR;
     }
         quit = PyObject_GetAttrString (module, "quit");
         if (quit == NULL) {  /* assertion */
             Py_DECREF (atexit_register);
+            Py_DECREF (PgExc_BufferError);
             DECREF_MOD (module);
             MODINIT_ERROR;
         }
         Py_DECREF (quit);
         if (rval == NULL) {
             DECREF_MOD (module);
+            Py_DECREF (PgExc_BufferError);
             MODINIT_ERROR;
         }
         Py_DECREF (rval);

File src/bufferproxy.c

 #include "pgcompat.h"
 #include "pgbufferproxy.h"
 
+/* No build will support the new and old buffer protocols simultaneously. */
+#if !PG_ENABLE_NEWBUF && HAVE_OLD_BUFPROTO
+#define PG_ENABLE_OLDBUF 1
+#else
+#define PG_ENABLE_OLDBUF 0
+#endif
+
+
 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
 #define BUFPROXY_MY_ENDIAN '<'
 #define BUFPROXY_OTHER_ENDIAN '>'
     PyObject_HEAD
     PyObject *obj;                             /* Wrapped object              */
     Pg_buffer *view_p;                         /* For array interface export  */
+#if PG_ENABLE_OLDBUF
+    Py_ssize_t segcount;                       /* bf_getsegcount return value */
+    Py_ssize_t seglen;                         /* bf_getsegcount len argument */
+#endif
     pg_getbufferfunc get_buffer;               /* Pg_buffer get callback      */
     PyObject *dict;                            /* Allow arbitrary attributes  */
     PyObject *weakrefs;                        /* Reference cycles can happen */
 } Pg_buffer_d;
 
 static int PgBufproxy_Trip(PyObject *);
+static Py_buffer *_proxy_get_view (PgBufproxyObject*);
 
 /* $$ Transitional stuff */
 #warning Transitional stuff: must disappear!
         }
         proxy->view_p = view_p;
     }
+    assert(((Py_buffer *)view_p)->len && ((Py_buffer *)view_p)->itemsize);
     return (Py_buffer *)view_p;
 }
 
     {0, 0, 0, 0, 0}
 };
 
+
+#if PG_ENABLE_NEWBUF || PG_ENABLE_OLDBUF
+
 #if PG_ENABLE_NEWBUF
 static int
 proxy_getbuffer(PgBufproxyObject *self, Py_buffer *view_p, int flags)
     PyMem_Free(view_p->internal);
 }
 
-static PyBufferProcs proxy_bufferprocs = {
-    (getbufferproc)proxy_getbuffer,
-    (releasebufferproc)proxy_releasebuffer
-};
+#endif /* #if PG_ENABLE_NEWBUF */
 
-#else
 
-static Py_ssize_t
-proxy_getreadbuf(PgBufproxyObject *buffer, Py_ssize_t _index, const void **ptr)
-{
-    NOTIMPLEMENTED(-1);
+#if PG_ENABLE_OLDBUF
+static int
+_is_byte_view(Py_buffer *view_p) {
+    const char *format = view_p->format;
+
+    /* Conditional ||'s */
+    return ((!format)                                                   ||
+            (format[0] == 'B' && format[1] == '\0')                     ||
+            (format[0] == '=' && format[1] == 'B' && format[2] == '\0') ||
+            (format[0] == '<' && format[1] == 'B' && format[2] == '\0') ||
+            (format[0] == '>' && format[1] == 'B' && format[2] == '\0') ||
+            (format[0] == '@' && format[1] == 'B' && format[2] == '\0') ||
+            (format[0] == '!' && format[1] == 'B' && format[2] == '\0')    );
 }
 
 static Py_ssize_t
-proxy_getwritebuf(PgBufproxyObject *buffer, Py_ssize_t _index, const void **ptr)
+proxy_getreadbuf(PgBufproxyObject *self, Py_ssize_t _index, void **ptr)
 {
-    NOTIMPLEMENTED(-1);
+    Py_buffer *view_p = (Py_buffer *)self->view_p;
+    Py_ssize_t offset = 0;
+    Py_ssize_t dim;
+
+    if (_index < 0 || _index >= self->segcount) {
+        if (_index == 0 && self->segcount == 0) {
+            *ptr = 0;
+            return 0;
+        }
+        PyErr_SetString(PyExc_IndexError, "segment index out of range");
+        return -1;
+    }
+    if (!view_p) {
+        *ptr = 0;
+        return 0;
+    }
+    if (self->segcount == 1) {
+        assert(_index == 0);
+        *ptr = view_p->buf;
+        return view_p->len;
+    }
+    /* Segments will be strictly in C contiguous order, which may
+       differ from the actual order in memory. It can affect buffer
+       copying. This may never be an issue, though, since Python
+       never directly supported multi-segment buffers. And besides,
+       the old buffer is deprecated. */
+    for (dim = view_p->ndim - 1; dim != -1; --dim) {
+        offset += _index % view_p->shape[dim] * view_p->strides[dim];
+        _index /= view_p->shape[dim];
+    }
+    *ptr = (char *)view_p->buf + offset;
+    return view_p->itemsize;
 }
 
 static Py_ssize_t
-proxy_getsegcount(PgBufproxyObject *buffer, Py_ssize_t *lenp)
+proxy_getwritebuf(PgBufproxyObject *self, Py_ssize_t _index, void **ptr)
 {
-    NOTIMPLEMENTED(-1);
+    void *p;
+    Py_ssize_t seglen = proxy_getreadbuf(self, _index, &p);
+
+    if (seglen < 0) {
+        return -1;
+    }
+    if (seglen > 0 && ((Py_buffer *)self->view_p)->readonly) /* cond. && */ {
+        PyErr_SetString(PyExc_ValueError, "buffer is not writeable");
+        return -1;
+    }
+    *ptr = p;
+    return seglen;
 }
 
+static Py_ssize_t
+proxy_getsegcount(PgBufproxyObject *self, Py_ssize_t *lenp)
+{
+    Py_buffer *view_p = _proxy_get_view(self);
+
+    if (!view_p) {
+        PyErr_Clear();
+        self->seglen = 0;
+        self->segcount = 0;
+    }
+    else if (view_p->ndim == 0 ||
+             (view_p->ndim == 1 && _is_byte_view(view_p))) {
+        self->seglen = view_p->len;
+        self->segcount = 1;
+    }
+    else {
+        self->seglen = view_p->len;
+        self->segcount = view_p->len / view_p->itemsize;
+    }
+    if (lenp) {
+        *lenp = self->seglen;
+    }
+    return self->segcount;
+}
+
+#endif /* #if PG_ENABLE_OLDBUF */
+
+
+#define PROXY_BUFFERPROCS (&proxy_bufferprocs)
+
 static PyBufferProcs proxy_bufferprocs = {
+#if PG_ENABLE_OLDBUF
     (readbufferproc)proxy_getreadbuf,
     (writebufferproc)proxy_getwritebuf,
     (segcountproc)proxy_getsegcount,
     0
-#if PY_VERSION_HEX >= 0x02060000
-    ,
+#elif HAVE_OLD_BUFPROTO
+    0,
+    0,
+    0,
+    0
+#endif
+
+#if HAVE_OLD_BUFPROTO && HAVE_NEW_BUFPROTO
+     ,
+#endif
+
+#if PG_ENABLE_NEWBUF
+    (getbufferproc)proxy_getbuffer,
+    (releasebufferproc)proxy_releasebuffer
+#elif HAVE_NEW_BUFPROTO
     0,
     0
 #endif
 };
 
-#endif /* #if PG_ENABLE_NEWBUF */
+#endif /* #if PG_ENABLE_NEWBUF || PG_ENABLE_OLDBUF */
+
+
+#if !defined(PROXY_BUFFERPROCS)
+#define PROXY_BUFFERPROCS 0
+#endif
 
 #define PROXY_TPFLAGS \
     (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC)
     0,                          /* tp_str */
     0,                          /* tp_getattro */
     0,                          /* tp_setattro */
-    &proxy_bufferprocs,         /* tp_as_buffer */
+    PROXY_BUFFERPROCS,          /* tp_as_buffer */
     PROXY_TPFLAGS,              /* tp_flags */
     "Object bufproxy as an array struct\n",
     (traverseproc)proxy_traverse,  /* tp_traverse */
 
 /**** Module methods ***/
 
+#if PG_ENABLE_OLDBUF
+static PyObject *
+get_read_buffer(PyObject *self, PyObject *args, PyObject *kwds)
+{
+    long segment = 0;
+    PyObject *obj = 0;
+    Py_ssize_t len = 0;
+    void *ptr = 0;
+    readbufferproc getreadbuffer = 0;
+    static char *keywords[] = {"obj", "segment", 0};
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "Ol", keywords,
+                                     &obj, &segment)) {
+        return 0;
+    }
+    if (!Py_TYPE(obj)->tp_as_buffer) {
+        PyErr_SetString(PyExc_ValueError, "No tp_as_buffer struct");
+        return 0;
+    }
+    getreadbuffer = Py_TYPE(obj)->tp_as_buffer->bf_getreadbuffer;
+    if (!getreadbuffer) {
+        PyErr_SetString(PyExc_ValueError, "No bf_getreadbuffer slot function");
+        return 0;
+    }
+    len = getreadbuffer(obj, segment, &ptr);
+    if (len < 0) {
+        return 0;
+    }
+    return Py_BuildValue("ll", (long)len, (long)ptr);
+}
+
+static PyObject *
+get_write_buffer(PyObject *self, PyObject *args, PyObject *kwds)
+{
+    long segment = 0;
+    PyObject *obj = 0;
+    Py_ssize_t len = 0;
+    void *ptr = 0;
+    writebufferproc getwritebuffer = 0;
+    static char *keywords[] = {"obj", "segment", 0};
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "Ol", keywords,
+                                     &obj, &segment)) {
+        return 0;
+    }
+    if (!Py_TYPE(obj)->tp_as_buffer) {
+        PyErr_SetString(PyExc_ValueError, "No tp_as_buffer struct");
+        return 0;
+    }
+    getwritebuffer = Py_TYPE(obj)->tp_as_buffer->bf_getwritebuffer;
+    if (!getwritebuffer) {
+        PyErr_SetString(PyExc_ValueError, "No bf_getwritebuffer slot function");
+        return 0;
+    }
+    len = getwritebuffer(obj, segment, &ptr);
+    if (len < 0) {
+        return 0;
+    }
+    return Py_BuildValue("ll", (long)len, (long)ptr);
+}
+
+static PyObject *
+get_segcount(PyObject *self, PyObject *obj)
+{
+    Py_ssize_t segcount = 0;
+    Py_ssize_t len = 0;
+    segcountproc getsegcount = 0;
+
+    if (!Py_TYPE(obj)->tp_as_buffer) {
+        PyErr_SetString(PyExc_ValueError, "No tp_as_buffer struct");
+        return 0;
+    }
+    getsegcount = Py_TYPE(obj)->tp_as_buffer->bf_getsegcount;
+    if (!getsegcount) {
+        PyErr_SetString(PyExc_ValueError, "No bf_getsegcount slot function");
+        return 0;
+    }
+    segcount = getsegcount(obj, &len);
+    return Py_BuildValue("ll", (long)segcount, (long)len);
+}
+
+#endif
+
 static PyMethodDef bufferproxy_methods[] = {
+#if PG_ENABLE_OLDBUF
+    {"get_read_buffer", (PyCFunction)get_read_buffer,
+     METH_VARARGS | METH_KEYWORDS, "call bf_getreadbuffer slot function"},
+    {"get_write_buffer", (PyCFunction)get_write_buffer,
+     METH_VARARGS | METH_KEYWORDS, "call bf_getwritebuffer slot function"},
+    {"get_segcount", (PyCFunction)get_segcount,
+     METH_O, "call bf_getsegcount slot function"},
+#endif
     {0, 0, 0, 0}
 };
 

File src/pygame.h

 #define VIEW_F_ORDER       4
 
 #define PYGAMEAPI_BASE_FIRSTSLOT 0
-#define PYGAMEAPI_BASE_NUMSLOTS 20
+#define PYGAMEAPI_BASE_NUMSLOTS 21
 #ifndef PYGAMEAPI_BASE_INTERNAL
 #define PyExc_SDLError ((PyObject*)PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT])
 
     (*(int(*)(Pg_buffer*, PyObject*, int))                              \
      PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 19])
 
+#define PgExc_BufferError                                               \
+    ((PyObject*)PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 20])
+
 #define import_pygame_base() IMPORT_PYGAME_MODULE(base, BASE)
 #endif
 

File src/surface.c

     view_p->buf = surface->pixels;
     view_p->itemsize = 1;
     view_p->len = surface->pitch * surface->h;
+    view_p->ndim = 1;
     view_p->readonly = 0;
     if (flags & PyBUF_FORMAT) {
         view_p->format = FormatUint8;
         return _get_buffer_0D (obj, pg_view_p, flags);
     }
     if (flags == PyBUF_SIMPLE) {
-        PyErr_Format (PyExc_BufferError,
+        PyErr_Format (PgExc_BufferError,
                       "PyBUF_SIMPLE flag unsupported for a "
                       "%i bytes-per-pixel 1D surface view", itemsize);
         return -1;
     }
-    if (!(flags & PyBUF_ND)) {
-        PyErr_Format (PyExc_BufferError,
+    if ((flags & PyBUF_ND) != PyBUF_ND) {
+        PyErr_Format (PgExc_BufferError,
                       "PyBUF_ND flag required for a "
                       "%i bytes-per-pixel 1D surface view", itemsize);
         return -1;
     }
-    if (!(flags & PyBUF_FORMAT)) {
-        PyErr_Format (PyExc_BufferError,
+    if ((flags & PyBUF_FORMAT) != PyBUF_FORMAT) {
+        PyErr_Format (PgExc_BufferError,
                       "PyBUF_FORMAT required for a 1D view of a "
                       "%i bytes-per-pixel surface", itemsize);
         return -1;
     view_p->ndim = 1;
     view_p->readonly = 0;
     view_p->len = surface->w * surface->h * itemsize;
-    view_p->shape = (Py_ssize_t *)&view_p->internal;
-    view_p->strides = (flags & PyBUF_STRIDES) ? &itemsize : 0;
+    view_p->shape[0] = surface->w * surface->h;
+    if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) {
+        view_p->strides[0] = itemsize;
+    }
     view_p->suboffsets = 0;
     Py_INCREF (obj);
     view_p->obj = obj;
 
     view_p->obj = 0;
     if ((flags & PyBUF_RECORDS) != PyBUF_RECORDS) {
-        PyErr_SetString (PyExc_BufferError,
+        PyErr_SetString (PgExc_BufferError,
                          "A PyBUF_RECORDS flag is required for a "
                          "2D surface view");
         return -1;
     }
     if ((flags & PyBUF_C_CONTIGUOUS) == PyBUF_C_CONTIGUOUS) {
-        PyErr_SetString (PyExc_BufferError,
+        PyErr_SetString (PgExc_BufferError,
                          "A 2D surface view is not C contiguous");
         return -1;
     }
     if ((flags & PyBUF_F_CONTIGUOUS) == PyBUF_F_CONTIGUOUS &&
         surface->pitch != surface->w * itemsize) {
-        PyErr_SetString (PyExc_BufferError,
+        PyErr_SetString (PgExc_BufferError,
                          "This 2D surface view is not F contiguous");
         return -1;
     }
     if ((flags & PyBUF_ANY_CONTIGUOUS) == PyBUF_ANY_CONTIGUOUS &&
         surface->pitch != surface->w * itemsize) {
-        PyErr_SetString (PyExc_BufferError,
+        PyErr_SetString (PgExc_BufferError,
                          "This 2D surface view is not contiguous");
         return -1;
     }
 
     view_p->obj = 0;
     if ((flags & PyBUF_RECORDS) != PyBUF_RECORDS) {
-        PyErr_SetString (PyExc_BufferError,
+        PyErr_SetString (PgExc_BufferError,
                          "A PyBUF_RECORDS flag is required for a "
                          "2D surface view");
         return -1;
     if ((flags & PyBUF_C_CONTIGUOUS) == PyBUF_C_CONTIGUOUS ||
         (flags & PyBUF_F_CONTIGUOUS) == PyBUF_F_CONTIGUOUS ||
         (flags & PyBUF_ANY_CONTIGUOUS) == PyBUF_ANY_CONTIGUOUS) {
-        PyErr_SetString (PyExc_BufferError,
+        PyErr_SetString (PgExc_BufferError,
                          "A 3D surface view is not contiguous");
         return -1;
     }
 
     view_p->obj = 0;
     if ((flags & PyBUF_RECORDS) != PyBUF_RECORDS) {
-        PyErr_SetString (PyExc_BufferError,
+        PyErr_SetString (PgExc_BufferError,
                          "A PyBUF_RECORDS flag is required for a "
                          "surface colorplane view");
         return -1;
     if ((flags & PyBUF_C_CONTIGUOUS) == PyBUF_C_CONTIGUOUS ||
         (flags & PyBUF_F_CONTIGUOUS) == PyBUF_F_CONTIGUOUS ||
         (flags & PyBUF_ANY_CONTIGUOUS) == PyBUF_ANY_CONTIGUOUS) {
-        PyErr_SetString (PyExc_BufferError,
+        PyErr_SetString (PgExc_BufferError,
                          "A surface colorplane view is not contiguous");
         return -1;
     }
         return -1;
     }
     if (!PySurface_LockBy (surf, consumer)) {
-        PyErr_Format (PyExc_BufferError,
+        PyErr_Format (PgExc_BufferError,
                       "Unable to lock <%s at %p> by <%s at %p>",
                       Py_TYPE(surf)->tp_name, (void *)surf,
                       Py_TYPE(consumer)->tp_name, (void *)consumer);

File test/bufferproxy_test.py

         self.assertEqual(r[:2], '*<')
         self.assertEqual(r[-2:], '>*')
 
-    def test_newbuf_arg(self):
+    if sys.version_info >= (3,):
+        def test_newbuf_arg(self):
+            self.NEWBUF_test_newbuf_arg()
+
+    def NEWBUF_test_newbuf_arg(self):
         from array import array
 
         raw = as_bytes('abc\x00def')
             self.assertEqual(d['data'], (b_address, False))
         finally:
             d = None
-    if sys.version_info < (3,):
-        del test_newbuf_arg
+
+    try:
+        pygame.bufferproxy.get_segcount
+    except AttributeError:
+        pass
+    else:
+        def test_oldbuf_arg(self):
+            self.OLDBUF_test_oldbuf_arg()
+
+    def OLDBUF_test_oldbuf_arg(self):
+        from pygame.bufferproxy import (get_segcount, get_read_buffer,
+                                        get_write_buffer)
+
+        content = as_bytes('\x01\x00\x00\x02') * 12
+        memory = ctypes.create_string_buffer(content)
+        memaddr = ctypes.addressof(memory)
+        def raise_exception(o):
+            raise ValueError("An exception")
+
+        bf = BufferProxy({'shape': (len(content),),
+                          'typestr': '|u1',
+                          'data': (memaddr, False),
+                          'strides': (1,)})
+        seglen, segaddr = get_read_buffer(bf, 0)
+        self.assertEqual(segaddr, 0)
+        self.assertEqual(seglen, 0)
+        seglen, segaddr = get_write_buffer(bf, 0)
+        self.assertEqual(segaddr, 0)
+        self.assertEqual(seglen, 0)
+        segcount, buflen = get_segcount(bf)
+        self.assertEqual(segcount, 1)
+        self.assertEqual(buflen, len(content))
+        seglen, segaddr = get_read_buffer(bf, 0)
+        self.assertEqual(segaddr, memaddr)
+        self.assertEqual(seglen, len(content))
+        seglen, segaddr = get_write_buffer(bf, 0)
+        self.assertEqual(segaddr, memaddr)
+        self.assertEqual(seglen, len(content))
+        
+        bf = BufferProxy({'shape': (len(content),),
+                          'typestr': '|u1',
+                          'data': (memaddr, True),
+                          'strides': (1,)})
+        segcount, buflen = get_segcount(bf)
+        self.assertEqual(segcount, 1)
+        self.assertEqual(buflen, len(content))
+        seglen, segaddr = get_read_buffer(bf, 0)
+        self.assertEqual(segaddr, memaddr)
+        self.assertEqual(seglen, len(content))
+        self.assertRaises(ValueError, get_write_buffer, bf, 0)
+
+        bf = BufferProxy({'shape': (len(content),),
+                          'typestr': '|u1',
+                          'data': (memaddr, True),
+                          'strides': (1,),
+                          'before': raise_exception})
+        segcount, buflen = get_segcount(bf)
+        self.assertEqual(segcount, 0)
+        self.assertEqual(buflen, 0)
+
+        bf = BufferProxy({'shape': (3, 4),
+                          'typestr': '|u4',
+                          'data': (memaddr, True),
+                          'strides': (12, 4)})
+        segcount, buflen = get_segcount(bf)
+        self.assertEqual(segcount, 3 * 4)
+        self.assertEqual(buflen, 3 * 4 * 4)
+        for i in range(0, 4):
+            seglen, segaddr = get_read_buffer(bf, i)
+            self.assertEqual(segaddr, memaddr + i * 4)
+            self.assertEqual(seglen, 4)
 
 class BufferProxyLegacyTest(unittest.TestCase):
     content = as_bytes('\x01\x00\x00\x02') * 12

File test/surface_test.py

         gc.collect()
         self.assertTrue(weak_s() is None)
 
+    try:
+        pygame.bufferproxy.get_segcount
+    except AttributeError:
+        pass
+    else:
+        def test_get_buffer_oldbuf(self):
+            self.OLDBUF_get_buffer_oldbuf()
+
+    def OLDBUF_get_buffer_oldbuf(self):
+        from pygame.bufferproxy import get_segcount, get_write_buffer
+
+        s = pygame.Surface((2, 4), pygame.SRCALPHA, 32)
+        v = s.get_buffer()
+        segcount, buflen = get_segcount(v)
+        self.assertEqual(segcount, 1)
+        self.assertEqual(buflen, s.get_pitch() * s.get_height())
+        seglen, segaddr = get_write_buffer(v, 0)
+        self.assertEqual(segaddr, s._pixels_address)
+        self.assertEqual(seglen, buflen)
+        v = s.get_buffer('1')
+        segcount, buflen = get_segcount(v)
+        self.assertEqual(segcount, 8)
+        self.assertEqual(buflen, s.get_pitch() * s.get_height())
+        seglen, segaddr = get_write_buffer(v, 7)
+        self.assertEqual(segaddr, s._pixels_address + s.get_bytesize() * 7)
+        self.assertEqual(seglen, s.get_bytesize())
+        
     def test_set_colorkey(self):
 
         # __doc__ (as of 2008-06-25) for pygame.surface.Surface.set_colorkey: