[libcxx] r328751 - Fix PR36914 - num_get::get(unsigned) incorrectly handles negative numbers.
Eric Fiselier via cfe-commits
cfe-commits at lists.llvm.org
Wed Mar 28 18:18:53 PDT 2018
Author: ericwf
Date: Wed Mar 28 18:18:53 2018
New Revision: 328751
URL: http://llvm.org/viewvc/llvm-project?rev=328751&view=rev
Log:
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.
Added:
libcxx/trunk/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/test_neg_one.pass.cpp
Modified:
libcxx/trunk/include/locale
libcxx/trunk/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/test_min_max.pass.cpp
Modified: libcxx/trunk/include/locale
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/locale?rev=328751&r1=328750&r2=328751&view=diff
==============================================================================
--- libcxx/trunk/include/locale (original)
+++ libcxx/trunk/include/locale Wed Mar 28 18:18:53 2018
@@ -768,10 +768,10 @@ __num_get_unsigned_integral(const char*
{
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*
__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;
Modified: libcxx/trunk/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/test_min_max.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/test_min_max.pass.cpp?rev=328751&r1=328750&r2=328751&view=diff
==============================================================================
--- libcxx/trunk/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/test_min_max.pass.cpp (original)
+++ libcxx/trunk/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/test_min_max.pass.cpp Wed Mar 28 18:18:53 2018
@@ -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)
Added: libcxx/trunk/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/test_neg_one.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/test_neg_one.pass.cpp?rev=328751&view=auto
==============================================================================
--- libcxx/trunk/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/test_neg_one.pass.cpp (added)
+++ libcxx/trunk/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/test_neg_one.pass.cpp Wed Mar 28 18:18:53 2018
@@ -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>();
+}
More information about the cfe-commits
mailing list