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:
parent
25920dd18a
commit
eb0ff770e2
|
@ -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.
|
||||
|
|
|
@ -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) );
|
||||
|
|
|
@ -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();
|
||||
}
|
Loading…
Reference in New Issue