read-file: Avoid memory reallocations with regular files.
authorGiuseppe Scrivano <gscrivano@gnu.org>
Sat, 28 Aug 2010 14:12:37 +0000 (16:12 +0200)
committerBruno Haible <bruno@clisp.org>
Sat, 28 Aug 2010 14:14:23 +0000 (16:14 +0200)
* lib/read-file.c: Include <sys/stat.h>, <stdio.h>, <stdint.h>.
(fread_file): With regular files, use the remaining length as the
initial buffer size.  Check against overflow.
* modules/read-file (Depends-on): Add ftello, malloc-posix, stdint,
sys_stat.

ChangeLog
lib/read-file.c
modules/read-file

index 085fa74..8a32339 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2010-08-28  Giuseppe Scrivano  <gscrivano@gnu.org>
+            Eric Blake  <eblake@redhat.com>
+            Bruno Haible  <bruno@clisp.org>
+
+       read-file: Avoid memory reallocations with regular files.
+       * lib/read-file.c: Include <sys/stat.h>, <stdio.h>, <stdint.h>.
+       (fread_file): With regular files, use the remaining length as the
+       initial buffer size.  Check against overflow.
+       * modules/read-file (Depends-on): Add ftello, malloc-posix, stdint,
+       sys_stat.
+
 2010-08-28  Bruno Haible  <bruno@clisp.org>
 
        ftello: Relax license.
index 6b655db..27241b4 100644 (file)
 
 #include "read-file.h"
 
-/* Get realloc, free. */
+/* Get fstat.  */
+#include <sys/stat.h>
+
+/* Get ftello.  */
+#include <stdio.h>
+
+/* Get SIZE_MAX.  */
+#include <stdint.h>
+
+/* Get malloc, realloc, free. */
 #include <stdlib.h>
 
 /* Get errno. */
@@ -36,50 +45,90 @@ fread_file (FILE * stream, size_t * length)
 {
   char *buf = NULL;
   size_t alloc = 0;
-  size_t size = 0;
-  int save_errno;
-
-  for (;;)
-    {
-      size_t count;
-      size_t requested;
-
-      if (size + BUFSIZ + 1 > alloc)
-        {
-          char *new_buf;
 
-          alloc += alloc / 2;
-          if (alloc < size + BUFSIZ + 1)
-            alloc = size + BUFSIZ + 1;
-
-          new_buf = realloc (buf, alloc);
-          if (!new_buf)
-            {
-              save_errno = errno;
+  /* For a regular file, allocate a buffer that has exactly the right
+     size.  This avoids the need to do dynamic reallocations later.  */
+  {
+    struct stat st;
+
+    if (fstat (fileno (stream), &st) >= 0 && S_ISREG (st.st_mode))
+      {
+        off_t pos = ftello (stream);
+
+        if (pos >= 0 && pos < st.st_size)
+          {
+            off_t alloc_off = st.st_size - pos;
+
+            if (SIZE_MAX <= alloc_off)
+              {
+                errno = ENOMEM;
+                return NULL;
+              }
+
+            alloc = alloc_off + 1;
+
+            buf = malloc (alloc);
+            if (!buf)
+              /* errno is ENOMEM.  */
+              return NULL;
+          }
+      }
+  }
+
+  {
+    size_t size = 0; /* number of bytes read so far */
+    int save_errno;
+
+    for (;;)
+      {
+        size_t count;
+        size_t requested;
+
+        if (size + BUFSIZ + 1 > alloc)
+          {
+            char *new_buf;
+            size_t new_alloc = alloc + alloc / 2;
+
+            /* Check against overflow.  */
+            if (new_alloc < alloc)
+              {
+                save_errno = ENOMEM;
+                break;
+              }
+
+            alloc = new_alloc;
+            if (alloc < size + BUFSIZ + 1)
+              alloc = size + BUFSIZ + 1;
+
+            new_buf = realloc (buf, alloc);
+            if (!new_buf)
+              {
+                save_errno = errno;
+                break;
+              }
+
+            buf = new_buf;
+          }
+
+        requested = alloc - size - 1;
+        count = fread (buf + size, 1, requested, stream);
+        size += count;
+
+        if (count != requested)
+          {
+            save_errno = errno;
+            if (ferror (stream))
               break;
-            }
-
-          buf = new_buf;
-        }
-
-      requested = alloc - size - 1;
-      count = fread (buf + size, 1, requested, stream);
-      size += count;
-
-      if (count != requested)
-        {
-          save_errno = errno;
-          if (ferror (stream))
-            break;
-          buf[size] = '\0';
-          *length = size;
-          return buf;
-        }
-    }
-
-  free (buf);
-  errno = save_errno;
-  return NULL;
+            buf[size] = '\0';
+            *length = size;
+            return buf;
+          }
+      }
+
+    free (buf);
+    errno = save_errno;
+    return NULL;
+  }
 }
 
 static char *
index e302940..6ad941b 100644 (file)
@@ -7,7 +7,11 @@ lib/read-file.c
 m4/read-file.m4
 
 Depends-on:
+ftello
+malloc-posix
 realloc-posix
+stdint
+sys_stat
 
 configure.ac:
 gl_FUNC_READ_FILE