Mercurial > hg > octave-lojdl > gnulib-hg
changeset 13593:9134588a0ed4
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.
author | Giuseppe Scrivano <gscrivano@gnu.org> |
---|---|
date | Sat, 28 Aug 2010 16:12:37 +0200 |
parents | 6027af37caeb |
children | 4cd1a9ea04e8 |
files | ChangeLog lib/read-file.c modules/read-file |
diffstat | 3 files changed, 103 insertions(+), 39 deletions(-) [+] |
line wrap: on
line diff
--- 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.
--- a/lib/read-file.c +++ b/lib/read-file.c @@ -20,7 +20,16 @@ #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 @@ { char *buf = NULL; size_t alloc = 0; - size_t size = 0; - int save_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; - for (;;) - { - size_t count; - size_t requested; + 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; - if (size + BUFSIZ + 1 > alloc) - { - char *new_buf; + 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 += alloc / 2; - if (alloc < size + BUFSIZ + 1) - alloc = size + BUFSIZ + 1; + alloc = new_alloc; + if (alloc < size + BUFSIZ + 1) + alloc = size + BUFSIZ + 1; + + new_buf = realloc (buf, alloc); + if (!new_buf) + { + save_errno = errno; + break; + } - new_buf = realloc (buf, alloc); - if (!new_buf) - { - save_errno = errno; + 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; + buf[size] = '\0'; + *length = size; + return buf; + } + } - if (count != requested) - { - save_errno = errno; - if (ferror (stream)) - break; - buf[size] = '\0'; - *length = size; - return buf; - } - } - - free (buf); - errno = save_errno; - return NULL; + free (buf); + errno = save_errno; + return NULL; + } } static char *