[Python-checkins] r68354 - in sandbox/trunk/io-c: _bufferedio.c _iomodule.h io.c

antoine.pitrou python-checkins at python.org
Tue Jan 6 02:04:25 CET 2009


Author: antoine.pitrou
Date: Tue Jan  6 02:04:25 2009
New Revision: 68354

Log:
Buffered{Reader,Writer,Random} overhaul: use a shared buffer and optimize mixed
reads and writes by avoiding spurious flushes.



Modified:
   sandbox/trunk/io-c/_bufferedio.c
   sandbox/trunk/io-c/_iomodule.h
   sandbox/trunk/io-c/io.c

Modified: sandbox/trunk/io-c/_bufferedio.c
==============================================================================
--- sandbox/trunk/io-c/_bufferedio.c	(original)
+++ sandbox/trunk/io-c/_bufferedio.c	Tue Jan  6 02:04:25 2009
@@ -39,11 +39,11 @@
     if (data == NULL)
         goto error;
 
-        if (!PyBytes_Check(data)) {
-            Py_DECREF(data);
-            PyErr_SetString(PyExc_TypeError, "read() should return bytes");
+    if (!PyBytes_Check(data)) {
+        Py_DECREF(data);
+        PyErr_SetString(PyExc_TypeError, "read() should return bytes");
         goto error;
-        }
+    }
 
     len = Py_SIZE(data);
     memcpy(buf.buf, PyBytes_AS_STRING(data), len);
@@ -110,27 +110,30 @@
 
     PyObject *raw;
     int ok;    /* Initialized? */
+    int readable;
+    int writable;
 
-    /* Position inside the raw stream (-1 if unknown). */
-    Py_ssize_t raw_pos;
+    /* Absolute position inside the raw stream (-1 if unknown). */
+    Py_ssize_t abs_pos;
 
     /* A static buffer of size `buffer_size` */
-    unsigned char *read_buf;
-    /* Current reading pos in the buffer. */
-    Py_ssize_t read_pos;
+    char *buffer;
+    /* Current logical position in the buffer. */
+    Py_ssize_t pos;
+    /* Position of the raw stream in the buffer. */
+    Py_ssize_t raw_pos;
+
     /* Just after the last buffered byte in the buffer, or -1 if the buffer
-       is invalid.
-       Should most often be equal to buffer_size for "normal" blocking I/O. */
+       isn't ready for reading. */
     Py_ssize_t read_end;
-    PyThread_type_lock read_lock;
 
-    /* A static buffer of size `buffer_size` */
-    unsigned char *write_buf;
     /* Just after the last byte actually written */
     Py_ssize_t write_pos;
-    /* Just after the last buffered byte */
+    /* Just after the last byte waiting to be written, or -1 if the buffer
+       isn't ready for writing. */
     Py_ssize_t write_end;
-    PyThread_type_lock write_lock;
+
+    PyThread_type_lock lock;
 
     Py_ssize_t buffer_size;
     Py_ssize_t buffer_mask;
@@ -139,23 +142,39 @@
     PyObject *weakreflist;
 } BufferedObject;
 
-/* These macros protect the BufferedObject against concurrent operations. */
-
-#define ENTER_BUFFERED_READER(self) \
-    Py_BEGIN_ALLOW_THREADS \
-    PyThread_acquire_lock(self->read_lock, 1); \
-    Py_END_ALLOW_THREADS
+/*
+    Implementation notes:
+    
+    * BufferedReader, BufferedWriter and BufferedRandom try to share most
+      methods (this is helped by the members `readable` and `writable`, which
+      are initialized in the respective constructors)
+    * They also share a single buffer for reading and writing. This enables
+      interleaved reads and writes without flushing. It also makes the logic
+      a bit trickier to get right.
+    * The absolute position of the raw stream is cached, if possible, in the
+      `abs_pos` member. It must be updated every time an operation is done
+      on the raw stream. If not sure, it can be reinitialized by calling
+      _Buffered_raw_tell(), which queries the raw stream (_Buffered_raw_seek()
+      also does it). To read it, use RAW_TELL().
+    * Three helpers, _BufferedReader_raw_read, _BufferedWriter_raw_write and
+      _BufferedWriter_flush_unlocked do a lot of useful housekeeping.
+
+    NOTE: we should try to maintain block alignment of reads and writes to the
+    raw stream (according to the buffer size), but for now it is only done
+    in read() and friends.
+    
+    XXX: method naming is a bit messy.
+*/
 
-#define LEAVE_BUFFERED_READER(self) \
-    PyThread_release_lock(self->read_lock);
+/* These macros protect the BufferedObject against concurrent operations. */
 
-#define ENTER_BUFFERED_WRITER(self) \
+#define ENTER_BUFFERED(self) \
     Py_BEGIN_ALLOW_THREADS \
-    PyThread_acquire_lock(self->write_lock, 1); \
+    PyThread_acquire_lock(self->lock, 1); \
     Py_END_ALLOW_THREADS
 
-#define LEAVE_BUFFERED_WRITER(self) \
-    PyThread_release_lock(self->write_lock);
+#define LEAVE_BUFFERED(self) \
+    PyThread_release_lock(self->lock);
 
 #define CHECK_INITIALIZED(self) \
     if (self->ok <= 0) { \
@@ -172,10 +191,28 @@
     }
 
 #define VALID_READ_BUFFER(self) \
-    (self->read_end != -1)
+    (self->readable && self->read_end != -1)
 
-#define AVAILABLE_BYTES(self) \
-    (VALID_READ_BUFFER(self) ? (self->read_end - self->read_pos) : 0)
+#define VALID_WRITE_BUFFER(self) \
+    (self->writable && self->write_end != -1)
+
+#define ADJUST_POSITION(self, _new_pos) \
+    do { \
+        self->pos = _new_pos; \
+        if (VALID_READ_BUFFER(self) && self->read_end < self->pos) \
+            self->read_end = self->pos; \
+    } while(0)
+
+#define READAHEAD(self) \
+    ((self->readable && VALID_READ_BUFFER(self)) \
+        ? (self->read_end - self->pos) : 0)
+
+#define RAW_OFFSET(self) \
+    (((VALID_READ_BUFFER(self) || VALID_WRITE_BUFFER(self)) \
+        && self->raw_pos >= 0) ? self->raw_pos - self->pos : 0)
+
+#define RAW_TELL(self) \
+    (self->abs_pos != -1 ? self->abs_pos : _Buffered_raw_tell(self))
 
 #define MINUS_LAST_BLOCK(self, size) \
     (self->buffer_mask ? \
@@ -186,28 +223,38 @@
 static void
 BufferedObject_dealloc(BufferedObject *self)
 {
+    PyObject *res;
+    /* XXX this is inelegant */
+    if (Py_TYPE(self)->tp_del == NULL) {
+        /* We need to resurrect the object as calling close() can invoke
+           arbitrary code. */
+        ((PyObject *) self)->ob_refcnt++;
+        res = PyObject_CallMethodObjArgs((PyObject *) self, _PyIO_str_close,
+                                          NULL);
+        if (res == NULL) {
+            /* XXX dump exception on terminal?
+               But IOBase.__del__ prefers to remain silent... */
+            PyErr_Clear();
+        }
+        Py_XDECREF(res);
+        if (--((PyObject *) self)->ob_refcnt != 0)
+            return;
+    }
     if (self->weakreflist != NULL)
         PyObject_ClearWeakRefs((PyObject *)self);
     Py_CLEAR(self->raw);
-    if (self->read_buf) {
-        PyMem_Free(self->read_buf);
-        self->write_buf = NULL;
-    }
-    if (self->write_buf) {
-        PyMem_Free(self->write_buf);
-        self->write_buf = NULL;
-    }
-    if (self->read_lock) {
-        PyThread_free_lock(self->read_lock);
-        self->read_lock = NULL;
-    }
-    if (self->write_lock) {
-        PyThread_free_lock(self->write_lock);
-        self->write_lock = NULL;
+    if (self->buffer) {
+        PyMem_Free(self->buffer);
+        self->buffer = NULL;
+    }
+    if (self->lock) {
+        PyThread_free_lock(self->lock);
+        self->lock = NULL;
     }
     Py_CLEAR(self->dict);
+    Py_TYPE(self)->tp_free((PyObject *)self);
 }
-
+
 
 /*
  * _BufferedIOMixin methods
@@ -232,6 +279,8 @@
      * and a flush may be necessary to synch both views of the current
      *  file state.
      */
+    /* FIXME: raw objects don't have flush()
+     */
     res = PyObject_CallMethodObjArgs(self->raw, _PyIO_str_flush, NULL);
     if (res == NULL)
         return NULL;
@@ -245,7 +294,7 @@
     /* XXX: Should seek() be used, instead of passing the position
      * XXX  directly to truncate?
      */
-    res = PyObject_CallMethod(self->raw, "truncate", "O", pos);
+    res = PyObject_CallMethodObjArgs(self->raw, _PyIO_str_truncate, pos, NULL);
     Py_DECREF(pos);
 
     return res;
@@ -355,6 +404,20 @@
     return PyObject_CallMethodObjArgs(self->raw, _PyIO_str_isatty, NULL);
 }
 
+
+/* Forward decls */
+static PyObject *
+_BufferedWriter_flush_unlocked(BufferedObject *, int);
+static void
+_BufferedReader_reset_buf(BufferedObject *self);
+static void
+_BufferedWriter_reset_buf(BufferedObject *self);
+static PyObject *
+_BufferedReader_peek_unlocked(BufferedObject *self, Py_ssize_t);
+static PyObject *
+_BufferedReader_read_unlocked(BufferedObject *self, Py_ssize_t);
+
+
 /*
  * Helpers
  */
@@ -388,7 +451,7 @@
         return -1;
     n = PyNumber_AsSsize_t(res, PyExc_ValueError);
     Py_DECREF(res);
-    self->raw_pos = n;
+    self->abs_pos = n;
     /* TODO: sanity check (n >= 0) */
     return n;
 }
@@ -403,7 +466,7 @@
         return -1;
     n = PyNumber_AsSsize_t(res, PyExc_ValueError);
     Py_DECREF(res);
-    self->raw_pos = n;
+    self->abs_pos = n;
     /* TODO: sanity check (n >= 0) */
     return n;
 }
@@ -417,7 +480,20 @@
             "buffer size must be strictly positive");
         return -1;
     }
+    if (self->buffer)
+        PyMem_Free(self->buffer);
+    self->buffer = PyMem_Malloc(self->buffer_size);
+    if (self->buffer == NULL) {
+        PyErr_NoMemory();
+        return -1;
+    }
+    self->lock = PyThread_allocate_lock();
+    if (self->lock == NULL) {
+        PyErr_SetString(PyExc_RuntimeError, "can't allocate read lock");
+        return -1;
+    }
     /* Find out whether buffer_size is a power of 2 */
+    /* XXX is this optimization useful? */
     for (n = self->buffer_size - 1; n & 1; n >>= 1)
         ;
     if (n == 0)
@@ -429,6 +505,264 @@
     return 0;
 }
 
+/*
+ * Shared methods and wrappers
+ */
+
+static PyObject *
+Buffered_flush(BufferedObject *self, PyObject *args)
+{
+    PyObject *res;
+
+    CHECK_INITIALIZED(self)
+    if (BufferedIOMixin_closed(self)) {
+        PyErr_SetString(PyExc_ValueError, "flush of closed file");
+        return NULL;
+    }
+
+    ENTER_BUFFERED(self)
+    res = _BufferedWriter_flush_unlocked(self, 0);
+    if (res != NULL && self->readable) {
+        /* Rewind the raw stream so that its position corresponds to
+           the current logical position. */
+        Py_ssize_t n;
+        n = _Buffered_raw_seek(self, -RAW_OFFSET(self), 1);
+        if (n == -1)
+            Py_CLEAR(res);
+        _BufferedReader_reset_buf(self);
+    }
+    LEAVE_BUFFERED(self)
+
+    return res;
+}
+
+static PyObject *
+Buffered_peek(BufferedObject *self, PyObject *args)
+{
+    Py_ssize_t n = 0;
+    PyObject *res = NULL;
+
+    CHECK_INITIALIZED(self)
+    if (!PyArg_ParseTuple(args, "|n:peek", &n)) {
+        return NULL;
+    }
+
+    ENTER_BUFFERED(self)
+    
+    if (self->writable) {
+        res = _BufferedWriter_flush_unlocked(self, 1);
+        if (res == NULL)
+            goto end;
+        Py_CLEAR(res);
+    }
+    res = _BufferedReader_peek_unlocked(self, n);
+
+end:
+    LEAVE_BUFFERED(self)
+    return res;
+}
+
+static PyObject *
+Buffered_read(BufferedObject *self, PyObject *args)
+{
+    Py_ssize_t n = -1;
+    PyObject *res;
+
+    CHECK_INITIALIZED(self)
+    if (!PyArg_ParseTuple(args, "|n:read", &n)) {
+        return NULL;
+    }
+
+    if (BufferedIOMixin_closed(self)) {
+        PyErr_SetString(PyExc_ValueError, "read of closed file");
+        return NULL;
+    }
+
+    ENTER_BUFFERED(self)
+    res = _BufferedReader_read_unlocked(self, n);
+    LEAVE_BUFFERED(self)
+
+    return res;
+}
+
+static PyObject *
+Buffered_read1(BufferedObject *self, PyObject *args)
+{
+    Py_ssize_t n, have;
+    PyObject *res;
+
+    CHECK_INITIALIZED(self)
+    if (!PyArg_ParseTuple(args, "n:read1", &n)) {
+        return NULL;
+    }
+
+    /* TODO: n < 0 should raise an error (?) */
+    if (n <= 0)
+        return PyBytes_FromStringAndSize(NULL, 0);
+
+    ENTER_BUFFERED(self)
+    
+    if (self->writable) {
+        res = _BufferedWriter_flush_unlocked(self, 1);
+        if (res == NULL)
+            goto end;
+        Py_CLEAR(res);
+    }
+
+    /* Return up to n bytes.  If at least one byte is buffered, we
+       only return buffered bytes.  Otherwise, we do one raw read. */
+
+    /* XXX: this mimicks the io.py implementation but is probably wrong.
+       If we need to read from the raw stream, then we could actually read
+       all `n` bytes asked by the caller (and possibly more, so as to fill
+       our buffer for the next reads). */
+
+    res = _BufferedReader_peek_unlocked(self, 1);
+    if (res == NULL)
+        goto end;
+    Py_CLEAR(res);
+
+    have = self->read_end - self->pos;
+    if (n > have)
+        n = have;
+
+    res = _BufferedReader_read_unlocked(self, n);
+
+end:
+    LEAVE_BUFFERED(self)
+    return res;
+}
+
+static PyObject *
+Buffered_readinto(BufferedObject *self, PyObject *args)
+{
+    PyObject *res = NULL;
+
+    CHECK_INITIALIZED(self)
+    
+    /* TODO: use raw.readinto() instead! */
+    if (self->writable) {
+        ENTER_BUFFERED(self)
+        res = _BufferedWriter_flush_unlocked(self, 0);
+        LEAVE_BUFFERED(self)
+        if (res == NULL)
+            goto end;
+        Py_DECREF(res);
+    }
+    res = BufferedIOBase_readinto((PyObject *)self, args);
+
+end:
+    return res;
+}
+
+static PyObject *
+Buffered_tell(BufferedObject *self, PyObject *args)
+{
+    Py_ssize_t pos;
+
+    CHECK_INITIALIZED(self)
+    pos = _Buffered_raw_tell(self);
+    if (pos == -1)
+        return NULL;
+    pos -= RAW_OFFSET(self);
+    /* TODO: sanity check (pos >= 0) */
+    return PyLong_FromSsize_t(pos);
+}
+
+static PyObject *
+Buffered_seek(BufferedObject *self, PyObject *args)
+{
+    Py_ssize_t current, target, avail, n;
+    int whence = 0;
+    PyObject *res = NULL;
+
+    CHECK_INITIALIZED(self)
+    if (!PyArg_ParseTuple(args, "n|i:seek", &target, &whence)) {
+        return NULL;
+    }
+    /* TODO: sanity check whence value */
+
+    if (whence != 2 && self->readable) {
+        /* Check if seeking leaves us inside the current buffer,
+           so as to return quickly if possible.
+           Don't know how to do that when whence == 2, though. */
+        current = RAW_TELL(self);
+        avail = READAHEAD(self);
+        if (avail > 0) {
+            Py_ssize_t offset;
+            if (whence == 0)
+                offset = target - (current - RAW_OFFSET(self));
+            else
+                offset = target;
+            if (offset >= -self->pos && offset <= avail) {
+                self->pos += offset;
+                res = PyLong_FromSsize_t(current - avail + offset);
+                goto end;
+            }
+        }
+    }
+
+    /* Fallback: invoke raw seek() method and clear buffer */
+    if (self->writable) {
+        res = _BufferedWriter_flush_unlocked(self, 0);
+        if (res == NULL)
+            goto end;
+        Py_CLEAR(res);
+    }
+
+    /* TODO: align on block boundary and read buffer if needed? */
+    if (whence == 1)
+        target -= RAW_OFFSET(self);
+    n = _Buffered_raw_seek(self, target, whence);
+    if (n == -1)
+        goto end;
+    self->raw_pos = -1;
+    res = PyLong_FromSsize_t(n);
+    if (res != NULL)
+        _BufferedReader_reset_buf(self);
+
+end:
+    LEAVE_BUFFERED(self)
+    return res;
+}
+
+static PyObject *
+Buffered_truncate(BufferedObject *self, PyObject *args)
+{
+    PyObject *pos = Py_None;
+    PyObject *res = NULL;
+
+    CHECK_INITIALIZED(self)
+    if (!PyArg_ParseTuple(args, "|O:truncate", &pos)) {
+        return NULL;
+    }
+
+    if (pos != Py_None)
+        Py_INCREF(pos);
+
+    ENTER_BUFFERED(self)
+
+    if (self->writable) {
+        res = _BufferedWriter_flush_unlocked(self, 0);
+        if (res == NULL)
+            goto end;
+        Py_CLEAR(res);
+        _BufferedWriter_reset_buf(self);
+    }
+    if (self->readable)
+        _BufferedReader_reset_buf(self);
+    res = PyObject_CallMethodObjArgs(self->raw, _PyIO_str_truncate, pos, NULL);
+    if (res == NULL)
+        goto end;
+    /* Reset cached position */
+    if (_Buffered_raw_tell(self) == -1)
+        PyErr_Clear();
+
+end:
+    LEAVE_BUFFERED(self)
+    return res;
+}
+
 
 /*
  * class BufferedReader
@@ -437,11 +771,9 @@
 PyDoc_STRVAR(BufferedReader_doc,
              "Create a new buffered reader using the given readable raw IO object.");
 
-static int _BufferedReader_reset_buf(BufferedObject *self)
+static void _BufferedReader_reset_buf(BufferedObject *self)
 {
     self->read_end = -1;
-    self->read_pos = 0;
-    return 0;
 }
 
 static int
@@ -465,33 +797,19 @@
     Py_INCREF(raw);
     self->raw = raw;
     self->buffer_size = buffer_size;
+    self->readable = 1;
+    self->writable = 0;
 
     if (_Buffered_init(self) < 0)
         return -1;
-    
-    if (self->read_buf)
-        PyMem_Free(self->read_buf);
-    self->read_buf = PyMem_Malloc(self->buffer_size);
-    if (self->read_buf == NULL) {
-        PyErr_NoMemory();
-        return -1;
-    }
-
-    if (_BufferedReader_reset_buf(self) < 0)
-        return -1;
-
-    self->read_lock = PyThread_allocate_lock();
-    if (self->read_lock == NULL) {
-                PyErr_SetString(PyExc_RuntimeError, "can't allocate read lock");
-        return -1;
-    }
+    _BufferedReader_reset_buf(self);
 
     self->ok = 1;
     return 0;
 }
 
 static Py_ssize_t
-_BufferedReader_raw_read(BufferedObject *self, unsigned char *start, Py_ssize_t len)
+_BufferedReader_raw_read(BufferedObject *self, char *start, Py_ssize_t len)
 {
     Py_buffer buf;
     PyObject *memobj, *res;
@@ -514,8 +832,25 @@
     n = PyNumber_AsSsize_t(res, PyExc_ValueError);
     Py_DECREF(res);
     /* TODO: sanity check (0 <= n <= len) */
-    if (n > 0 && self->raw_pos != -1)
-        self->raw_pos += n;
+    if (n > 0 && self->abs_pos != -1)
+        self->abs_pos += n;
+    return n;
+}
+
+static Py_ssize_t
+_BufferedReader_fill_buffer(BufferedObject *self)
+{
+    Py_ssize_t start, len, n;
+    if (VALID_READ_BUFFER(self))
+        start = self->read_end;
+    else
+        start = 0;
+    len = self->buffer_size - start;
+    n = _BufferedReader_raw_read(self, self->buffer + start, len);
+    if (n <= 0)
+        return n;
+    self->read_end = start + n;
+    self->raw_pos = start + n;
     return n;
 }
 
@@ -524,7 +859,7 @@
 {
     PyObject *data, *chunks, *res = NULL;
     Py_ssize_t current_size, remaining, written;
-    unsigned char *out;
+    char *out;
     static PyObject *sep = NULL;
 
     /* Special case for when the number of bytes to read is unspecified. */
@@ -534,19 +869,23 @@
             return NULL;
 
         /* First copy what we have in the current buffer. */
-        current_size = AVAILABLE_BYTES(self);
+        current_size = READAHEAD(self);
         data = NULL;
         if (current_size) {
             data = PyBytes_FromStringAndSize(
-                self->read_buf + self->read_pos, current_size);
+                self->buffer + self->pos, current_size);
             if (data == NULL) {
                 Py_DECREF(chunks);
                 return NULL;
             }
         }
-        if (_BufferedReader_reset_buf(self) == -1) {
-            Py_DECREF(chunks);
-            return NULL;
+        _BufferedReader_reset_buf(self);
+        /* We're going past the buffer's bounds, flush it */
+        if (self->writable) {
+            res = _BufferedWriter_flush_unlocked(self, 1);
+            if (res == NULL)
+                return NULL;
+            Py_CLEAR(res);
         }
         while (1) {
             if (data) {
@@ -590,19 +929,19 @@
                 }
             }
             current_size += PyBytes_GET_SIZE(data);
-            if (self->raw_pos != -1)
-                self->raw_pos += PyBytes_GET_SIZE(data);
+            if (self->abs_pos != -1)
+                self->abs_pos += PyBytes_GET_SIZE(data);
         }
     }
 
     /* The number of bytes to read is specified, return at most n bytes. */
-    current_size = AVAILABLE_BYTES(self);
+    current_size = READAHEAD(self);
     if (n <= current_size) {
         /* Fast path: the data to read is fully buffered. */
-        res = PyBytes_FromStringAndSize(self->read_buf + self->read_pos, n);
+        res = PyBytes_FromStringAndSize(self->buffer + self->pos, n);
         if (res == NULL)
             goto error;
-        self->read_pos += n;
+        self->pos += n;
         return res;
     }
 
@@ -616,14 +955,13 @@
     remaining = n;
     written = 0;
     if (current_size > 0) {
-        memcpy(out, self->read_buf + self->read_pos, current_size);
+        memcpy(out, self->buffer + self->pos, current_size);
         remaining -= current_size;
         written += current_size;
     }
-    if (_BufferedReader_reset_buf(self) == -1)
-        goto error;
+    _BufferedReader_reset_buf(self);
     while (remaining > 0) {
-        /* We want to read a whole block at the end into read_buf.
+        /* We want to read a whole block at the end into buffer.
            If we had readv() we could do this in one pass. */
         Py_ssize_t r = MINUS_LAST_BLOCK(self, remaining);
         if (r == 0)
@@ -646,12 +984,11 @@
         written += r;
     }
     assert(remaining <= self->buffer_size);
-    self->read_pos = 0;
+    self->pos = 0;
+    self->raw_pos = 0;
     self->read_end = 0;
     while (self->read_end < self->buffer_size) {
-        Py_ssize_t r = _BufferedReader_raw_read(self, 
-            self->read_buf + self->read_end,
-            self->buffer_size - self->read_end);
+        Py_ssize_t r = _BufferedReader_fill_buffer(self);
         if (r == -1)
             goto error;
         if (r == 0 || r == -2) {
@@ -666,212 +1003,53 @@
             return Py_None;
         }
         if (remaining > r) {
-            memcpy(out + written, self->read_buf + self->read_end, r);
+            memcpy(out + written, self->buffer + self->pos, r);
             written += r;
-            self->read_pos += r;
+            self->pos += r;
             remaining -= r;
         }
         else if (remaining > 0) {
-            memcpy(out + written, self->read_buf + self->read_end, remaining);
+            memcpy(out + written, self->buffer + self->pos, remaining);
             written += remaining;
-            self->read_pos += remaining;
+            self->pos += remaining;
             remaining = 0;
         }
-        self->read_end += r;
         if (remaining == 0)
-            /* XXX perhaps we could try again to fill the buffer instead? */
             break;
     }
 
     return res;
 
 error:
-    Py_XDECREF(res);
-    return NULL;
-}
-
-static PyObject *
-BufferedReader_read(BufferedObject *self, PyObject *args)
-{
-    Py_ssize_t n = -1;
-    PyObject *res;
-
-    CHECK_INITIALIZED(self)
-    if (!PyArg_ParseTuple(args, "|n:read", &n)) {
-        return NULL;
-    }
-
-    if (BufferedIOMixin_closed(self)) {
-        PyErr_SetString(PyExc_ValueError, "read of closed file");
-        return NULL;
-    }
-
-    ENTER_BUFFERED_READER(self)
-
-    res = _BufferedReader_read_unlocked(self, n);
-
-    LEAVE_BUFFERED_READER(self)
-
-    return res;
-}
-
-static PyObject *
-_BufferedReader_peek_unlocked(BufferedObject *self, Py_ssize_t n)
-{
-    Py_ssize_t have, r;
-
-    have = AVAILABLE_BYTES(self);
-    /* Constraints:
-       1. we don't want to advance the file position.
-       2. we don't want to lose block alignment, so we can't shift the buffer
-          to make some place.
-       Therefore, we either return `have` bytes (if > 0), or a full buffer.
-    */
-    if (have > 0) {
-        return PyBytes_FromStringAndSize(self->read_buf + self->read_pos, have);
-    }
-
-    /* Fill the buffer from the raw stream, and copy it to the result. */
-    r = _BufferedReader_raw_read(self, self->read_buf, self->buffer_size);
-    if (r == -1)
-        return NULL;
-    if (r == -2)
-        r = 0;
-    self->read_pos = 0;
-    self->read_end = r;
-    return PyBytes_FromStringAndSize(self->read_buf, r);
-
-}
-
-static PyObject *
-BufferedReader_peek(BufferedObject *self, PyObject *args)
-{
-    Py_ssize_t n = 0;
-    PyObject *res;
-
-    CHECK_INITIALIZED(self)
-    if (!PyArg_ParseTuple(args, "|n:peek", &n)) {
-        return NULL;
-    }
-
-    ENTER_BUFFERED_READER(self)
-
-    res = _BufferedReader_peek_unlocked(self, n);
-
-    LEAVE_BUFFERED_READER(self)
-
-    return res;
-}
-
-static PyObject *
-BufferedReader_read1(BufferedObject *self, PyObject *args)
-{
-    Py_ssize_t n, have;
-    PyObject *res;
-
-    CHECK_INITIALIZED(self)
-    if (!PyArg_ParseTuple(args, "n:read1", &n)) {
-        return NULL;
-    }
-
-    /* TODO: n < 0 should raise an error (?) */
-    if (n <= 0)
-        return PyBytes_FromStringAndSize(NULL, 0);
-
-    ENTER_BUFFERED_READER(self)
-
-    /* Return up to n bytes.  If at least one byte is buffered, we
-       only return buffered bytes.  Otherwise, we do one raw read. */
-
-    /* TODO: this mimicks the io.py implementation but is probably wrong.
-       If we need to read from the raw stream, then we should actually read
-       all `n` bytes asked by the caller (and possibly more, so as to fill
-       our buffer for the next reads). */
-
-    res = _BufferedReader_peek_unlocked(self, 1);
-    if (res == NULL)
-        goto end;
-    Py_DECREF(res);
-
-    have = self->read_end - self->read_pos;
-    if (n > have)
-        n = have;
-
-    res = _BufferedReader_read_unlocked(self, n);
-
-end:
-    LEAVE_BUFFERED_READER(self)
-    return res;
-}
-
-static PyObject *
-BufferedReader_seek(BufferedObject *self, PyObject *args)
-{
-    Py_ssize_t current, target, avail, n;
-    int whence = 0;
-    PyObject *res = NULL;
-
-    CHECK_INITIALIZED(self)
-    if (!PyArg_ParseTuple(args, "n|i:seek", &target, &whence)) {
-        return NULL;
-    }
-    /* TODO: sanity check whence value */
-
-    ENTER_BUFFERED_READER(self)
-
-    if (whence != 2) {
-        /* Check if seeking leaves us inside the current buffer,
-           so as to return quickly if possible.
-           Don't know how to do that when whence == 2, though. */
-        current = self->raw_pos;
-        if (current == -1)
-            current = _Buffered_raw_tell(self);
-        if (current == -1)
-            goto end;
-        avail = AVAILABLE_BYTES(self);
-        if (avail > 0) {
-            Py_ssize_t offset;
-            if (whence == 0)
-                offset = target - (current - avail);
-            else
-                offset = target;
-            if (offset >= -self->read_pos && offset <= avail) {
-                self->read_pos += offset;
-                res = PyLong_FromSsize_t(current - avail + offset);
-                goto end;
-            }
-        }
-    }
-
-    /* Fallback: invoke raw seek() method and clear buffer */
-    if (whence == 1)
-        target -= AVAILABLE_BYTES(self);
-
-    /* TODO: align on block boundary and read buffer if needed? */
-    n = _Buffered_raw_seek(self, target, whence);
-    if (n == -1)
-        goto end;
-    res = PyLong_FromSsize_t(n);
-    if (res != NULL && _BufferedReader_reset_buf(self) < 0)
-        Py_CLEAR(res);
-
-end:
-    LEAVE_BUFFERED_READER(self)
-    return res;
+    Py_XDECREF(res);
+    return NULL;
 }
 
 static PyObject *
-BufferedReader_tell(BufferedObject *self, PyObject *args)
+_BufferedReader_peek_unlocked(BufferedObject *self, Py_ssize_t n)
 {
-    Py_ssize_t pos;
+    Py_ssize_t have, r;
 
-    CHECK_INITIALIZED(self)
-    pos = _Buffered_raw_tell(self);
-    if (pos == -1)
+    have = READAHEAD(self);
+    /* Constraints:
+       1. we don't want to advance the file position.
+       2. we don't want to lose block alignment, so we can't shift the buffer
+          to make some place.
+       Therefore, we either return `have` bytes (if > 0), or a full buffer.
+    */
+    if (have > 0) {
+        return PyBytes_FromStringAndSize(self->buffer + self->pos, have);
+    }
+
+    /* Fill the buffer from the raw stream, and copy it to the result. */
+    _BufferedReader_reset_buf(self);
+    r = _BufferedReader_fill_buffer(self);
+    if (r == -1)
         return NULL;
-    pos -= AVAILABLE_BYTES(self);
-    /* TODO: sanity check (pos >= 0) */
-    return PyLong_FromSsize_t(pos);
+    if (r == -2)
+        r = 0;
+    self->pos = 0;
+    return PyBytes_FromStringAndSize(self->buffer, r);
 }
 
 static PyMethodDef BufferedReader_methods[] = {
@@ -885,11 +1063,11 @@
     {"fileno", (PyCFunction)BufferedIOMixin_fileno, METH_NOARGS},
     {"isatty", (PyCFunction)BufferedIOMixin_isatty, METH_NOARGS},
 
-    {"read", (PyCFunction)BufferedReader_read, METH_VARARGS},
-    {"peek", (PyCFunction)BufferedReader_peek, METH_VARARGS},
-    {"read1", (PyCFunction)BufferedReader_read1, METH_VARARGS},
-    {"seek", (PyCFunction)BufferedReader_seek, METH_VARARGS},
-    {"tell", (PyCFunction)BufferedReader_tell, METH_NOARGS},
+    {"read", (PyCFunction)Buffered_read, METH_VARARGS},
+    {"peek", (PyCFunction)Buffered_peek, METH_VARARGS},
+    {"read1", (PyCFunction)Buffered_read1, METH_VARARGS},
+    {"seek", (PyCFunction)Buffered_seek, METH_VARARGS},
+    {"tell", (PyCFunction)Buffered_tell, METH_NOARGS},
     {NULL, NULL}
 };
 
@@ -959,12 +1137,11 @@
     "DEFAULT_BUFFER_SIZE. max_buffer_size isn't used anymore.\n"
     );
 
-static int
+static void
 _BufferedWriter_reset_buf(BufferedObject *self)
 {
     self->write_pos = 0;
-    self->write_end = 0;
-    return 0;
+    self->write_end = -1;
 }
 
 static int
@@ -989,33 +1166,21 @@
     Py_CLEAR(self->raw);
     Py_INCREF(raw);
     self->raw = raw;
+    self->readable = 0;
+    self->writable = 1;
 
     self->buffer_size = buffer_size;
     if (_Buffered_init(self) < 0)
         return -1;
-
-    if (self->write_buf)
-        PyMem_Free(self->write_buf);
-    self->write_buf = PyMem_Malloc(self->buffer_size);
-    if (self->write_buf == NULL) {
-        PyErr_NoMemory();
-        return -1;
-    }
-    if (_BufferedWriter_reset_buf(self) < 0)
-        return -1;
-
-    self->write_lock = PyThread_allocate_lock();
-    if (self->write_lock == NULL) {
-                PyErr_SetString(PyExc_RuntimeError, "can't allocate write lock");
-        return -1;
-    }
+    _BufferedWriter_reset_buf(self);
+    self->pos = 0;
 
     self->ok = 1;
     return 0;
 }
 
 static Py_ssize_t
-_BufferedWriter_raw_write(BufferedObject *self, unsigned char *start, Py_ssize_t len)
+_BufferedWriter_raw_write(BufferedObject *self, char *start, Py_ssize_t len)
 {
     Py_buffer buf;
     PyObject *memobj, *res;
@@ -1033,24 +1198,39 @@
     n = PyNumber_AsSsize_t(res, PyExc_ValueError);
     Py_DECREF(res);
     /* TODO: sanity check (0 <= n <= len) */
+    if (n > 0 && self->abs_pos != -1)
+        self->abs_pos += n;
     return n;
 }
 
+/* `restore_pos` is 1 if we need to restore the raw stream position at
+   the end, 0 otherwise. */
 static PyObject *
-_BufferedWriter_flush_unlocked(BufferedObject *self)
+_BufferedWriter_flush_unlocked(BufferedObject *self, int restore_pos)
 {
-    Py_ssize_t written = 0, n;
-    PyObject *res = NULL;
+    Py_ssize_t written = 0, n, rewind;
 
+    if (!VALID_WRITE_BUFFER(self) || self->write_pos == self->write_end)
+        goto end;
+    /* First, rewind */
+    rewind = RAW_OFFSET(self) + (self->pos - self->write_pos);
+    if (rewind != 0) {
+        n = _Buffered_raw_seek(self, -rewind, 1);
+        if (n < 0) {
+            goto error;
+        }
+        self->raw_pos -= rewind;
+    }
     while (self->write_pos < self->write_end) {
         n = _BufferedWriter_raw_write(self,
-            self->write_buf + self->write_pos,
+            self->buffer + self->write_pos,
             self->write_end - self->write_pos);
-        if (n == -1 && PyErr_Occurred()) {
+        if (n == -1) {
             Py_ssize_t *w = _Buffered_check_blocking_error();
             if (w == NULL)
                 goto error;
             self->write_pos += *w;
+            self->raw_pos = self->write_pos;
             written += *w;
             *w = written;
             /* Already re-raised */
@@ -1058,15 +1238,26 @@
         }
         /* TODO: sanity check with a macro */
         self->write_pos += n;
+        self->raw_pos = self->write_pos;
         written += n;
     }
-    if (_BufferedWriter_reset_buf(self) < 0)
-        goto error;
 
+    if (restore_pos) {
+        Py_ssize_t forward = rewind - written;
+        if (forward != 0) {
+            n = _Buffered_raw_seek(self, forward, 1);
+            if (n < 0) {
+                goto error;
+            }
+            self->raw_pos += forward;
+        }
+    }
+    _BufferedWriter_reset_buf(self);
+
+end:
     Py_RETURN_NONE;
 
 error:
-    Py_XDECREF(res);
     return NULL;
 }
 
@@ -1088,48 +1279,61 @@
         return NULL;
     }
 
-    ENTER_BUFFERED_WRITER(self)
+    ENTER_BUFFERED(self)
 
     /* Fast path: the data to write can be fully buffered. */
-    avail = self->buffer_size - self->write_end;
+    if (!VALID_READ_BUFFER(self) && !VALID_WRITE_BUFFER(self)) {
+        self->pos = 0;
+        self->raw_pos = 0;
+    }
+    avail = self->buffer_size - self->pos;
     if (buf.len <= avail) {
-        memcpy(self->write_buf + self->write_end, buf.buf, buf.len);
-        self->write_end += buf.len;
+        memcpy(self->buffer + self->pos, buf.buf, buf.len);
+        if (!VALID_WRITE_BUFFER(self)) {
+            self->write_pos = self->pos;
+        }
+        ADJUST_POSITION(self, self->pos + buf.len);
+        if (self->pos > self->write_end)
+            self->write_end = self->pos;
         written = buf.len;
         goto end;
     }
 
     /* First write the current buffer */
-    res = _BufferedWriter_flush_unlocked(self);
+    res = _BufferedWriter_flush_unlocked(self, 0);
     if (res == NULL) {
         Py_ssize_t *w = _Buffered_check_blocking_error();
         if (w == NULL)
             goto error;
+        if (self->readable)
+            _BufferedReader_reset_buf(self);
         /* Make some place by shifting the buffer. */
-        memmove(self->write_buf, self->write_buf + self->write_pos,
+        assert(VALID_WRITE_BUFFER(self));
+        memmove(self->buffer, self->buffer + self->write_pos,
                 self->write_end - self->write_pos);
         self->write_end -= self->write_pos;
+        self->raw_pos -= self->write_pos;
+        self->pos -= self->write_pos;
         self->write_pos = 0;
         avail = self->buffer_size - self->write_end;
         if (buf.len <= avail) {
             /* Everything can be buffered */
             PyErr_Clear();
-            memcpy(self->write_buf + self->write_end, buf.buf, buf.len);
+            memcpy(self->buffer + self->write_end, buf.buf, buf.len);
             self->write_end += buf.len;
             written = buf.len;
             goto end;
         }
         /* Buffer as much as possible. */
-        memcpy(self->write_buf + self->write_end, buf.buf, avail);
+        memcpy(self->buffer + self->write_end, buf.buf, avail);
         self->write_end += avail;
         /* Already re-raised */
         *w = avail;
-        res = NULL;
         goto error;
     }
     Py_CLEAR(res);
 
-    /* Then write buf itself. At this point write_buf has been emptied. */
+    /* Then write buf itself. At this point the buffer has been emptied. */
     remaining = buf.len;
     written = 0;
     while (remaining > self->buffer_size) {
@@ -1143,8 +1347,10 @@
             remaining -= *w;
             if (remaining > self->buffer_size) {
                 /* Can't buffer everything, still buffer as much as possible */
-                memcpy(self->write_buf,
+                memcpy(self->buffer,
                        (char *) buf.buf + written, self->buffer_size);
+                self->raw_pos = 0;
+                ADJUST_POSITION(self, self->buffer_size);
                 self->write_end = self->buffer_size;
                 *w = written + self->buffer_size;
                 /* Already re-raised */
@@ -1156,123 +1362,27 @@
         written += n;
         remaining -= n;
     }
+    if (self->readable)
+        _BufferedReader_reset_buf(self);
     if (remaining > 0) {
-        memcpy(self->write_buf, (char *) buf.buf + written, remaining);
+        memcpy(self->buffer, (char *) buf.buf + written, remaining);
         written += remaining;
     }
     self->write_pos = 0;
     /* TODO: sanity check (remaining >= 0) */
     self->write_end = remaining;
+    ADJUST_POSITION(self, remaining);
+    self->raw_pos = 0;
 
 end:
     res = PyLong_FromSsize_t(written);
 
 error:
-    LEAVE_BUFFERED_WRITER(self)
+    LEAVE_BUFFERED(self)
     PyBuffer_Release(&buf);
     return res;
 }
 
-static PyObject *
-BufferedWriter_truncate(BufferedObject *self, PyObject *args)
-{
-    PyObject *pos = NULL;
-    PyObject *res;
-
-    CHECK_INITIALIZED(self)
-    if (!PyArg_ParseTuple(args, "|O:truncate", &pos)) {
-        return NULL;
-    }
-    /* will be parsed again by BufferedIOMixin_truncate */
-    Py_XDECREF(pos);
-
-    if (BufferedIOMixin_closed(self)) {
-        PyErr_SetString(PyExc_ValueError, "truncate of closed file");
-        return NULL;
-    }
-
-    ENTER_BUFFERED_WRITER(self)
-
-    res = _BufferedWriter_flush_unlocked(self);
-    if (res == NULL)
-        goto end;
-    Py_DECREF(res);
-
-    res = BufferedIOMixin_truncate(self, args);
-
-  end:
-    LEAVE_BUFFERED_WRITER(self)
-    return res;
-}
-
-static PyObject *
-BufferedWriter_flush(BufferedObject *self, PyObject *args)
-{
-    PyObject *res;
-
-    CHECK_INITIALIZED(self)
-    if (BufferedIOMixin_closed(self)) {
-        PyErr_SetString(PyExc_ValueError, "flush of closed file");
-        return NULL;
-    }
-
-    ENTER_BUFFERED_WRITER(self)
-
-    res = _BufferedWriter_flush_unlocked(self);
-
-    LEAVE_BUFFERED_WRITER(self)
-
-    return res;
-}
-
-static PyObject *
-BufferedWriter_tell(BufferedObject *self, PyObject *args)
-{
-    PyObject *op1, *op2, *res;
-
-    CHECK_INITIALIZED(self)
-    op1 = PyObject_CallMethod(self->raw, "tell", NULL);
-    op2 = PyLong_FromSsize_t(self->write_end - self->write_pos);
-    if (op2 == NULL) {
-        Py_DECREF(op1);
-        return NULL;
-    }
-
-    res = PyNumber_Add(op1, op2);
-    Py_DECREF(op1);
-    Py_DECREF(op2);
-    return res;
-}
-
-static PyObject *
-BufferedWriter_seek(BufferedObject *self, PyObject *args)
-{
-    Py_ssize_t pos;
-    int whence = 0;
-    PyObject *res;
-
-    CHECK_INITIALIZED(self)
-    if (!PyArg_ParseTuple(args, "n|i:seek", &pos, &whence)) {
-        return NULL;
-    }
-    if (BufferedIOMixin_closed(self)) {
-        PyErr_SetString(PyExc_ValueError, "seek of closed file");
-        return NULL;
-    }
-
-    ENTER_BUFFERED_WRITER(self)
-
-    res = _BufferedWriter_flush_unlocked(self);
-    if (res == NULL)
-        goto end;
-
-    res = PyObject_CallMethod(self->raw, "seek", "ni", pos, whence);
-
-  end:
-    LEAVE_BUFFERED_WRITER(self)
-    return res;
-}
-
 static PyMethodDef BufferedWriter_methods[] = {
     /* BufferedIOMixin methods */
     {"close", (PyCFunction)BufferedIOMixin_close, METH_NOARGS},
@@ -1283,10 +1393,10 @@
     {"isatty", (PyCFunction)BufferedIOMixin_isatty, METH_NOARGS},
 
     {"write", (PyCFunction)BufferedWriter_write, METH_VARARGS},
-    {"truncate", (PyCFunction)BufferedWriter_truncate, METH_VARARGS},
-    {"flush", (PyCFunction)BufferedWriter_flush, METH_NOARGS},
-    {"seek", (PyCFunction)BufferedWriter_seek, METH_VARARGS},
-    {"tell", (PyCFunction)BufferedWriter_tell, METH_NOARGS},
+    {"truncate", (PyCFunction)Buffered_truncate, METH_VARARGS},
+    {"flush", (PyCFunction)Buffered_flush, METH_NOARGS},
+    {"seek", (PyCFunction)Buffered_seek, METH_VARARGS},
+    {"tell", (PyCFunction)Buffered_tell, METH_NOARGS},
     {NULL, NULL}
 };
 
@@ -1513,6 +1623,7 @@
     {"read", (PyCFunction)BufferedRWPair_read, METH_VARARGS},
     {"peek", (PyCFunction)BufferedRWPair_peek, METH_VARARGS},
     {"read1", (PyCFunction)BufferedRWPair_read1, METH_VARARGS},
+    {"readinto", (PyCFunction)Buffered_readinto, METH_VARARGS},
 
     {"write", (PyCFunction)BufferedRWPair_write, METH_VARARGS},
     {"flush", (PyCFunction)BufferedRWPair_flush, METH_NOARGS},
@@ -1607,211 +1718,19 @@
     Py_INCREF(raw);
     self->raw = raw;
     self->buffer_size = buffer_size;
+    self->readable = 1;
+    self->writable = 1;
 
     if (_Buffered_init(self) < 0)
         return -1;
-    Py_CLEAR(self->read_buf);
-    self->read_buf = PyMem_Malloc(self->buffer_size);
-    if (self->read_buf == NULL) {
-        PyErr_NoMemory();
-        return -1;
-    }
-    if (_BufferedReader_reset_buf(self) < 0)
-        return -1;
-
-    self->read_lock = PyThread_allocate_lock();
-    if (self->read_lock == NULL) {
-                PyErr_SetString(PyExc_RuntimeError, "can't allocate read lock");
-        return -1;
-    }
-
-    Py_CLEAR(self->write_buf);
-    self->write_buf = PyMem_Malloc(self->buffer_size);
-    if (self->write_buf == NULL) {
-        PyErr_NoMemory();
-        return -1;
-    }
-    if (_BufferedWriter_reset_buf(self) < 0)
-        return -1;
-
-    self->write_lock = PyThread_allocate_lock();
-    if (self->write_lock == NULL) {
-                PyErr_SetString(PyExc_RuntimeError, "can't allocate write lock");
-        return -1;
-    }
+    _BufferedReader_reset_buf(self);
+    _BufferedWriter_reset_buf(self);
+    self->pos = 0;
 
     self->ok = 1;
     return 0;
 }
 
-static PyObject *
-BufferedRandom_tell(BufferedObject *self, PyObject *args)
-{
-    CHECK_INITIALIZED(self)
-    if (self->write_end > self->write_pos)
-        return BufferedWriter_tell(self, args);
-    else
-        return BufferedReader_tell(self, args);
-}
-
-static PyObject *
-BufferedRandom_seek(BufferedObject *self, PyObject *args)
-{
-    Py_ssize_t pos;
-    int whence = 0;
-    PyObject *res;
-
-    CHECK_INITIALIZED(self)
-    if (!PyArg_ParseTuple(args, "n|i:seek", &pos, &whence)) {
-        return NULL;
-    }
-
-    res = PyObject_CallMethod((PyObject *)self, "flush", NULL);
-    if (res == NULL)
-        return NULL;
-
-    /* First do the raw seek, then empty the read buffer, so that
-     * if the raw seek fails, we don't lose buffered data forever.
-     */
-
-    ENTER_BUFFERED_READER(self)
-
-    pos = _Buffered_raw_seek(self, pos, whence);
-    if (pos == -1)
-        goto end;
-    res = PyLong_FromSsize_t(pos);
-    if (res == NULL)
-        goto end;
-
-    if (_BufferedReader_reset_buf(self) < 0
-        || _BufferedWriter_reset_buf(self) < 0)
-        Py_CLEAR(res);
-
-end:
-    LEAVE_BUFFERED_READER(self)
-    return res;
-}
-
-static PyObject *
-BufferedRandom_truncate(BufferedObject *self, PyObject *args)
-{
-    PyObject *pos = Py_None;
-    PyObject *res;
-
-    CHECK_INITIALIZED(self)
-    if (!PyArg_ParseTuple(args, "|O:truncate", &pos)) {
-        return NULL;
-    }
-
-    if (pos == Py_None)
-        pos = PyObject_CallMethod(self->raw, "tell", NULL);
-    else
-        Py_INCREF(pos);
-
-    /* Use seek to flush the read buffer. */
-    res = PyObject_CallMethod((PyObject *)self, "seek", "O", pos);
-    Py_DECREF(pos);
-    if (res == NULL)
-        return NULL;
-    Py_DECREF(res);
-
-    args = PyTuple_New(0);
-    if (args == NULL)
-        return NULL;
-    res = BufferedWriter_truncate(self, args);
-    Py_DECREF(args);
-
-    return res;
-}
-
-static PyObject *
-BufferedRandom_read(BufferedObject *self, PyObject *args)
-{
-    Py_ssize_t n = -1;
-    PyObject *res;
-
-    CHECK_INITIALIZED(self)
-    if (!PyArg_ParseTuple(args, "|n:read", &n)) {
-        return NULL;
-    }
-
-    res = BufferedWriter_flush(self, Py_None);
-    if (res == NULL)
-        return NULL;
-
-    return BufferedReader_read(self, args);
-}
-
-static PyObject *
-BufferedRandom_readinto(BufferedObject *self, PyObject *args)
-{
-    PyObject *res;
-
-    CHECK_INITIALIZED(self)
-    res = BufferedWriter_flush(self, Py_None);
-    if (res == NULL)
-        return NULL;
-    Py_DECREF(res);
-
-    return BufferedIOBase_readinto((PyObject *)self, args);
-}
-
-static PyObject *
-BufferedRandom_peek(BufferedObject *self, PyObject *args)
-{
-    PyObject *res;
-    CHECK_INITIALIZED(self)
-    res = BufferedWriter_flush(self, Py_None);
-    if (res == NULL)
-        return NULL;
-    Py_DECREF(res);
-
-    return BufferedReader_peek(self, args);
-}
-
-static PyObject *
-BufferedRandom_read1(BufferedObject *self, PyObject *args)
-{
-    PyObject *res;
-    CHECK_INITIALIZED(self)
-    res = BufferedWriter_flush(self, Py_None);
-    if (res == NULL)
-        return NULL;
-    Py_DECREF(res);
-
-    return BufferedReader_read1(self, args);
-}
-
-static PyObject *
-BufferedRandom_write(BufferedObject *self, PyObject *args)
-{
-    Py_ssize_t readahead;
-    CHECK_INITIALIZED(self)
-    readahead = AVAILABLE_BYTES(self);
-    if (readahead > 0) {
-        PyObject *res;
-        /* Undo readahead */
-
-        ENTER_BUFFERED_READER(self)
-    
-        res = PyObject_CallMethod(self->raw, "seek", "ni",
-                                  -readahead, 1);
-        Py_XDECREF(res);
-        if (res != NULL) {
-            if (_BufferedReader_reset_buf(self) < 0)
-                res = NULL;
-        }
-
-        LEAVE_BUFFERED_READER(self)
-
-        if (res == NULL)
-            return NULL;
-    }
-
-    return BufferedWriter_write(self, args);
-}
-
-
 static PyMethodDef BufferedRandom_methods[] = {
     /* BufferedIOMixin methods */
     {"close", (PyCFunction)BufferedIOMixin_close, METH_NOARGS},
@@ -1821,16 +1740,16 @@
     {"fileno", (PyCFunction)BufferedIOMixin_fileno, METH_NOARGS},
     {"isatty", (PyCFunction)BufferedIOMixin_isatty, METH_NOARGS},
 
-    {"flush", (PyCFunction)BufferedWriter_flush, METH_NOARGS},
+    {"flush", (PyCFunction)Buffered_flush, METH_NOARGS},
 
-    {"seek", (PyCFunction)BufferedRandom_seek, METH_VARARGS},
-    {"tell", (PyCFunction)BufferedRandom_tell, METH_NOARGS},
-    {"truncate", (PyCFunction)BufferedRandom_truncate, METH_VARARGS},
-    {"read", (PyCFunction)BufferedRandom_read, METH_VARARGS},
-    {"readinto", (PyCFunction)BufferedRandom_readinto, METH_VARARGS},
-    {"peek", (PyCFunction)BufferedRandom_peek, METH_VARARGS},
-    {"read1", (PyCFunction)BufferedRandom_read1, METH_VARARGS},
-    {"write", (PyCFunction)BufferedRandom_write, METH_VARARGS},
+    {"seek", (PyCFunction)Buffered_seek, METH_VARARGS},
+    {"tell", (PyCFunction)Buffered_tell, METH_NOARGS},
+    {"truncate", (PyCFunction)Buffered_truncate, METH_VARARGS},
+    {"read", (PyCFunction)Buffered_read, METH_VARARGS},
+    {"read1", (PyCFunction)Buffered_read1, METH_VARARGS},
+    {"readinto", (PyCFunction)Buffered_readinto, METH_VARARGS},
+    {"peek", (PyCFunction)Buffered_peek, METH_VARARGS},
+    {"write", (PyCFunction)BufferedWriter_write, METH_VARARGS},
     {NULL, NULL}
 };
 

Modified: sandbox/trunk/io-c/_iomodule.h
==============================================================================
--- sandbox/trunk/io-c/_iomodule.h	(original)
+++ sandbox/trunk/io-c/_iomodule.h	Tue Jan  6 02:04:25 2009
@@ -60,5 +60,6 @@
 extern PyObject *_PyIO_str_readinto;
 extern PyObject *_PyIO_str_seekable;
 extern PyObject *_PyIO_str_tell;
+extern PyObject *_PyIO_str_truncate;
 extern PyObject *_PyIO_str_writable;
 extern PyObject *_PyIO_str_write;

Modified: sandbox/trunk/io-c/io.c
==============================================================================
--- sandbox/trunk/io-c/io.c	(original)
+++ sandbox/trunk/io-c/io.c	Tue Jan  6 02:04:25 2009
@@ -24,6 +24,7 @@
 PyObject *_PyIO_str_readinto;
 PyObject *_PyIO_str_seekable;
 PyObject *_PyIO_str_tell;
+PyObject *_PyIO_str_truncate;
 PyObject *_PyIO_str_writable;
 PyObject *_PyIO_str_write;
 
@@ -643,6 +644,8 @@
         goto fail;
     if (!(_PyIO_str_tell = PyUnicode_InternFromString("tell")))
         goto fail;
+    if (!(_PyIO_str_truncate = PyUnicode_InternFromString("truncate")))
+        goto fail;
     if (!(_PyIO_str_write = PyUnicode_InternFromString("write")))
         goto fail;
     if (!(_PyIO_str_writable = PyUnicode_InternFromString("writable")))


More information about the Python-checkins mailing list