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.
This commit is contained in:
Jonathan Wakely 2020-06-24 11:45:01 +01:00
parent 25920dd18a
commit eb0ff770e2
3 changed files with 99 additions and 7 deletions

View File

@ -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.

View File

@ -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) );

View File

@ -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();
}