Fix PR36914 - num_get::get(unsigned) incorrectly handles negative numbers.

This patch corrects num_get for unsigned types to support strings
with a leading `-` character. According to the standard the
number should be parsed as an unsigned integer and then
negated.

llvm-svn: 328751
This commit is contained in:
Eric Fiselier 2018-03-29 01:18:53 +00:00
parent 446f498de4
commit bc3a21f823
3 changed files with 180 additions and 17 deletions

View File

@ -768,10 +768,10 @@ __num_get_unsigned_integral(const char* __a, const char* __a_end,
{
if (__a != __a_end)
{
if (*__a == '-')
{
__err = ios_base::failbit;
return 0;
const bool __negate = *__a == '-';
if (__negate && ++__a == __a_end) {
__err = ios_base::failbit;
return 0;
}
typename remove_reference<decltype(errno)>::type __save_errno = errno;
errno = 0;
@ -785,13 +785,14 @@ __num_get_unsigned_integral(const char* __a, const char* __a_end,
__err = ios_base::failbit;
return 0;
}
else if (__current_errno == ERANGE ||
numeric_limits<_Tp>::max() < __ll)
else if (__current_errno == ERANGE || numeric_limits<_Tp>::max() < __ll)
{
__err = ios_base::failbit;
return numeric_limits<_Tp>::max();
}
return static_cast<_Tp>(__ll);
_Tp __res = static_cast<_Tp>(__ll);
if (__negate) __res = -__res;
return __res;
}
__err = ios_base::failbit;
return 0;

View File

@ -15,9 +15,17 @@
using namespace std;
template <class T>
bool check_stream_failed(std::string const& val) {
istringstream ss(val);
T result;
return !(ss >> result);
}
template<typename T>
void check_limits()
{
const bool is_unsigned = std::is_unsigned<T>::value;
T minv = numeric_limits<T>::min();
T maxv = numeric_limits<T>::max();
@ -36,17 +44,12 @@ void check_limits()
assert(new_minv == minv);
assert(new_maxv == maxv);
if(mins == "0")
mins = "-1";
else
mins[mins.size() - 1]++;
maxs[maxs.size() - 1]++;
istringstream maxoss2(maxs), minoss2(mins);
assert(! (maxoss2 >> new_maxv));
assert(! (minoss2 >> new_minv));
assert(check_stream_failed<T>(maxs));
if (!is_unsigned) {
mins[mins.size() - 1]++;
assert(check_stream_failed<T>(mins));
}
}
int main(void)

View File

@ -0,0 +1,159 @@
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// <locale>
// class num_get<charT, InputIterator>
// iter_type get(iter_type in, iter_type end, ios_base&,
// ios_base::iostate& err, unsigned int& v) const;
#include <locale>
#include <ios>
#include <cassert>
#include <streambuf>
#include <sstream>
#include <iostream>
#include "test_iterators.h"
typedef std::num_get<char, input_iterator<const char*> > F;
class my_facet
: public F
{
public:
explicit my_facet(std::size_t refs = 0)
: F(refs) {}
};
template <class T>
std::string make_neg_string(T value) {
std::ostringstream ss;
assert(ss << value);
std::string res = ss.str();
return '-' + res;
}
template <class T>
void test_neg_one() {
const my_facet f(1);
std::ios ios(0);
T v = static_cast<T>(42);
{
const char str[] = "-1";
std::ios_base::iostate err = ios.goodbit;
input_iterator<const char*> iter =
f.get(input_iterator<const char*>(str),
input_iterator<const char*>(str+sizeof(str)),
ios, err, v);
assert(iter.base() == str+sizeof(str)-1);
assert(err == ios.goodbit);
assert(v == T(-1));
}
v = 42;
{
const char str[] = "-";
std::ios_base::iostate err = ios.goodbit;
input_iterator<const char*> iter =
f.get(input_iterator<const char*>(str),
input_iterator<const char*>(str+sizeof(str)),
ios, err, v);
assert(iter.base() == str+sizeof(str)-1);
assert(err == ios.failbit);
assert(v == 0);
}
}
template <class T>
void test_negate() {
typedef typename std::make_signed<T>::type SignedT;
const my_facet f(1);
std::ios ios(0);
T v = 42;
{
T value = std::numeric_limits<SignedT>::max();
++value;
std::string std_str = make_neg_string(value);
const char* str = std_str.data();
size_t size = std_str.size();
std::ios_base::iostate err = ios.goodbit;
input_iterator<const char*> iter =
f.get(input_iterator<const char*>(str),
input_iterator<const char*>(str+size+1),
ios, err, v);
assert(iter.base() == str+size);
assert(err == ios.goodbit);
T expected = -value;
assert(v == expected);
}
v = 42;
{
T value = std::numeric_limits<SignedT>::max();
++value;
++value;
std::string std_str = make_neg_string(value);
const char* str = std_str.data();
size_t size = std_str.size();
std::ios_base::iostate err = ios.goodbit;
input_iterator<const char*> iter =
f.get(input_iterator<const char*>(str),
input_iterator<const char*>(str+size+1),
ios, err, v);
assert(iter.base() == str+size);
assert(err == ios.goodbit);
T expected = -value;
assert(v == expected);
}
v = 42;
{
T value = std::numeric_limits<T>::max();
std::string std_str = make_neg_string(value);
const char* str = std_str.data();
size_t size = std_str.size();
std::ios_base::iostate err = ios.goodbit;
input_iterator<const char*> iter =
f.get(input_iterator<const char*>(str),
input_iterator<const char*>(str+size+1),
ios, err, v);
assert(iter.base() == str+size);
assert(err == ios.goodbit);
T expected = -value;
assert(v == expected);
}
v = 42;
{
std::string std_str = make_neg_string(std::numeric_limits<T>::max());
std_str.back()++;
const char* str = std_str.data();
size_t size = std_str.size();
std::ios_base::iostate err = ios.goodbit;
input_iterator<const char*> iter =
f.get(input_iterator<const char*>(str),
input_iterator<const char*>(str+size+1),
ios, err, v);
assert(iter.base() == str+size);
assert(err == ios.failbit);
assert(v == T(-1));
}
}
int main(void)
{
test_neg_one<long>();
test_neg_one<long long>();
test_neg_one<unsigned short>();
test_neg_one<unsigned int>();
test_neg_one<unsigned long>();
test_neg_one<unsigned long long>();
test_negate<unsigned short>();
test_negate<unsigned int>();
test_negate<unsigned long>();
test_negate<unsigned long long>();
}