[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