]> granicus.if.org Git - python/commitdiff
Issue #12213: Fix a buffering bug with interleaved reads and writes that
authorAntoine Pitrou <solipsis@pitrou.net>
Sat, 20 Aug 2011 12:52:04 +0000 (14:52 +0200)
committerAntoine Pitrou <solipsis@pitrou.net>
Sat, 20 Aug 2011 12:52:04 +0000 (14:52 +0200)
could appear on BufferedRandom streams.

1  2 
Lib/test/test_io.py
Misc/NEWS
Modules/_io/bufferedio.c

Simple merge
diff --cc Misc/NEWS
index 7dc106afee10d6434b30b4e3c661f66a0e068ab7,da0e67f666c49e036d54df7e6928de80e91d4eeb..29a2e6f924a787f7d7960f70d649411828b8cf7b
+++ b/Misc/NEWS
@@@ -265,18 -70,6 +265,21 @@@ Core and Builtin
  Library
  -------
  
++- Issue #12213: Fix a buffering bug with interleaved reads and writes that
++  could appear on BufferedRandom streams.
++
 +- Issue #12778: Reduce memory consumption when JSON-encoding a large
 +  container of many small objects.
 +
 +- Issue #12650: Fix a race condition where a subprocess.Popen could leak
 +  resources (FD/zombie) when killed at the wrong time.
 +
 +- Issue #12744: Fix inefficient representation of integers between 2**31 and
 +  2**63 on systems with a 64-bit C "long".
 +
 +- Issue #12646: Add an 'eof' attribute to zlib.Decompress, to make it easier to
 +  detect truncated input streams.
 +
  - Issue #11513: Fix exception handling ``tarfile.TarFile.gzopen()`` when
    the file cannot be opened.
  
index cdaa36e3a7773ad49fc6a2c4b402d86e20b805e6,d6f0c9cc83507f6026e794515b7da84ed2d64b47..a8631e0bc61d4258dc84b1f72cb54b3a8fc41d22
@@@ -864,14 -875,7 +876,7 @@@ buffered_read1(buffered *self, PyObjec
  
      if (!ENTER_BUFFERED(self))
          return NULL;
 -    
 +
-     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. */
  
@@@ -913,78 -924,10 +925,78 @@@ end
  static PyObject *
  buffered_readinto(buffered *self, PyObject *args)
  {
 +    Py_buffer buf;
 +    Py_ssize_t n, written = 0, remaining;
 +    PyObject *res = NULL;
 +
      CHECK_INITIALIZED(self)
 -    
 -    /* TODO: use raw.readinto() (or a direct copy from our buffer) instead! */
 -    return bufferediobase_readinto((PyObject *)self, args);
 +
 +    if (!PyArg_ParseTuple(args, "w*:readinto", &buf))
 +        return NULL;
 +
 +    n = Py_SAFE_DOWNCAST(READAHEAD(self), Py_off_t, Py_ssize_t);
 +    if (n > 0) {
 +        if (n >= buf.len) {
 +            memcpy(buf.buf, self->buffer + self->pos, buf.len);
 +            self->pos += buf.len;
 +            res = PyLong_FromSsize_t(buf.len);
 +            goto end_unlocked;
 +        }
 +        memcpy(buf.buf, self->buffer + self->pos, n);
 +        self->pos += n;
 +        written = n;
 +    }
 +
 +    if (!ENTER_BUFFERED(self))
 +        goto end_unlocked;
 +
 +    if (self->writable) {
-         res = _bufferedwriter_flush_unlocked(self, 0);
++        res = buffered_flush_and_rewind_unlocked(self);
 +        if (res == NULL)
 +            goto end;
 +        Py_CLEAR(res);
 +    }
 +
 +    _bufferedreader_reset_buf(self);
 +    self->pos = 0;
 +
 +    for (remaining = buf.len - written;
 +         remaining > 0;
 +         written += n, remaining -= n) {
 +        /* If remaining bytes is larger than internal buffer size, copy
 +         * directly into caller's buffer. */
 +        if (remaining > self->buffer_size) {
 +            n = _bufferedreader_raw_read(self, (char *) buf.buf + written,
 +                                         remaining);
 +        }
 +        else {
 +            n = _bufferedreader_fill_buffer(self);
 +            if (n > 0) {
 +                if (n > remaining)
 +                    n = remaining;
 +                memcpy((char *) buf.buf + written,
 +                       self->buffer + self->pos, n);
 +                self->pos += n;
 +                continue; /* short circuit */
 +            }
 +        }
 +        if (n == 0 || (n == -2 && written > 0))
 +            break;
 +        if (n < 0) {
 +            if (n == -2) {
 +                Py_INCREF(Py_None);
 +                res = Py_None;
 +            }
 +            goto end;
 +        }
 +    }
 +    res = PyLong_FromSsize_t(written);
 +
 +end:
 +    LEAVE_BUFFERED(self);
 +end_unlocked:
 +    PyBuffer_Release(&buf);
 +    return res;
  }
  
  static PyObject *
@@@ -1414,50 -1353,22 +1418,51 @@@ _bufferedreader_read_all(buffered *self
      if (current_size) {
          data = PyBytes_FromStringAndSize(
              self->buffer + self->pos, current_size);
 -        if (data == NULL) {
 -            Py_DECREF(chunks);
 +        if (data == NULL)
              return NULL;
 -        }
+         self->pos += current_size;
      }
-     _bufferedreader_reset_buf(self);
      /* We're going past the buffer's bounds, flush it */
      if (self->writable) {
-         res = _bufferedwriter_flush_unlocked(self, 1);
+         res = buffered_flush_and_rewind_unlocked(self);
 -        if (res == NULL) {
 -            Py_DECREF(chunks);
 +        if (res == NULL)
              return NULL;
 -        }
          Py_CLEAR(res);
      }
+     _bufferedreader_reset_buf(self);
 +
 +    if (PyObject_HasAttr(self->raw, _PyIO_str_readall)) {
 +        chunk = PyObject_CallMethodObjArgs(self->raw, _PyIO_str_readall, NULL);
 +        if (chunk == NULL)
 +            return NULL;
 +        if (chunk != Py_None && !PyBytes_Check(chunk)) {
 +            Py_XDECREF(data);
 +            Py_DECREF(chunk);
 +            PyErr_SetString(PyExc_TypeError, "readall() should return bytes");
 +            return NULL;
 +        }
 +        if (chunk == Py_None) {
 +            if (current_size == 0)
 +                return chunk;
 +            else {
 +                Py_DECREF(chunk);
 +                return data;
 +            }
 +        }
 +        else if (current_size) {
 +            PyBytes_Concat(&data, chunk);
 +            Py_DECREF(chunk);
 +            if (data == NULL)
 +                return NULL;
 +            return data;
 +        } else
 +            return chunk;
 +    }
 +
 +    chunks = PyList_New(0);
 +    if (chunks == NULL)
 +        return NULL;
 +
      while (1) {
          if (data) {
              if (PyList_Append(chunks, data) < 0) {