diff options
author | Pádraig Brady <P@draigBrady.com> | 2014-10-11 10:06:48 +0100 |
---|---|---|
committer | Pádraig Brady <P@draigBrady.com> | 2015-06-24 17:03:35 +0100 |
commit | fc6825b231b43c12738fb65a9309cdecb67f6833 (patch) | |
tree | 50bb0b64a18e3a6b636ecc11f8d41b5778488d45 | |
parent | 53883af063d13a628520615e12705bc6f3a549ec (diff) | |
download | coreutils-fc6825b231b43c12738fb65a9309cdecb67f6833.tar.xz |
seq: support inf last item more generally/efficiently
* src/seq.c (main): Call seq_fast for infinite last value.
This implicitly avoids format conversion on the
999999 -> 1000000 transition.
* src/seq.c (seq_fast): Generalize the buffer handling,
and adjust to handle the "inf" last value specifics.
* tests/misc/seq-precision.sh: A new test.
* tests/local.mk: Reference the new test.
-rw-r--r-- | src/seq.c | 85 | ||||
-rw-r--r-- | tests/local.mk | 1 | ||||
-rwxr-xr-x | tests/misc/seq-precision.sh | 37 |
3 files changed, 98 insertions, 25 deletions
@@ -411,52 +411,84 @@ trim_leading_zeros (char const *s) static bool seq_fast (char const *a, char const *b) { + bool inf = STREQ (b, "inf"); + /* Skip past any leading 0's. Without this, our naive cmp function would declare 000 to be larger than 99. */ a = trim_leading_zeros (a); b = trim_leading_zeros (b); size_t p_len = strlen (a); - size_t q_len = strlen (b); - size_t n = MAX (p_len, q_len); - char *p0 = xmalloc (n + 1); - char *p = memcpy (p0 + n - p_len, a, p_len + 1); - char *q0 = xmalloc (n + 1); - char *q = memcpy (q0 + n - q_len, b, q_len + 1); - - bool ok = cmp (p, p_len, q, q_len) <= 0; + size_t q_len = inf ? 0 : strlen (b); + + /* Allow for at least 31 digits without realloc. + 1 more than p_len is needed for the inf case. */ + size_t inc_size = MAX (MAX (p_len + 1, q_len), 31); + + /* Copy input strings (incl NUL) to end of new buffers. */ + char *p0 = xmalloc (inc_size + 1); + char *p = memcpy (p0 + inc_size - p_len, a, p_len + 1); + char *q; + char *q0; + if (! inf) + { + q0 = xmalloc (inc_size + 1); + q = memcpy (q0 + inc_size - q_len, b, q_len + 1); + } + else + q = q0 = NULL; + + bool ok = inf || cmp (p, p_len, q, q_len) <= 0; if (ok) { - /* Buffer at least this many numbers per fwrite call. - This gives a speed-up of more than 2x over the unbuffered code + /* Reduce number of fwrite calls which is seen to + give a speed-up of more than 2x over the unbuffered code when printing the first 10^9 integers. */ - enum {N = 40}; - char *buf = xmalloc (N * (n + 1)); - char const *buf_end = buf + N * (n + 1); + size_t buf_size = MAX (BUFSIZ, (inc_size + 1) * 2); + char *buf = xmalloc (buf_size); + char const *buf_end = buf + buf_size; - char *z = buf; + char *bufp = buf; /* Write first number to buffer. */ - z = mempcpy (z, p, p_len); + bufp = mempcpy (bufp, p, p_len); /* Append separator then number. */ - while (cmp (p, p_len, q, q_len) < 0) + while (inf || cmp (p, p_len, q, q_len) < 0) { - *z++ = *separator; + *bufp++ = *separator; incr (&p, &p_len); - z = mempcpy (z, p, p_len); + + /* Double up the buffers when needed for the inf case. */ + if (p_len == inc_size) + { + inc_size *= 2; + p0 = xrealloc (p0, inc_size + 1); + p = memmove (p0 + p_len, p0, p_len + 1); + + if (buf_size < (inc_size + 1) * 2) + { + size_t buf_offset = bufp - buf; + buf_size = (inc_size + 1) * 2; + buf = xrealloc (buf, buf_size); + buf_end = buf + buf_size; + bufp = buf + buf_offset; + } + } + + bufp = mempcpy (bufp, p, p_len); /* If no place for another separator + number then output buffer so far, and reset to start of buffer. */ - if (buf_end - (n + 1) < z) + if (buf_end - (p_len + 1) < bufp) { - fwrite (buf, z - buf, 1, stdout); - z = buf; + fwrite (buf, bufp - buf, 1, stdout); + bufp = buf; } } /* Write any remaining buffered output, and the terminator. */ - *z++ = *terminator; - fwrite (buf, z - buf, 1, stdout); + *bufp++ = *terminator; + fwrite (buf, bufp - buf, 1, stdout); IF_LINT (free (buf)); } @@ -593,7 +625,8 @@ main (int argc, char **argv) } } - if (first.precision == 0 && step.precision == 0 && last.precision == 0 + if (first.precision == 0 && step.precision == 0 + && (! isfinite (last.value) || last.precision == 0) && 0 <= first.value && step.value == 1 && 0 <= last.value && !equal_width && !format_str && strlen (separator) == 1) { @@ -601,7 +634,9 @@ main (int argc, char **argv) char *s2; if (asprintf (&s1, "%0.Lf", first.value) < 0) xalloc_die (); - if (asprintf (&s2, "%0.Lf", last.value) < 0) + if (! isfinite (last.value)) + s2 = xstrdup ("inf"); /* Ensure "inf" is used. */ + else if (asprintf (&s2, "%0.Lf", last.value) < 0) xalloc_die (); if (*s1 != '-' && *s2 != '-' && seq_fast (s1, s2)) diff --git a/tests/local.mk b/tests/local.mk index 7b8c91edf..d60f6cfec 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -236,6 +236,7 @@ all_tests = \ tests/misc/test.pl \ tests/misc/seq.pl \ tests/misc/seq-long-double.sh \ + tests/misc/seq-precision.sh \ tests/misc/head.pl \ tests/misc/head-elide-tail.pl \ tests/tail-2/tail-n0f.sh \ diff --git a/tests/misc/seq-precision.sh b/tests/misc/seq-precision.sh new file mode 100755 index 000000000..9ba601446 --- /dev/null +++ b/tests/misc/seq-precision.sh @@ -0,0 +1,37 @@ +#!/bin/sh +# Test for output with appropriate precision + +# Copyright (C) 2015 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 <http://www.gnu.org/licenses/>. + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ seq + +# Integer only. Before v8.24 this would switch output format +seq 999999 inf | head -n2 > out || fail=1 +printf "%s\n" 999999 1000000 > exp || framework_failure_ +compare exp out || fail=1 + +# Excercise buffer handling in non floating point output +for i in $(seq 100); do + n1="$(printf '%*s' $i '' | tr ' ' 9)" + n2="1$(echo $n1 | tr 9 0)" + + seq $n1 $n2 > out || fail=1 + printf "%s\n" "$n1" "$n2" > exp || framework_failure_ + compare exp out || fail=1 +done + +Exit $fail |