]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
seq: support inf last item more generally/efficiently
authorPádraig Brady <P@draigBrady.com>
Sat, 11 Oct 2014 09:06:48 +0000 (10:06 +0100)
committerPádraig Brady <P@draigBrady.com>
Wed, 24 Jun 2015 16:03:35 +0000 (17:03 +0100)
* 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.

src/seq.c
tests/local.mk
tests/misc/seq-precision.sh [new file with mode: 0755]

index c8c0492b16da68da31ff9c52a2345ff0e43e9926..e8d4f18924bea37f644ac098051d60d88dcef696 100644 (file)
--- a/src/seq.c
+++ b/src/seq.c
@@ -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))
index 7b8c91edf1a0a8adc6bc76b478b25cf8f0c86ce8..d60f6cfecd08a0a6aaeacfdec4c0c2321e2ddc09 100644 (file)
@@ -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 (executable)
index 0000000..9ba6014
--- /dev/null
@@ -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