From: Giuseppe Scrivano Date: Sat, 28 Aug 2010 14:12:37 +0000 (+0200) Subject: read-file: Avoid memory reallocations with regular files. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3e53db21df4287b43a46ad27fc2fac56b9194ffc;p=pspp read-file: Avoid memory reallocations with regular files. * lib/read-file.c: Include , , . (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. --- diff --git a/ChangeLog b/ChangeLog index 085fa74e0c..8a32339035 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2010-08-28 Giuseppe Scrivano + Eric Blake + Bruno Haible + + read-file: Avoid memory reallocations with regular files. + * lib/read-file.c: Include , , . + (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 ftello: Relax license. diff --git a/lib/read-file.c b/lib/read-file.c index 6b655db5d9..27241b4504 100644 --- a/lib/read-file.c +++ b/lib/read-file.c @@ -20,7 +20,16 @@ #include "read-file.h" -/* Get realloc, free. */ +/* Get fstat. */ +#include + +/* Get ftello. */ +#include + +/* Get SIZE_MAX. */ +#include + +/* Get malloc, realloc, free. */ #include /* 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 * diff --git a/modules/read-file b/modules/read-file index e302940010..6ad941b270 100644 --- a/modules/read-file +++ b/modules/read-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