changeset 9826:c421fead68d3

Improve freadseek's efficiency after ungetc.
author Bruno Haible <bruno@clisp.org>
date Sun, 30 Mar 2008 12:25:40 +0200
parents 8620b596f2e0
children 32d08ee66484
files ChangeLog lib/freadseek.c modules/freadseek
diffstat 3 files changed, 75 insertions(+), 33 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+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
--- a/lib/freadseek.c
+++ b/lib/freadseek.c
@@ -22,28 +22,21 @@
 #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 @@
 			 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
+}
 
-      offset -= increment;
+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;
+
+      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 @@
 	{
 	  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;
 }
--- a/modules/freadseek
+++ b/modules/freadseek
@@ -6,10 +6,12 @@
 lib/freadseek.c
 
 Depends-on:
+freadahead
 freadptr
 lseek
 
 configure.ac:
+AC_REQUIRE([AC_C_INLINE])
 
 Makefile.am:
 lib_SOURCES += freadseek.c