[Python-checkins] r68034 - sandbox/trunk/io-c/_textio.c

amaury.forgeotdarc python-checkins at python.org
Mon Dec 29 23:29:10 CET 2008


Author: amaury.forgeotdarc
Date: Mon Dec 29 23:29:09 2008
New Revision: 68034

Log:
First version of TextIOWrapper.tell().
It crashes - switching to another machine to debug.


Modified:
   sandbox/trunk/io-c/_textio.c

Modified: sandbox/trunk/io-c/_textio.c
==============================================================================
--- sandbox/trunk/io-c/_textio.c	(original)
+++ sandbox/trunk/io-c/_textio.c	Mon Dec 29 23:29:09 2008
@@ -751,6 +751,7 @@
         PyObject *next_input = PyNumber_Add(dec_buffer, input_chunk);
         if (next_input == NULL)
             goto fail;
+        assert (PyBytes_Check(next_input));
         Py_DECREF(dec_buffer);
         Py_CLEAR(self->snapshot);
         self->snapshot = Py_BuildValue("NN", dec_flags, next_input);
@@ -1051,7 +1052,7 @@
 }
 
 static PyObject *
-TextIOWrapper_buildCookie(CookieStruct *cookie, PyObject *cookieObj)
+TextIOWrapper_buildCookie(CookieStruct *cookie)
 {
     char buffer[sizeof(CookieStruct)];
     static int one = 1;
@@ -1209,7 +1210,7 @@
             goto fail;
         Py_DECREF(res);
 
-        self->snapshot = Py_BuildValue("((iy))", cookie.dec_flags, "");
+        self->snapshot = Py_BuildValue("iy", cookie.dec_flags, "");
         if (self->snapshot == NULL)
             goto fail;
     }
@@ -1223,7 +1224,9 @@
         if (input_chunk == NULL)
             goto fail;
 
-        self->snapshot = Py_BuildValue("((iO))", cookie.dec_flags, input_chunk);
+        assert (PyBytes_Check(input_chunk));
+
+        self->snapshot = Py_BuildValue("iO", cookie.dec_flags, input_chunk);
         if (self->snapshot == NULL) {
             Py_DECREF(input_chunk);
             goto fail;
@@ -1252,6 +1255,163 @@
 
 }
 
+static PyObject *
+TextIOWrapper_tell(PyTextIOWrapperObject *self, PyObject *args)
+{
+    PyObject *res;
+    PyObject *posobj = NULL;
+    CookieStruct cookie = {0,0,0,0,0};
+    PyObject *next_input;
+    Py_ssize_t chars_to_skip, chars_decoded;
+    PyObject *saved_state = NULL;
+    char *input, *input_end;
+
+    if (!self->seekable) {
+        PyErr_SetString(PyExc_IOError,
+                        "underlying stream is not seekable");
+        goto fail;
+    }
+
+    res = PyObject_CallMethod((PyObject *)self, "flush", NULL);
+    if (res == NULL)
+        goto fail;
+    Py_DECREF(res);
+
+    posobj = PyObject_CallMethod(self->buffer, "tell", NULL);
+    if (posobj == NULL)
+        goto fail;
+
+    if(self->decoder == NULL || self->snapshot == NULL) {
+        assert (self->decoded_chars == NULL || PyUnicode_GetSize(self->decoded_chars) == 0);
+        return posobj;
+    }
+
+#if defined(HAVE_LARGEFILE_SUPPORT)
+    cookie.start_pos = PyLong_AsLongLong(posobj);
+#else
+    cookie.start_pos = PyLong_AsLong(posobj);
+#endif
+    if (PyErr_Occurred())
+        goto fail;
+
+    /* Skip backward to the snapshot point (see _read_chunk). */
+    if (!PyArg_Parse(self->snapshot, "(iO)", &cookie.dec_flags, &next_input))
+        goto fail;
+
+    assert (PyBytes_Check(next_input));
+
+    cookie.start_pos -= PyBytes_GET_SIZE(next_input);
+
+    /* How many decoded characters have been used up since the snapshot? */
+    if (self->decoded_chars_used == 0)  {
+        /* We haven't moved from the snapshot point. */
+        Py_DECREF(posobj);
+        return TextIOWrapper_buildCookie(&cookie);
+    }
+
+    chars_to_skip = self->decoded_chars_used;
+
+    /* Starting from the snapshot position, we will walk the decoder
+     * forward until it gives us enough decoded characters.
+     */
+    saved_state = PyObject_CallMethod(self->decoder, "getstate", NULL);
+    if (saved_state == NULL)
+        goto fail;
+
+    /* Note our initial start point. */
+    res = PyObject_CallMethod(self->decoder, "setstate",
+                              "((yi))", "", cookie.dec_flags);
+    if (res == NULL)
+        goto fail;
+
+    /* Feed the decoder one byte at a time.  As we go, note the
+     * nearest "safe start point" before the current location
+     * (a point where the decoder has nothing buffered, so seek()
+     * can safely start from there and advance to this location).
+     */
+    chars_decoded = 0;
+    input = PyBytes_AS_STRING(next_input);
+    input_end = input + PyBytes_GET_SIZE(next_input);
+    while (input < input_end) {
+        PyObject *state;
+        char *dec_buffer;
+        Py_ssize_t dec_buffer_len;
+        int dec_flags;
+
+        PyObject *decoded = PyObject_CallMethod(
+            self->decoder, "decode", "y#", &input, 1);
+        if (decoded == NULL)
+            goto fail;
+        assert (PyUnicode_Check(decoded));
+        chars_decoded += PyUnicode_GET_SIZE(decoded);
+        Py_DECREF(decoded);
+
+        cookie.bytes_to_feed += 1;
+
+        state = PyObject_CallMethod(self->decoder, "getstate", "NULL");
+        if (state == NULL)
+            goto fail;
+        if (!PyArg_Parse(state, "(y#i)", &dec_buffer, &dec_buffer_len, &dec_flags)) {
+            Py_DECREF(state);
+            goto fail;
+        }
+        Py_DECREF(state);
+
+        if (dec_buffer_len == 0) {
+            /* Decoder buffer is empty, so this is a safe start point. */
+            cookie.start_pos += cookie.bytes_to_feed;
+            chars_to_skip -= chars_decoded;
+            cookie.dec_flags = dec_flags;
+            cookie.bytes_to_feed = 0;
+            chars_decoded = 0;
+        }
+        if (chars_decoded >= chars_to_skip)
+            break;
+    }
+    if (chars_decoded < chars_to_skip) {
+        /* We didn't get enough decoded data; signal EOF to get more. */
+        PyObject *decoded = PyObject_CallMethod(
+            self->decoder, "decode", "yi", "", /* final = */ 1);
+        if (decoded == NULL)
+            goto fail;
+        assert (PyUnicode_Check(decoded));
+        chars_decoded += PyUnicode_GET_SIZE(decoded);
+        cookie.need_eof = 1;
+
+        if (chars_decoded < chars_to_skip) {
+            PyErr_SetString(PyExc_IOError,
+                            "can't reconstruct logical file position");
+            goto fail;
+        }
+    }
+
+    /* finally */
+    Py_XDECREF(posobj);
+    res = PyObject_CallMethod(self->decoder, "setstate", "(O)", saved_state);
+    Py_DECREF(saved_state);
+    if (res == NULL)
+        return NULL;
+
+    /* The returned cookie corresponds to the last safe start point. */
+    cookie.chars_to_skip = Py_SAFE_DOWNCAST(chars_to_skip, Py_ssize_t, int);
+    return TextIOWrapper_buildCookie(&cookie);
+
+  fail:
+    Py_XDECREF(posobj);
+    if (saved_state) {
+        PyObject *type, *value, *traceback;
+        PyErr_Fetch(&type, &value, &traceback);
+
+        res = PyObject_CallMethod(self->decoder, "setstate", "(O)", saved_state);
+        Py_DECREF(saved_state);
+        if (res == NULL)
+            return NULL;
+        Py_DECREF(res);
+
+        PyErr_Restore(type, value, traceback);
+    }
+    return NULL;
+}
 
 /* Inquiries */
 
@@ -1356,8 +1516,8 @@
     {"isatty", (PyCFunction)TextIOWrapper_isatty, METH_NOARGS},
 
     {"seek", (PyCFunction)TextIOWrapper_seek, METH_VARARGS},
-/*    {"tell", (PyCFunction)TextIOWrapper_tell, METH_NOARGS},
-    {"truncate", (PyCFunction)TextIOWrapper_truncate, METH_VARARGS},
+    {"tell", (PyCFunction)TextIOWrapper_tell, METH_NOARGS},
+/*    {"truncate", (PyCFunction)TextIOWrapper_truncate, METH_VARARGS},
     {"readinto", (PyCFunction)TextIOWrapper_readinto, METH_VARARGS},
     {"peek", (PyCFunction)TextIOWrapper_peek, METH_VARARGS},
     {"read1", (PyCFunction)TextIOWrapper_read1, METH_VARARGS},


More information about the Python-checkins mailing list