From 5bcad6426278104f121155e7591b34a18fb912fd Mon Sep 17 00:00:00 2001 From: Ondrej Oprala
Date: Tue, 22 Jan 2013 14:21:23 +0100 Subject: [PATCH] dd: Postpone buffer allocations if possible. * src/dd.c: Add new static global variable ibuf. (alloc_ibuf, alloc_obuf): New functions with code factored out from dd_copy. (dd_copy): Call the new functions to allocate memory for ibuf and obuf when necessary. * tests/dd/no-allocate.sh: New test. * tests/local.mk: Add the test. --- src/dd.c | 91 ++++++++++++++++++++++++++++++------------------- tests/dd/no-allocate.sh | 29 ++++++++++++++++ tests/local.mk | 1 + 3 files changed, 86 insertions(+), 35 deletions(-) create mode 100755 tests/dd/no-allocate.sh diff --git a/src/dd.c b/src/dd.c index ef5664b..da1d791 100644 --- a/src/dd.c +++ b/src/dd.c @@ -236,6 +236,9 @@ static uintmax_t r_truncate = 0; static char newline_character = '\n'; static char space_character = ' '; +/* Input buffer. */ +static char *ibuf; + /* Output buffer. */ static char *obuf; @@ -1833,16 +1836,52 @@ human_size (size_t n) return human_readable (n, hbuf, human_opts, 1, 1); } +static void +alloc_ibuf (void) +{ + char *real_buf = malloc (input_blocksize + INPUT_BLOCK_SLOP); + if (!real_buf) + error (EXIT_FAILURE, 0, + _("memory exhausted by input buffer of size %zu bytes (%s)"), + input_blocksize, human_size (input_blocksize)); + + real_buf += SWAB_ALIGN_OFFSET; /* allow space for swab */ + + ibuf = ptr_align (real_buf, page_size); +} + +static void +alloc_obuf (void) +{ + if (conversions_mask & C_TWOBUFS) + { + /* Page-align the output buffer, too. */ + char *real_obuf = malloc (output_blocksize + OUTPUT_BLOCK_SLOP); + if (!real_obuf) + error (EXIT_FAILURE, 0, + _("memory exhausted by output buffer of size %zu bytes (%s)"), + output_blocksize, human_size (output_blocksize)); + obuf = ptr_align (real_obuf, page_size); + } + else + { + if (!ibuf) + alloc_ibuf (); + obuf = ibuf; + } + + /* Write a sentinel to the slop after the buffer, + to allow efficient checking for NUL blocks. */ + assert (sizeof (uintptr_t) <= OUTPUT_BLOCK_SLOP); + memset (obuf + output_blocksize, 1, sizeof (uintptr_t)); +} + /* The main loop. */ static int dd_copy (void) { - char *ibuf, *bufstart; /* Input buffer. */ - /* These are declared static so that even though we don't free the - buffers, valgrind will recognize that there is no "real" leak. */ - static char *real_buf; /* real buffer address before alignment */ - static char *real_obuf; + char *bufstart; /* Input buffer. */ ssize_t nread; /* Bytes read in the current block. */ /* If nonzero, then the previously read block was partial and @@ -1869,37 +1908,14 @@ dd_copy (void) It is necessary when accessing raw (i.e. character special) disk devices on Unixware or other SVR4-derived system. */ - real_buf = malloc (input_blocksize + INPUT_BLOCK_SLOP); - if (!real_buf) - error (EXIT_FAILURE, 0, - _("memory exhausted by input buffer of size %zu bytes (%s)"), - input_blocksize, human_size (input_blocksize)); - - ibuf = real_buf; - ibuf += SWAB_ALIGN_OFFSET; /* allow space for swab */ - - ibuf = ptr_align (ibuf, page_size); - - if (conversions_mask & C_TWOBUFS) - { - /* Page-align the output buffer, too. */ - real_obuf = malloc (output_blocksize + OUTPUT_BLOCK_SLOP); - if (!real_obuf) - error (EXIT_FAILURE, 0, - _("memory exhausted by output buffer of size %zu bytes (%s)"), - output_blocksize, human_size (output_blocksize)); - obuf = ptr_align (real_obuf, page_size); - } - else - { - real_obuf = NULL; - obuf = ibuf; - } + /* Delay buffer allocation if possible. */ + if ((skip_records > OFF_T_MAX / input_blocksize) + || 0 > skip_via_lseek (input_file, STDIN_FILENO, 0, SEEK_CUR)) + alloc_ibuf (); - /* Write a sentinel to the slop after the buffer, - to allow efficient checking for NUL blocks. */ - assert (sizeof (uintptr_t) <= OUTPUT_BLOCK_SLOP); - memset (obuf + output_blocksize, 1, sizeof (uintptr_t)); + if ((seek_records > OFF_T_MAX / output_blocksize) + || 0 > skip_via_lseek (output_file, STDOUT_FILENO, 0, SEEK_CUR)) + alloc_obuf (); if (skip_records != 0 || skip_bytes != 0) { @@ -1955,6 +1971,11 @@ dd_copy (void) if (max_records == 0 && max_bytes == 0) return exit_status; + if (!ibuf) + alloc_ibuf (); + if (!obuf) + alloc_obuf (); + while (1) { if (r_partial + r_full >= max_records + !!max_bytes) diff --git a/tests/dd/no-allocate.sh b/tests/dd/no-allocate.sh new file mode 100755 index 0000000..bf40094 --- /dev/null +++ b/tests/dd/no-allocate.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# make sure that dd doesn't allocate memory unnecessarily + +# Copyright (C) 2013 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see