aboutsummaryrefslogtreecommitdiff
path: root/libstdc++-v3/testsuite/27_io/basic_istream
diff options
context:
space:
mode:
authorJonathan Wakely <jwakely@redhat.com>2020-07-13 10:26:39 +0100
committerJonathan Wakely <jwakely@redhat.com>2020-07-13 12:09:44 +0100
commitba8fe4b4832e30277f2e4a73b5d35b2e55074d07 (patch)
tree04643699ec81c2b8236dbfa4f3590ab2275d191f /libstdc++-v3/testsuite/27_io/basic_istream
parentb20097c65d2e74b1901fba1c55c77f0407e542d2 (diff)
downloadgcc-ba8fe4b4832e30277f2e4a73b5d35b2e55074d07.zip
gcc-ba8fe4b4832e30277f2e4a73b5d35b2e55074d07.tar.gz
gcc-ba8fe4b4832e30277f2e4a73b5d35b2e55074d07.tar.bz2
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.
Diffstat (limited to 'libstdc++-v3/testsuite/27_io/basic_istream')
-rw-r--r--libstdc++-v3/testsuite/27_io/basic_istream/ignore/char/94749.cc151
-rw-r--r--libstdc++-v3/testsuite/27_io/basic_istream/ignore/char/96161.cc79
-rw-r--r--libstdc++-v3/testsuite/27_io/basic_istream/ignore/wchar_t/94749.cc155
-rw-r--r--libstdc++-v3/testsuite/27_io/basic_istream/ignore/wchar_t/96161.cc79
4 files changed, 462 insertions, 2 deletions
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 03b5286..32b604d 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 0000000..c19e5f9
--- /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 e5ec4e7..23d3a0a 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 0000000..6423442
--- /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();
+}