[gcc/devel/c++-coroutines] libstdc++: Fix istream::ignore exit conditions (PR 94749, PR 96161)

Iain D Sandoe iains@gcc.gnu.org
Mon Jul 13 20:19:30 GMT 2020


https://gcc.gnu.org/g:ba8fe4b4832e30277f2e4a73b5d35b2e55074d07

commit ba8fe4b4832e30277f2e4a73b5d35b2e55074d07
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Mon Jul 13 10:26:39 2020 +0100

    libstdc++: Fix istream::ignore exit conditions (PR 94749, PR 96161)
    
    My previous fix for PR 94749 did fix the reported case, so that the next
    character is not discarded if it happens to equal the delimiter when __n
    characters have already been read. But it introduced a new bug, which is
    that the delimiter character would *not* be discarded if the number of
    characters discarded is numeric_limits<streamsize>::max() or more before
    reaching the delimiter.
    
    The new bug happens because I changed the code to check _M_gcount < __n.
    But when __n == numeric_limits<streamsize>::max() that is false, and so
    we don't discard the delimiter. It's not sufficient to check for the
    delimiter when the __large_ignore condition is true, because there's an
    edge case where the delimiter is reached when _M_gcount == __n and so
    we break out of the loop without setting __large_ignore.
    
    PR 96161 is a similar bug to the original PR 94749 report, where eofbit
    is set after discarding __n characters if there happen to be no more
    characters in the stream.
    
    This patch fixes both cases (and the regression) by checking different
    conditions for the __n == max case and the __n < max case. For the
    former case, we know that we must have either reached the delimiter or
    EOF, and the value of _M_gcount doesn't matter (except to avoid integer
    overflow). For the latter case we need to check _M_gcount first and only
    set eofbit or discard the delimiter if it didn't reach __n. For the
    latter case overflow can't happen because _M_gcount <= __n < max.
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/94749
            PR libstdc++/96161
            * include/bits/istream.tcc (basic_istream::ignore(streamsize))
            [n == max]: Check overflow conditions on _M_gcount. Rely on
            the fact that either EOF or the delimiter was reached.
            [n < max]: Check _M_gcount < n before checking for EOF or
            delimiter.
            (basic_istream::ignore(streamsize, char_type): Likewise.
            * src/c++98/compatibility.cc (istream::ignore(streamsize))
            (wistream::ignore(streamsize)): Likewise.
            * src/c++98/istream.cc (istream::ignore(streamsize, char_type))
            (wistream::ignore(streamsize, char_type)): Likewise.
            * testsuite/27_io/basic_istream/ignore/char/94749.cc: Check that
            delimiter is discarded if the number of characters ignored
            doesn't fit in streamsize.
            * testsuite/27_io/basic_istream/ignore/wchar_t/94749.cc:
            Likewise.
            * testsuite/27_io/basic_istream/ignore/char/96161.cc: New test.
            * testsuite/27_io/basic_istream/ignore/wchar_t/96161.cc: New test.

Diff:
---
 libstdc++-v3/include/bits/istream.tcc              |  44 ++++--
 libstdc++-v3/src/c++98/compatibility.cc            |  32 +++--
 libstdc++-v3/src/c++98/istream.cc                  |  56 ++++++--
 .../27_io/basic_istream/ignore/char/94749.cc       | 151 ++++++++++++++++++++
 .../27_io/basic_istream/ignore/char/96161.cc       |  79 +++++++++++
 .../27_io/basic_istream/ignore/wchar_t/94749.cc    | 155 ++++++++++++++++++++-
 .../27_io/basic_istream/ignore/wchar_t/96161.cc    |  79 +++++++++++
 7 files changed, 561 insertions(+), 35 deletions(-)

diff --git a/libstdc++-v3/include/bits/istream.tcc b/libstdc++-v3/include/bits/istream.tcc
index d36374c707f..5983e51873f 100644
--- a/libstdc++-v3/include/bits/istream.tcc
+++ b/libstdc++-v3/include/bits/istream.tcc
@@ -538,11 +538,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 		    break;
 		}
 
-	      if (__large_ignore)
-		_M_gcount = __gnu_cxx::__numeric_traits<streamsize>::__max;
+	      if (__n == __gnu_cxx::__numeric_traits<streamsize>::__max)
+		{
+		  if (__large_ignore)
+		    _M_gcount = __gnu_cxx::__numeric_traits<streamsize>::__max;
 
-	      if (traits_type::eq_int_type(__c, __eof))
-                __err |= ios_base::eofbit;
+		  if (traits_type::eq_int_type(__c, __eof))
+		    __err |= ios_base::eofbit;
+		}
+	      else if (_M_gcount < __n)
+		{
+		  if (traits_type::eq_int_type(__c, __eof))
+		    __err |= ios_base::eofbit;
+		}
             }
 	  __catch(__cxxabiv1::__forced_unwind&)
 	    {
@@ -596,15 +604,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 		    break;
 		}
 
-	      if (__large_ignore)
-		_M_gcount = __gnu_cxx::__numeric_traits<streamsize>::__max;
+	      if (__n == __gnu_cxx::__numeric_traits<streamsize>::__max)
+		{
+		  if (__large_ignore)
+		    _M_gcount = __gnu_cxx::__numeric_traits<streamsize>::__max;
 
-              if (traits_type::eq_int_type(__c, __eof))
-                __err |= ios_base::eofbit;
-	      else if (_M_gcount < __n) // implies __c == __delim
+		  if (traits_type::eq_int_type(__c, __eof))
+		    __err |= ios_base::eofbit;
+		  else
+		    {
+		      if (_M_gcount != __n)
+			++_M_gcount;
+		      __sb->sbumpc();
+		    }
+		}
+	      else if (_M_gcount < __n) // implies __c == __delim or EOF
 		{
-		  ++_M_gcount;
-		  __sb->sbumpc();
+		  if (traits_type::eq_int_type(__c, __eof))
+		    __err |= ios_base::eofbit;
+		  else
+		    {
+		      ++_M_gcount;
+		      __sb->sbumpc();
+		    }
 		}
             }
 	  __catch(__cxxabiv1::__forced_unwind&)
diff --git a/libstdc++-v3/src/c++98/compatibility.cc b/libstdc++-v3/src/c++98/compatibility.cc
index 4e95b7ec68c..6024bb9dd48 100644
--- a/libstdc++-v3/src/c++98/compatibility.cc
+++ b/libstdc++-v3/src/c++98/compatibility.cc
@@ -107,11 +107,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 		    break;
 		}
 
-	      if (__large_ignore)
-		_M_gcount = __gnu_cxx::__numeric_traits<streamsize>::__max;
+	      if (__n == __gnu_cxx::__numeric_traits<streamsize>::__max)
+		{
+		  if (__large_ignore)
+		    _M_gcount = __gnu_cxx::__numeric_traits<streamsize>::__max;
 
-	      if (traits_type::eq_int_type(__c, __eof))
-		__err |= ios_base::eofbit;
+		  if (traits_type::eq_int_type(__c, __eof))
+		    __err |= ios_base::eofbit;
+		}
+	      else if (_M_gcount < __n)
+		{
+		  if (traits_type::eq_int_type(__c, __eof))
+		    __err |= ios_base::eofbit;
+		}
 	    }
 	  __catch(__cxxabiv1::__forced_unwind&)
 	    {
@@ -178,11 +186,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 		    break;
 		}
 
-	      if (__large_ignore)
-		_M_gcount = __gnu_cxx::__numeric_traits<streamsize>::__max;
+	      if (__n == __gnu_cxx::__numeric_traits<streamsize>::__max)
+		{
+		  if (__large_ignore)
+		    _M_gcount = __gnu_cxx::__numeric_traits<streamsize>::__max;
 
-	      if (traits_type::eq_int_type(__c, __eof))
-		__err |= ios_base::eofbit;
+		  if (traits_type::eq_int_type(__c, __eof))
+		    __err |= ios_base::eofbit;
+		}
+	      else if (_M_gcount < __n)
+		{
+		  if (traits_type::eq_int_type(__c, __eof))
+		    __err |= ios_base::eofbit;
+		}
 	    }
 	  __catch(__cxxabiv1::__forced_unwind&)
 	    {
diff --git a/libstdc++-v3/src/c++98/istream.cc b/libstdc++-v3/src/c++98/istream.cc
index d6fee1a0d66..d2c6794be73 100644
--- a/libstdc++-v3/src/c++98/istream.cc
+++ b/libstdc++-v3/src/c++98/istream.cc
@@ -166,15 +166,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 		    break;
 		}
 
-	      if (__large_ignore)
-		_M_gcount = __gnu_cxx::__numeric_traits<streamsize>::__max;
+	      if (__n == __gnu_cxx::__numeric_traits<streamsize>::__max)
+		{
+		  if (__large_ignore)
+		    _M_gcount = __gnu_cxx::__numeric_traits<streamsize>::__max;
 
-	      if (traits_type::eq_int_type(__c, __eof))
-		__err |= ios_base::eofbit;
-	      else if (_M_gcount < __n) // implies __c == __delim
+		  if (traits_type::eq_int_type(__c, __eof))
+		    __err |= ios_base::eofbit;
+		  else
+		    {
+		      if (_M_gcount != __n)
+			++_M_gcount;
+		      __sb->sbumpc();
+		    }
+		}
+	      else if (_M_gcount < __n) // implies __c == __delim or EOF
 		{
-		  ++_M_gcount;
-		  __sb->sbumpc();
+		  if (traits_type::eq_int_type(__c, __eof))
+		    __err |= ios_base::eofbit;
+		  else
+		    {
+		      ++_M_gcount;
+		      __sb->sbumpc();
+		    }
 		}
 	    }
 	  __catch(__cxxabiv1::__forced_unwind&)
@@ -406,15 +420,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 		    break;
 		}
 
-	      if (__large_ignore)
-		_M_gcount = __gnu_cxx::__numeric_traits<streamsize>::__max;
+	      if (__n == __gnu_cxx::__numeric_traits<streamsize>::__max)
+		{
+		  if (__large_ignore)
+		    _M_gcount = __gnu_cxx::__numeric_traits<streamsize>::__max;
 
-	      if (traits_type::eq_int_type(__c, __eof))
-		__err |= ios_base::eofbit;
-	      else if (_M_gcount < __n) // implies __c == __delim
+		  if (traits_type::eq_int_type(__c, __eof))
+		    __err |= ios_base::eofbit;
+		  else
+		    {
+		      if (_M_gcount != __n)
+			++_M_gcount;
+		      __sb->sbumpc();
+		    }
+		}
+	      else if (_M_gcount < __n) // implies __c == __delim or EOF
 		{
-		  ++_M_gcount;
-		  __sb->sbumpc();
+		  if (traits_type::eq_int_type(__c, __eof))
+		    __err |= ios_base::eofbit;
+		  else
+		    {
+		      ++_M_gcount;
+		      __sb->sbumpc();
+		    }
 		}
 	    }
 	  __catch(__cxxabiv1::__forced_unwind&)
diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/ignore/char/94749.cc b/libstdc++-v3/testsuite/27_io/basic_istream/ignore/char/94749.cc
index 03b5286b00e..32b604d5b15 100644
--- a/libstdc++-v3/testsuite/27_io/basic_istream/ignore/char/94749.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_istream/ignore/char/94749.cc
@@ -16,11 +16,13 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-do run }
+// { dg-options "-DSIMULATOR_TEST" { target simulator } }
 
 // PR libstdc++/94749
 // basic_istream::ignore(n, c) discards n+1 if next character is equal to c.
 
 #include <sstream>
+#include <limits>
 #include <testsuite_hooks.h>
 
 typedef char C;
@@ -30,8 +32,10 @@ test01()
 {
   std::basic_istringstream<C> s(" +   -");
   s.ignore(1, '+');
+  VERIFY( s.gcount() == 1 );
   VERIFY( s.get() == '+' );
   s.ignore(3, '-');
+  VERIFY( s.gcount() == 3 );
   VERIFY( s.get() == '-' );
 }
 
@@ -40,8 +44,10 @@ test02()
 {
   std::basic_istringstream<C> s(".+...-");
   s.ignore(1, '+');
+  VERIFY( s.gcount() == 1 );
   VERIFY( s.get() == '+' );
   s.ignore(3, '-');
+  VERIFY( s.gcount() == 3 );
   VERIFY( s.get() == '-' );
 }
 
@@ -50,8 +56,10 @@ test03()
 {
   std::basic_istringstream<C, __gnu_cxx::char_traits<C> > s(" +   -");
   s.ignore(1, '+');
+  VERIFY( s.gcount() == 1 );
   VERIFY( s.get() == '+' );
   s.ignore(3, '-');
+  VERIFY( s.gcount() == 3 );
   VERIFY( s.get() == '-' );
 }
 
@@ -60,11 +68,150 @@ test04()
 {
   std::basic_istringstream<C, __gnu_cxx::char_traits<C> > s(".+...-");
   s.ignore(1, '+');
+  VERIFY( s.gcount() == 1 );
   VERIFY( s.get() == '+' );
   s.ignore(3, '-');
+  VERIFY( s.gcount() == 3 );
   VERIFY( s.get() == '-' );
 }
 
+// The original fix for PR libstdc++/94749 failed to discard the delimiter
+// if it occurred after numeric_limits<streamsize>::max() had been seen.
+// This streambuf will keep filling the get area with zero bytes until
+// almost numeric_limits<streamsize>::max() characters have been read,
+// and then return one more buffer that has "123" at its end.
+template<typename T>
+struct buff : std::basic_streambuf<typename T::char_type, T>
+{
+  typedef typename T::char_type		  char_type;
+  typedef typename T::int_type		  int_type;
+  typedef std::streamsize		  streamsize;
+  typedef std::numeric_limits<streamsize> limits;
+
+  buff() : count(0), buf() { }
+
+  int_type underflow()
+  {
+    // Number of characters left until we overflow the counter
+    const streamsize headroom = limits::max() - count;
+
+    if (headroom == 0)
+      return T::eof();
+
+    if (bufsz < headroom)
+    {
+      this->setg(buf, buf, buf + bufsz);
+      count += bufsz;
+    }
+    else
+    {
+      // write "123" across the 2GB boundary
+      buf[headroom-1] = '1';
+      buf[headroom+0] = '2';
+      buf[headroom+1] = '3';
+      this->setg(buf, buf, buf + headroom + 2);
+      count = limits::max();
+    }
+
+    return buf[0];
+  }
+
+  streamsize count;
+
+  static const streamsize bufsz = 2048 << limits::digits10;
+  char_type buf[bufsz + 2];
+};
+
+void
+test05()
+{
+  // Not possible to overflow 64-bit streamsize in reasonable time.
+  if (std::numeric_limits<std::streamsize>::digits > 32)
+    return;
+
+  typedef std::char_traits<C> T;
+
+  std::basic_istream<C, T> in(new buff<T>);
+
+  in.ignore(std::numeric_limits<std::streamsize>::max(), '1');
+  VERIFY(in.good());
+  VERIFY(in.gcount() == std::numeric_limits<std::streamsize>::max());
+  VERIFY(in.get() == '2');
+  VERIFY(in.get() == '3');
+  VERIFY(in.get() == T::eof());
+
+  delete in.rdbuf(new buff<T>);
+
+  in.ignore(std::numeric_limits<std::streamsize>::max(), '2');
+  VERIFY(in.good());
+  // The standard doesn't say what gcount() should return in this case:
+  VERIFY(in.gcount() == std::numeric_limits<std::streamsize>::max());
+  VERIFY(in.get() == '3');
+  VERIFY(in.get() == T::eof());
+
+  delete in.rdbuf(new buff<T>);
+
+  in.ignore(std::numeric_limits<std::streamsize>::max(), '3');
+  VERIFY(in.good());
+  // The standard doesn't say what gcount() should return in this case:
+  VERIFY(in.gcount() == std::numeric_limits<std::streamsize>::max());
+  VERIFY(in.get() == T::eof());
+
+  delete in.rdbuf(new buff<T>);
+
+  in.ignore(std::numeric_limits<std::streamsize>::max(), '4');
+  VERIFY(in.eof());
+  // The standard doesn't say what gcount() should return in this case:
+  VERIFY(in.gcount() == std::numeric_limits<std::streamsize>::max());
+  VERIFY(in.get() == T::eof());
+
+  delete in.rdbuf(nullptr);
+}
+
+void
+test06()
+{
+  if (std::numeric_limits<std::streamsize>::digits > 32)
+    return;
+
+  typedef __gnu_cxx::char_traits<C> T;
+
+  std::basic_istream<C, T> in(new buff<T>);
+
+  in.ignore(std::numeric_limits<std::streamsize>::max(), '1');
+  VERIFY(in.good());
+  VERIFY(in.gcount() == std::numeric_limits<std::streamsize>::max());
+  VERIFY(in.get() == '2');
+  VERIFY(in.get() == '3');
+  VERIFY(in.get() == T::eof());
+
+  delete in.rdbuf(new buff<T>);
+
+  in.ignore(std::numeric_limits<std::streamsize>::max(), '2');
+  VERIFY(in.good());
+  // The standard doesn't say what gcount() should return in this case:
+  VERIFY(in.gcount() == std::numeric_limits<std::streamsize>::max());
+  VERIFY(in.get() == '3');
+  VERIFY(in.get() == T::eof());
+
+  delete in.rdbuf(new buff<T>);
+
+  in.ignore(std::numeric_limits<std::streamsize>::max(), '3');
+  VERIFY(in.good());
+  // The standard doesn't say what gcount() should return in this case:
+  VERIFY(in.gcount() == std::numeric_limits<std::streamsize>::max());
+  VERIFY(in.get() == T::eof());
+
+  delete in.rdbuf(new buff<T>);
+
+  in.ignore(std::numeric_limits<std::streamsize>::max(), '4');
+  VERIFY(in.eof());
+  // The standard doesn't say what gcount() should return in this case:
+  VERIFY(in.gcount() == std::numeric_limits<std::streamsize>::max());
+  VERIFY(in.get() == T::eof());
+
+  delete in.rdbuf(nullptr);
+}
 
 int
 main()
@@ -73,4 +220,8 @@ main()
   test02();
   test03();
   test04();
+  test05();
+#ifndef SIMULATOR_TEST
+  test06();
+#endif
 }
diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/ignore/char/96161.cc b/libstdc++-v3/testsuite/27_io/basic_istream/ignore/char/96161.cc
new file mode 100644
index 00000000000..c19e5f9e758
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_istream/ignore/char/96161.cc
@@ -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 }
+
+// PR libstdc++/96161
+// basic_istream::ignore sets eofbit too soon
+
+#include <sstream>
+#include <limits>
+#include <testsuite_hooks.h>
+
+typedef char C;
+
+void
+test01()
+{
+  std::basic_istringstream<C> s("  ");
+  s.ignore(2, '+');
+  VERIFY( s.gcount() == 2 );
+  VERIFY( s.good() );
+  VERIFY( s.get() == std::char_traits<C>::eof() );
+  VERIFY( s.eof() );
+}
+
+void
+test02()
+{
+  std::basic_istringstream<C> s("  ");
+  s.ignore(2);
+  VERIFY( s.gcount() == 2 );
+  VERIFY( s.good() );
+  VERIFY( s.get() == std::char_traits<C>::eof() );
+  VERIFY( s.eof() );
+}
+
+void
+test03()
+{
+  std::basic_istringstream<C, __gnu_cxx::char_traits<C> > s("  ");
+  s.ignore(2, '+');
+  VERIFY( s.gcount() == 2 );
+  VERIFY( s.good() );
+  VERIFY( s.get() == __gnu_cxx::char_traits<C>::eof() );
+  VERIFY( s.eof() );
+}
+
+void
+test04()
+{
+  std::basic_istringstream<C, __gnu_cxx::char_traits<C> > s("  ");
+  s.ignore(2);
+  VERIFY( s.gcount() == 2 );
+  VERIFY( s.good() );
+  VERIFY( s.get() == __gnu_cxx::char_traits<C>::eof() );
+  VERIFY( s.eof() );
+}
+
+int main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/ignore/wchar_t/94749.cc b/libstdc++-v3/testsuite/27_io/basic_istream/ignore/wchar_t/94749.cc
index e5ec4e7d697..23d3a0ad170 100644
--- a/libstdc++-v3/testsuite/27_io/basic_istream/ignore/wchar_t/94749.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_istream/ignore/wchar_t/94749.cc
@@ -16,11 +16,13 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-do run }
+// { dg-options "-DSIMULATOR_TEST" { target simulator } }
 
 // PR libstdc++/94749
 // basic_istream::ignore(n, c) discards n+1 if next character is equal to c.
 
 #include <sstream>
+#include <limits>
 #include <testsuite_hooks.h>
 
 typedef wchar_t C;
@@ -30,8 +32,10 @@ test01()
 {
   std::basic_istringstream<C> s(L" +   -");
   s.ignore(1, L'+');
+  VERIFY( s.gcount() == 1 );
   VERIFY( s.get() == L'+' );
   s.ignore(3, L'-');
+  VERIFY( s.gcount() == 3 );
   VERIFY( s.get() == L'-' );
 }
 
@@ -40,8 +44,10 @@ test02()
 {
   std::basic_istringstream<C> s(L".+...-");
   s.ignore(1, L'+');
+  VERIFY( s.gcount() == 1 );
   VERIFY( s.get() == L'+' );
   s.ignore(3, L'-');
+  VERIFY( s.gcount() == 3 );
   VERIFY( s.get() == L'-' );
 }
 
@@ -50,8 +56,10 @@ test03()
 {
   std::basic_istringstream<C, __gnu_cxx::char_traits<C> > s(L" +   -");
   s.ignore(1, L'+');
+  VERIFY( s.gcount() == 1 );
   VERIFY( s.get() == L'+' );
   s.ignore(3, L'-');
+  VERIFY( s.gcount() == 3 );
   VERIFY( s.get() == L'-' );
 }
 
@@ -60,17 +68,160 @@ test04()
 {
   std::basic_istringstream<C, __gnu_cxx::char_traits<C> > s(L".+...-");
   s.ignore(1, L'+');
+  VERIFY( s.gcount() == 1 );
   VERIFY( s.get() == L'+' );
   s.ignore(3, L'-');
+  VERIFY( s.gcount() == 3 );
   VERIFY( s.get() == L'-' );
 }
 
+// The original fix for PR libstdc++/94749 failed to discard the delimiter
+// if it occurred after numeric_limits<streamsize>::max() had been seen.
+// This streambuf will keep filling the get area with zero bytes until
+// almost numeric_limits<streamsize>::max() characters have been read,
+// and then return one more buffer that has L"123" at its end.
+template<typename T>
+struct buff : std::basic_streambuf<typename T::char_type, T>
+{
+  typedef typename T::char_type		  char_type;
+  typedef typename T::int_type		  int_type;
+  typedef std::streamsize		  streamsize;
+  typedef std::numeric_limits<streamsize> limits;
+
+  buff() : count(0), buf() { }
+
+  int_type underflow()
+  {
+    // Number of characters left until we overflow the counter
+    const streamsize headroom = limits::max() - count;
+
+    if (headroom == 0)
+      return T::eof();
+
+    if (bufsz < headroom)
+    {
+      this->setg(buf, buf, buf + bufsz);
+      count += bufsz;
+    }
+    else
+    {
+      // write L"123" across the 2GB boundary
+      buf[headroom-1] = L'1';
+      buf[headroom+0] = L'2';
+      buf[headroom+1] = L'3';
+      this->setg(buf, buf, buf + headroom + 2);
+      count = limits::max();
+    }
+
+    return buf[0];
+  }
+
+  streamsize count;
+
+  static const streamsize bufsz = 2048 << limits::digits10;
+  char_type buf[bufsz + 2];
+};
+
+void
+test05()
+{
+  // Not possible to overflow 64-bit streamsize in reasonable time.
+  if (std::numeric_limits<std::streamsize>::digits > 32)
+    return;
+
+  typedef std::char_traits<C> T;
+
+  std::basic_istream<C, T> in(new buff<T>);
+
+  in.ignore(std::numeric_limits<std::streamsize>::max(), L'1');
+  VERIFY(in.good());
+  VERIFY(in.gcount() == std::numeric_limits<std::streamsize>::max());
+  VERIFY(in.get() == L'2');
+  VERIFY(in.get() == L'3');
+  VERIFY(in.get() == T::eof());
+
+  delete in.rdbuf(new buff<T>);
+
+  in.ignore(std::numeric_limits<std::streamsize>::max(), L'2');
+  VERIFY(in.good());
+  // The standard doesn't say what gcount() should return in this case:
+  VERIFY(in.gcount() == std::numeric_limits<std::streamsize>::max());
+  VERIFY(in.get() == L'3');
+  VERIFY(in.get() == T::eof());
+
+  delete in.rdbuf(new buff<T>);
+
+  in.ignore(std::numeric_limits<std::streamsize>::max(), L'3');
+  VERIFY(in.good());
+  // The standard doesn't say what gcount() should return in this case:
+  VERIFY(in.gcount() == std::numeric_limits<std::streamsize>::max());
+  VERIFY(in.get() == T::eof());
+
+  delete in.rdbuf(new buff<T>);
+
+  in.ignore(std::numeric_limits<std::streamsize>::max(), L'4');
+  VERIFY(in.eof());
+  // The standard doesn't say what gcount() should return in this case:
+  VERIFY(in.gcount() == std::numeric_limits<std::streamsize>::max());
+  VERIFY(in.get() == T::eof());
+
+  delete in.rdbuf(nullptr);
+}
+
+void
+test06()
+{
+  if (std::numeric_limits<std::streamsize>::digits > 32)
+    return;
+
+  typedef __gnu_cxx::char_traits<C> T;
+
+  std::basic_istream<C, T> in(new buff<T>);
+
+  in.ignore(std::numeric_limits<std::streamsize>::max(), L'1');
+  VERIFY(in.good());
+  VERIFY(in.gcount() == std::numeric_limits<std::streamsize>::max());
+  VERIFY(in.get() == L'2');
+  VERIFY(in.get() == L'3');
+  VERIFY(in.get() == T::eof());
+
+  delete in.rdbuf(new buff<T>);
+
+  in.ignore(std::numeric_limits<std::streamsize>::max(), L'2');
+  VERIFY(in.good());
+  // The standard doesn't say what gcount() should return in this case:
+  VERIFY(in.gcount() == std::numeric_limits<std::streamsize>::max());
+  VERIFY(in.get() == L'3');
+  VERIFY(in.get() == T::eof());
+
+  delete in.rdbuf(new buff<T>);
+
+  in.ignore(std::numeric_limits<std::streamsize>::max(), L'3');
+  VERIFY(in.good());
+  // The standard doesn't say what gcount() should return in this case:
+  VERIFY(in.gcount() == std::numeric_limits<std::streamsize>::max());
+  VERIFY(in.get() == T::eof());
+
+  delete in.rdbuf(new buff<T>);
+
+  in.ignore(std::numeric_limits<std::streamsize>::max(), L'4');
+  VERIFY(in.eof());
+  // The standard doesn't say what gcount() should return in this case:
+  VERIFY(in.gcount() == std::numeric_limits<std::streamsize>::max());
+  VERIFY(in.get() == T::eof());
+
+  delete in.rdbuf(nullptr);
+}
 
 int
 main()
 {
-  // test01();
-  // test02();
+  test01();
+  test02();
   test03();
   test04();
+  test05();
+#ifndef SIMULATOR_TEST
+  test06();
+#endif
 }
diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/ignore/wchar_t/96161.cc b/libstdc++-v3/testsuite/27_io/basic_istream/ignore/wchar_t/96161.cc
new file mode 100644
index 00000000000..64234426d15
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_istream/ignore/wchar_t/96161.cc
@@ -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 }
+
+// PR libstdc++/96161
+// basic_istream::ignore sets eofbit too soon
+
+#include <sstream>
+#include <limits>
+#include <testsuite_hooks.h>
+
+typedef wchar_t C;
+
+void
+test01()
+{
+  std::basic_istringstream<C> s(L"  ");
+  s.ignore(2, L'+');
+  VERIFY( s.gcount() == 2 );
+  VERIFY( s.good() );
+  VERIFY( s.get() == std::char_traits<C>::eof() );
+  VERIFY( s.eof() );
+}
+
+void
+test02()
+{
+  std::basic_istringstream<C> s(L"  ");
+  s.ignore(2);
+  VERIFY( s.gcount() == 2 );
+  VERIFY( s.good() );
+  VERIFY( s.get() == std::char_traits<C>::eof() );
+  VERIFY( s.eof() );
+}
+
+void
+test03()
+{
+  std::basic_istringstream<C, __gnu_cxx::char_traits<C> > s(L"  ");
+  s.ignore(2, L'+');
+  VERIFY( s.gcount() == 2 );
+  VERIFY( s.good() );
+  VERIFY( s.get() == __gnu_cxx::char_traits<C>::eof() );
+  VERIFY( s.eof() );
+}
+
+void
+test04()
+{
+  std::basic_istringstream<C, __gnu_cxx::char_traits<C> > s(L"  ");
+  s.ignore(2);
+  VERIFY( s.gcount() == 2 );
+  VERIFY( s.good() );
+  VERIFY( s.get() == __gnu_cxx::char_traits<C>::eof() );
+  VERIFY( s.eof() );
+}
+
+int main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+}


More information about the Libstdc++-cvs mailing list