Improve freadseek's efficiency after ungetc.
authorBruno Haible <bruno@clisp.org>
Sun, 30 Mar 2008 10:25:40 +0000 (12:25 +0200)
committerBruno Haible <bruno@clisp.org>
Sun, 30 Mar 2008 10:25:40 +0000 (12:25 +0200)
ChangeLog
lib/freadseek.c
modules/freadseek

index e021282..0e88c4b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,15 @@
 2008-03-30  Bruno Haible  <bruno@clisp.org>
 
+       Improve freadseek's efficiency after ungetc.
+       * lib/freadseek.c: Include freadahead.h.
+       (freadptrinc): New function, extracted from freadseek.
+       (freadseek): Use it in a loop. Use freadahead to determine the number
+       of loop iterations.
+       * modules/freadseek (Depends-on): Add freadahead.
+       (configure.ac): Require AC_C_INLINE.
+
+2008-03-30  Bruno Haible  <bruno@clisp.org>
+
        * lib/freadseek.c (freadseek): Don't ignore the return value of
        freadptr.
 
index 9e56ccc..c4078d4 100644 (file)
 #include <stdlib.h>
 #include <unistd.h>
 
+#include "freadahead.h"
 #include "freadptr.h"
 
-int
-freadseek (FILE *fp, size_t offset)
+/* Increment the in-memory pointer.  INCREMENT must be at most the buffer size
+   returned by freadptr().
+   This is very cheap (no system calls).  */
+static inline void
+freadptrinc (FILE *fp, size_t increment)
 {
-  size_t buffered;
-  int fd;
-
-  if (offset == 0)
-    return 0;
-
-  /* Increment the in-memory pointer.  This is very cheap (no system calls).  */
-  if (freadptr (fp, &buffered) != NULL && buffered > 0)
-    {
-      size_t increment = (buffered < offset ? buffered : offset);
-
-      /* Keep this code in sync with freadptr!  */
+  /* Keep this code in sync with freadptr!  */
 #if defined _IO_ferror_unlocked     /* GNU libc, BeOS */
-      fp->_IO_read_ptr += increment;
+  fp->_IO_read_ptr += increment;
 #elif defined __sferror             /* FreeBSD, NetBSD, OpenBSD, MacOS X, Cygwin */
-      fp->_p += increment;
-      fp->_r -= increment;
+  fp->_p += increment;
+  fp->_r -= increment;
 #elif defined _IOERR                /* AIX, HP-UX, IRIX, OSF/1, Solaris, mingw */
 # if defined __sun && defined _LP64 /* Solaris/{SPARC,AMD64} 64-bit */
 #  define fp_ ((struct { unsigned char *_ptr; \
@@ -53,27 +46,63 @@ freadseek (FILE *fp, size_t offset)
                         int _file; \
                         unsigned int _flag; \
                       } *) fp)
-      fp_->_ptr += increment;
-      fp_->_cnt -= increment;
+  fp_->_ptr += increment;
+  fp_->_cnt -= increment;
 # else
-      fp->_ptr += increment;
-      fp->_cnt -= increment;
+  fp->_ptr += increment;
+  fp->_cnt -= increment;
 # endif
 #elif defined __UCLIBC__            /* uClibc */
 # ifdef __STDIO_BUFFERS
-      fp->__bufpos += increment;
+  fp->__bufpos += increment;
 # else
-      abort ();
+  abort ();
 # endif
 #elif defined __QNX__               /* QNX */
-      fp->_Next += increment;
+  fp->_Next += increment;
 #else
  #error "Please port gnulib freadseek.c to your platform! Look at the definition of getc, getc_unlocked on your system, then report this to bug-gnulib."
 #endif
+}
+
+int
+freadseek (FILE *fp, size_t offset)
+{
+  size_t total_buffered;
+  int fd;
+
+  if (offset == 0)
+    return 0;
+
+  /* Seek over the already read and buffered input as quickly as possible,
+     without doing any system calls.  */
+  total_buffered = freadahead (fp);
+  /* This loop is usually executed at most twice: once for ungetc buffer (if
+     present) and once for the main buffer.  */
+  while (total_buffered > 0)
+    {
+      size_t buffered;
 
-      offset -= increment;
+      if (freadptr (fp, &buffered) != NULL && buffered > 0)
+       {
+         size_t increment = (buffered < offset ? buffered : offset);
+
+         freadptrinc (fp, increment);
+         offset -= increment;
+         if (offset == 0)
+           return 0;
+         total_buffered -= increment;
+         if (total_buffered == 0)
+           break;
+       }
+      /* Read one byte.  If we were reading from the ungetc buffer, this
+        switches the stream back to the main buffer.  */
+      if (fgetc (fp) == EOF)
+       goto eof;
+      offset--;
       if (offset == 0)
        return 0;
+      total_buffered--;
     }
 
   /* Test whether the stream is seekable or not.  */
@@ -93,18 +122,19 @@ freadseek (FILE *fp, size_t offset)
        {
          size_t count = (sizeof (buf) < offset ? sizeof (buf) : offset);
          if (fread (buf, 1, count, fp) < count)
-           {
-             if (ferror (fp))
-               /* EOF, or error before or while reading.  */
-               return EOF;
-             else
-               /* Encountered EOF.  */
-               return 0;
-           }
+           goto eof;
          offset -= count;
        }
       while (offset > 0);
 
       return 0;
    }
+
+ eof:
+  /* EOF, or error before or while reading.  */
+  if (ferror (fp))
+    return EOF;
+  else
+    /* Encountered EOF.  */
+    return 0;
 }
index c9a8555..8998421 100644 (file)
@@ -6,10 +6,12 @@ lib/freadseek.h
 lib/freadseek.c
 
 Depends-on:
+freadahead
 freadptr
 lseek
 
 configure.ac:
+AC_REQUIRE([AC_C_INLINE])
 
 Makefile.am:
 lib_SOURCES += freadseek.c