Don't flush write buffer for ftell
authorSiddhesh Poyarekar <siddhesh@redhat.com>
Fri, 28 Sep 2012 13:07:23 +0000 (18:37 +0530)
committerSiddhesh Poyarekar <siddhesh@redhat.com>
Fri, 28 Sep 2012 13:08:14 +0000 (18:38 +0530)
[BZ #5298]
Use write pointer state along with the file offset and/or the read
pointers to get the current file position.

ChangeLog
NEWS
libio/fileops.c
libio/wfileops.c

index 255e075..7010a8e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
 2012-09-28  Siddhesh Poyarekar  <siddhesh@redhat.com>
 
+       [BZ #5298]
+       * libio/fileops.c (_IO_new_file_seekoff): Don't flush buffer
+       for ftell.  Compute offsets from write pointers instead.
+       * libio/wfileops.c (_IO_wfile_seekoff): Likewise.
+
+2012-09-28  Siddhesh Poyarekar  <siddhesh@redhat.com>
+
        [BZ #14543]
        * libio/Makefile (tests): New test case tst-fseek.
        * libio/tst-fseek.c: New test case to verify that fseek/ftell
diff --git a/NEWS b/NEWS
index 70ddcbe..b8a01ef 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -9,12 +9,13 @@ Version 2.17
 
 * The following bugs are resolved with this release:
 
-  1349, 3479, 5044, 5400, 6778, 6808, 9685, 9914, 10014, 10038, 11607,
-  13412, 13542, 13629, 13679, 13696, 13717, 13741, 13939, 13966, 14042,
-  14090, 14150, 14151, 14154, 14157, 14166, 14173, 14195, 14237, 14252,
-  14283, 14298, 14303, 14307, 14328, 14331, 14336, 14337, 14347, 14349,
-  14376, 14459, 14476, 14505, 14510, 14516, 14518, 14519, 14530, 14532,
-  14538, 14543, 14544, 14545, 14562, 14576, 14579, 14583, 14587, 14621.
+  1349, 3479, 5044, 5298, 5400, 6778, 6808, 9685, 9914, 10014, 10038,
+  11607, 13412, 13542, 13629, 13679, 13696, 13717, 13741, 13939, 13966,
+  14042, 14090, 14150, 14151, 14154, 14157, 14166, 14173, 14195, 14237,
+  14252, 14283, 14298, 14303, 14307, 14328, 14331, 14336, 14337, 14347,
+  14349, 14376, 14459, 14476, 14505, 14510, 14516, 14518, 14519, 14530,
+  14532, 14538, 14543, 14544, 14545, 14562, 14576, 14579, 14583, 14587,
+  14621.
 
 * Support for STT_GNU_IFUNC symbols added for s390 and s390x.
   Optimized versions of memcpy, memset, and memcmp added for System z10 and
index e22efde..173091c 100644 (file)
@@ -978,6 +978,9 @@ _IO_new_file_seekoff (fp, offset, dir, mode)
   int must_be_exact = (fp->_IO_read_base == fp->_IO_read_end
                       && fp->_IO_write_base == fp->_IO_write_ptr);
 
+  bool was_writing = (fp->_IO_write_ptr > fp->_IO_write_base
+                     || _IO_in_put_mode (fp));
+
   if (mode == 0)
     dir = _IO_seek_cur, offset = 0; /* Don't move any pointers. */
 
@@ -988,10 +991,8 @@ _IO_new_file_seekoff (fp, offset, dir, mode)
      which assumes file_ptr() is eGptr.  Anyway, since we probably
      end up flushing when we close(), it doesn't make much difference.)
      FIXME: simulate mem-mapped files. */
-
-  if (fp->_IO_write_ptr > fp->_IO_write_base || _IO_in_put_mode (fp))
-    if (_IO_switch_to_get_mode (fp))
-      return EOF;
+  else if (was_writing && _IO_switch_to_get_mode (fp))
+    return EOF;
 
   if (fp->_IO_buf_base == NULL)
     {
@@ -1010,7 +1011,17 @@ _IO_new_file_seekoff (fp, offset, dir, mode)
     {
     case _IO_seek_cur:
       /* Adjust for read-ahead (bytes is buffer). */
-      offset -= fp->_IO_read_end - fp->_IO_read_ptr;
+      if (mode != 0 || !was_writing)
+       offset -= fp->_IO_read_end - fp->_IO_read_ptr;
+      else
+       {
+         /* _IO_read_end coincides with fp._offset, so the actual file position
+            is fp._offset - (_IO_read_end - new_write_ptr).  This is fine
+            even if fp._offset is not set, since fp->_IO_read_end is then at
+            _IO_buf_base and this adjustment is for unbuffered output.  */
+         offset -= fp->_IO_read_end - fp->_IO_write_ptr;
+       }
+
       if (fp->_offset == _IO_pos_BAD)
        {
          if (mode != 0)
index d3f46b2..1087e8d 100644 (file)
@@ -613,6 +613,10 @@ _IO_wfile_seekoff (fp, offset, dir, mode)
                       && (fp->_wide_data->_IO_write_base
                           == fp->_wide_data->_IO_write_ptr));
 
+  bool was_writing = ((fp->_wide_data->_IO_write_ptr
+                      > fp->_wide_data->_IO_write_base)
+                     || _IO_in_put_mode (fp));
+
   if (mode == 0)
     {
       /* XXX For wide stream with backup store it is not very
@@ -644,11 +648,8 @@ _IO_wfile_seekoff (fp, offset, dir, mode)
      which assumes file_ptr() is eGptr.  Anyway, since we probably
      end up flushing when we close(), it doesn't make much difference.)
      FIXME: simulate mem-mapped files. */
-
-  if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base
-      || _IO_in_put_mode (fp))
-    if (_IO_switch_to_wget_mode (fp))
-      return WEOF;
+  else if (was_writing && _IO_switch_to_wget_mode (fp))
+    return WEOF;
 
   if (fp->_wide_data->_IO_buf_base == NULL)
     {
@@ -679,29 +680,104 @@ _IO_wfile_seekoff (fp, offset, dir, mode)
       cv = fp->_codecvt;
       clen = (*cv->__codecvt_do_encoding) (cv);
 
-      if (clen > 0)
+      if (mode != 0 || !was_writing)
        {
-         offset -= (fp->_wide_data->_IO_read_end
-                    - fp->_wide_data->_IO_read_ptr) * clen;
-         /* Adjust by readahead in external buffer.  */
-         offset -= fp->_IO_read_end - fp->_IO_read_ptr;
+         if (clen > 0)
+           {
+             offset -= (fp->_wide_data->_IO_read_end
+                        - fp->_wide_data->_IO_read_ptr) * clen;
+             /* Adjust by readahead in external buffer.  */
+             offset -= fp->_IO_read_end - fp->_IO_read_ptr;
+           }
+         else
+           {
+             int nread;
+
+           flushed:
+             delta = (fp->_wide_data->_IO_read_ptr
+                      - fp->_wide_data->_IO_read_base);
+             fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
+             nread = (*cv->__codecvt_do_length) (cv,
+                                                 &fp->_wide_data->_IO_state,
+                                                 fp->_IO_read_base,
+                                                 fp->_IO_read_end, delta);
+             fp->_IO_read_ptr = fp->_IO_read_base + nread;
+             fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_read_ptr;
+             offset -= fp->_IO_read_end - fp->_IO_read_base - nread;
+           }
        }
       else
        {
-         int nread;
+         char *new_write_ptr = fp->_IO_write_ptr;
 
-         delta = fp->_wide_data->_IO_read_ptr - fp->_wide_data->_IO_read_base;
-         fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
-         nread = (*cv->__codecvt_do_length) (cv, &fp->_wide_data->_IO_state,
-                                             fp->_IO_read_base,
-                                             fp->_IO_read_end, delta);
-         fp->_IO_read_ptr = fp->_IO_read_base + nread;
-         fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_read_ptr;
-         offset -= fp->_IO_read_end - fp->_IO_read_base - nread;
+         if (clen > 0)
+           offset += (fp->_wide_data->_IO_write_ptr
+                      - fp->_wide_data->_IO_write_base) / clen;
+         else
+           {
+             enum __codecvt_result status;
+             delta = (fp->_wide_data->_IO_write_ptr
+                      - fp->_wide_data->_IO_write_base);
+             const wchar_t *write_base = fp->_wide_data->_IO_write_base;
+
+             /* FIXME: This actually ends up in two iterations of conversion,
+                one here and the next when the buffer actually gets flushed.
+                It may be possible to optimize this in future so that
+                wdo_write identifies already converted content and does not
+                redo it.  In any case, this is much better than having to
+                flush buffers for every ftell.  */
+             do
+               {
+                 /* Ugh, no point trying to avoid the flush.  Just do it
+                    and go back to how it was with the read mode.  */
+                 if (delta > 0 && new_write_ptr == fp->_IO_buf_end)
+                   {
+                     if (_IO_switch_to_wget_mode (fp))
+                       return WEOF;
+                     goto flushed;
+                   }
+
+                 const wchar_t *new_wbase = fp->_wide_data->_IO_write_base;
+                 fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
+                 status = (*cv->__codecvt_do_out) (cv,
+                                                   &fp->_wide_data->_IO_state,
+                                                   write_base,
+                                                   write_base + delta,
+                                                   &new_wbase,
+                                                   new_write_ptr,
+                                                   fp->_IO_buf_end,
+                                                   &new_write_ptr);
+
+                 delta -= new_wbase - write_base;
+
+                 /* If there was an error, then return WEOF.
+                    TODO: set buffer state.  */
+                 if (__builtin_expect (status == __codecvt_error, 0))
+                     return WEOF;
+               }
+             while (delta > 0);
+           }
+
+         /* _IO_read_end coincides with fp._offset, so the actual file position
+            is fp._offset - (_IO_read_end - new_write_ptr).  This is fine
+            even if fp._offset is not set, since fp->_IO_read_end is then at
+            _IO_buf_base and this adjustment is for unbuffered output.  */
+         offset -= fp->_IO_read_end - new_write_ptr;
        }
 
       if (fp->_offset == _IO_pos_BAD)
-       goto dumb;
+       {
+         if (mode != 0)
+           goto dumb;
+         else
+           {
+             result = _IO_SYSSEEK (fp, 0, dir);
+             if (result == EOF)
+               return result;
+             fp->_offset = result;
+           }
+       }
+
       /* Make offset absolute, assuming current pointer is file_ptr(). */
       offset += fp->_offset;