[gcc(refs/users/giulianob/heads/autopar_rebase2)] libstdc++: Fix std::from_chars to ignore leading zeros in base 2

Giuliano Belinassi giulianob@gcc.gnu.org
Tue Aug 18 00:09:44 GMT 2020


https://gcc.gnu.org/g:d74868a2dcf87bd90def604a1506d777074cf2a5

commit d74868a2dcf87bd90def604a1506d777074cf2a5
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Wed Jun 24 11:45:01 2020 +0100

    libstdc++: Fix std::from_chars to ignore leading zeros in base 2
    
    The parser for binary numbers returned an error if the entire string
    contains more digits than the result type. Leading zeros should be
    ignored.
    
    libstdc++-v3/ChangeLog:
    
            * include/std/charconv (__from_chars_binary): Ignore leading zeros.
            * testsuite/20_util/from_chars/1.cc: Check "0x1" for all bases,
            not just 10 and 16.
            * testsuite/20_util/from_chars/3.cc: New test.

Diff:
---
 libstdc++-v3/include/std/charconv              |  8 ++-
 libstdc++-v3/testsuite/20_util/from_chars/1.cc | 19 +++++--
 libstdc++-v3/testsuite/20_util/from_chars/3.cc | 79 ++++++++++++++++++++++++++
 3 files changed, 99 insertions(+), 7 deletions(-)

diff --git a/libstdc++-v3/include/std/charconv b/libstdc++-v3/include/std/charconv
index 8fbf64058ee..b725e5d2afd 100644
--- a/libstdc++-v3/include/std/charconv
+++ b/libstdc++-v3/include/std/charconv
@@ -417,7 +417,11 @@ namespace __detail
       static_assert(is_unsigned<_Tp>::value, "implementation bug");
 
       const ptrdiff_t __len = __last - __first;
-      int __i = 0;
+      ptrdiff_t __i = 0;
+      while (__i < __len && __first[__i] == '0')
+	++__i;
+      const ptrdiff_t __leading_zeroes = __i;
+
       while (__i < __len)
 	{
 	  const unsigned char __c = (unsigned)__first[__i] - '0';
@@ -428,7 +432,7 @@ namespace __detail
 	  __i++;
 	}
       __first += __i;
-      return __i <= __detail::__int_limits<_Tp>::digits;
+      return (__i - __leading_zeroes) <= __detail::__int_limits<_Tp>::digits;
     }
 
   /// std::from_chars implementation for integers in bases 3 to 10.
diff --git a/libstdc++-v3/testsuite/20_util/from_chars/1.cc b/libstdc++-v3/testsuite/20_util/from_chars/1.cc
index 916025bc7c6..ad5d50e67b3 100644
--- a/libstdc++-v3/testsuite/20_util/from_chars/1.cc
+++ b/libstdc++-v3/testsuite/20_util/from_chars/1.cc
@@ -31,7 +31,8 @@ check_from_chars(I expected, std::string s, int base = 0, char term = '\0')
   std::from_chars_result r = base == 0
     ? std::from_chars(begin, end, val)
     : std::from_chars(begin, end, val, base);
-  return r.ec == std::errc{} && (r.ptr == end || *r.ptr == term) && val == expected;
+  return r.ec == std::errc{} && (r.ptr == end || *r.ptr == term)
+    && val == expected;
 }
 
 #include <climits>
@@ -52,10 +53,18 @@ void
 test02()
 {
   // "0x" parsed as "0" not as hex prefix:
-  VERIFY( check_from_chars(0, "0x1", 10, 'x') );
-  VERIFY( check_from_chars(0, "0X1", 10, 'X') );
-  VERIFY( check_from_chars(0, "0x1", 16, 'x') );
-  VERIFY( check_from_chars(0, "0X1", 16, 'X') );
+  for (int base = 2; base < 34; ++base)
+  {
+    VERIFY( check_from_chars(0, "0x1", base, 'x') );
+    VERIFY( check_from_chars(0, "0X1", base, 'X') );
+  }
+
+  VERIFY( check_from_chars(1123, "0x1", 34) );
+  VERIFY( check_from_chars(1123, "0X1", 34) );
+  VERIFY( check_from_chars(1156, "0x1", 35) );
+  VERIFY( check_from_chars(1156, "0X1", 35) );
+  VERIFY( check_from_chars(1189, "0x1", 36) );
+  VERIFY( check_from_chars(1189, "0X1", 36) );
 
   VERIFY( check_from_chars(1155, "xx", 34) );
   VERIFY( check_from_chars(1155, "XX", 34) );
diff --git a/libstdc++-v3/testsuite/20_util/from_chars/3.cc b/libstdc++-v3/testsuite/20_util/from_chars/3.cc
new file mode 100644
index 00000000000..9d4a77f5c31
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/from_chars/3.cc
@@ -0,0 +1,79 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library 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, or (at your option)
+// any later version.
+
+// This library 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 library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do run { target c++14 } }
+
+#include <charconv>
+#include <string>
+#include <testsuite_hooks.h>
+
+#ifdef DEBUG
+#include <stdio.h>
+#endif
+
+long long
+read(const char* first, const char* last, int base)
+{
+  long long val = 0;
+  long long place = 1;
+  while (last > first)
+  {
+    val += (*--last - '0') * place;
+    place *= base;
+  }
+  return val;
+}
+
+void
+test01()
+{
+  std::from_chars_result res;
+  long long val;
+  for (auto s : { "10001", "10010", "10011", "10101", "10110", "10111",
+		  "11001", "11010", "11011", "11101", "11110", "11111" })
+  {
+    std::string ss[2] = { s, std::string(64, '0') + s };
+    for (const auto& str : ss)
+    {
+      const char* first = str.data();
+      for (int base = 2; base < 37; ++base)
+      {
+	const char* last = str.data() + str.length();
+	for (size_t n = 0; n < ss[0].length(); ++n)
+	{
+#ifdef DEBUG
+	  printf("Parsing \"%.*s\" in base %d\n", int(last - first), first,
+		 base);
+#endif
+	  res = std::from_chars(first, last, val, base);
+	  VERIFY( res.ptr == last );
+	  VERIFY( res.ec == std::errc{} );
+	  VERIFY( val == read(first, last, base) );
+	  // Test again with shorter string to check from_chars doesn't read
+	  // the digits past the last pointer.
+	  --last;
+	}
+      }
+    }
+  }
+}
+
+int
+main()
+{
+  test01();
+}


More information about the Libstdc++-cvs mailing list