re PR libstdc++/45628 (std::fstream::tellg invalidates I/O buffer)

2010-09-22  David Krauss  <potswa@mac.com>

	PR libstdc++/45628
	* include/bits/fstream.tcc (basic_filebuf::underflow): Add state
	transition to avoid modality requiring seekoff(0,ios::cur).
	(basic_filebuf::pbackfail): Likewise.
	(basic_filebuf::overflow): Likewise.
	(basic_filebuf::_M_seek): Avoid minor unnecessary conversion.
	(basic_filebuf::seekoff): Remove code to _M_get_ext_pos; make
	(0, ios::cur) a special case preserving buffer contents.
	(basic_filebuf::_M_get_ext_pos): New function to obtain status
	about codecvt extern_t buffer for overflow and seekoff.
	* include/std/fstream (basic_filebuf::_M_get_ext_pos): Likewise.
	* config/abi/pre/gnu.ver: Export new symbols.
	* testsuite/27_io/basic_filebuf/seekoff/char/45628-1.cc: New,
	verifies that seekoff(0, ios::cur) preserves buffers.
	* testsuite/27_io/basic_filebuf/seekoff/char/45628-2.cc: Likewise.
	for codecvt case. More lenient as it may still flush put area.
	* testsuite/27_io/basic_filebuf/seekoff/char/4.cc: Modify to
	check that seekoff is not required between read and write.
	* testsuite/27_io/basic_filebuf/seekoff/wchar_t/4.cc: Likewise.
	* testsuite/27_io/basic_filebuf/sync/wchar_t/1.cc: Remove.
	* testsuite/27_io/basic_filebuf/sync/wchar_t/1.cc: Likewise.
	* testsuite/util/testsuite_character.h (codecvt::do_length): Comply
	with 22.2.1.5.2/10 "Returns ... the LARGEST value in the range..."

From-SVN: r164529
This commit is contained in:
David Krauss 2010-09-22 19:40:43 +00:00 committed by Paolo Carlini
parent 5d64ee190c
commit 3531cf5ef3
11 changed files with 329 additions and 141 deletions

View File

@ -1,3 +1,29 @@
2010-09-22 David Krauss <potswa@mac.com>
PR libstdc++/45628
* include/bits/fstream.tcc (basic_filebuf::underflow): Add state
transition to avoid modality requiring seekoff(0,ios::cur).
(basic_filebuf::pbackfail): Likewise.
(basic_filebuf::overflow): Likewise.
(basic_filebuf::_M_seek): Avoid minor unnecessary conversion.
(basic_filebuf::seekoff): Remove code to _M_get_ext_pos; make
(0, ios::cur) a special case preserving buffer contents.
(basic_filebuf::_M_get_ext_pos): New function to obtain status
about codecvt extern_t buffer for overflow and seekoff.
* include/std/fstream (basic_filebuf::_M_get_ext_pos): Likewise.
* config/abi/pre/gnu.ver: Export new symbols.
* testsuite/27_io/basic_filebuf/seekoff/char/45628-1.cc: New,
verifies that seekoff(0, ios::cur) preserves buffers.
* testsuite/27_io/basic_filebuf/seekoff/char/45628-2.cc: Likewise.
for codecvt case. More lenient as it may still flush put area.
* testsuite/27_io/basic_filebuf/seekoff/char/4.cc: Modify to
check that seekoff is not required between read and write.
* testsuite/27_io/basic_filebuf/seekoff/wchar_t/4.cc: Likewise.
* testsuite/27_io/basic_filebuf/sync/wchar_t/1.cc: Remove.
* testsuite/27_io/basic_filebuf/sync/wchar_t/1.cc: Likewise.
* testsuite/util/testsuite_character.h (codecvt::do_length): Comply
with 22.2.1.5.2/10 "Returns ... the LARGEST value in the range..."
2010-09-22 Paolo Carlini <paolo.carlini@oracle.com>
* include/bits/functional_hash.h (__hash_base): Add.

View File

@ -332,7 +332,13 @@ GLIBCXX_3.4 {
# std::basic_filebuf
_ZNSt13basic_filebufI[cw]St11char_traitsI[cw]EEC*;
_ZNSt13basic_filebufI[cw]St11char_traitsI[cw]EED*;
_ZNSt13basic_filebufI[cw]St11char_traitsI[cw]EE[0-3]*;
_ZNSt13basic_filebufI[cw]St11char_traitsI[cw]EE0*;
_ZNSt13basic_filebufI[cw]St11char_traitsI[cw]EE13*;
_ZNSt13basic_filebufI[cw]St11char_traitsI[cw]EE15*;
_ZNSt13basic_filebufI[cw]St11char_traitsI[cw]EE16*;
_ZNSt13basic_filebufI[cw]St11char_traitsI[cw]EE19*;
_ZNSt13basic_filebufI[cw]St11char_traitsI[cw]EE2*;
_ZNSt13basic_filebufI[cw]St11char_traitsI[cw]EE3*;
_ZNSt13basic_filebufI[cw]St11char_traitsI[cw]EE4openEPKc*;
_ZNSt13basic_filebufI[cw]St11char_traitsI[cw]EE4sync*;
_ZNSt13basic_filebufI[cw]St11char_traitsI[cw]EE[5-9]*;
@ -1178,6 +1184,9 @@ GLIBCXX_3.4.15 {
_ZNSbIwSt11char_traitsIwESaIwEE4backEv;
_ZNKSbIwSt11char_traitsIwESaIwEE4backEv;
# basic_filebuf::_M_get_ext_pos
_ZNSt13basic_filebufI[cw]St11char_traitsI[cw]EE14_M_get_ext_pos*;
} GLIBCXX_3.4.14;
# Symbols in the support library (libsupc++) have their own tag.

View File

@ -1,7 +1,7 @@
// File based streams -*- C++ -*-
// Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
// 2007, 2008, 2009
// 2007, 2008, 2009, 2010
// Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
@ -205,8 +205,16 @@ _GLIBCXX_BEGIN_NAMESPACE(std)
{
int_type __ret = traits_type::eof();
const bool __testin = _M_mode & ios_base::in;
if (__testin && !_M_writing)
if (__testin)
{
if (_M_writing)
{
__ret = overflow();
if (__ret == traits_type::eof())
return __ret;
_M_set_buffer(-1);
_M_writing = false;
}
// Check for pback madness, and if so switch back to the
// normal buffers and jet outta here before expensive
// fileops happen...
@ -357,8 +365,16 @@ _GLIBCXX_BEGIN_NAMESPACE(std)
{
int_type __ret = traits_type::eof();
const bool __testin = _M_mode & ios_base::in;
if (__testin && !_M_writing)
if (__testin)
{
if (_M_writing)
{
__ret = overflow();
if (__ret == traits_type::eof())
return __ret;
_M_set_buffer(-1);
_M_writing = false;
}
// Remember whether the pback buffer is active, otherwise below
// we may try to store in it a second char (libstdc++/9761).
const bool __testpb = _M_pback_init;
@ -410,8 +426,16 @@ _GLIBCXX_BEGIN_NAMESPACE(std)
int_type __ret = traits_type::eof();
const bool __testeof = traits_type::eq_int_type(__c, __ret);
const bool __testout = _M_mode & ios_base::out;
if (__testout && !_M_reading)
if (__testout)
{
if (_M_reading)
{
_M_destroy_pback();
const int __gptr_off = _M_get_ext_pos(_M_state_last);
if (_M_seek(__gptr_off, ios_base::cur, _M_state_last)
== pos_type(off_type(-1)))
return __ret;
}
if (this->pbase() < this->pptr())
{
// If appropriate, append the overflow char.
@ -691,12 +715,20 @@ _GLIBCXX_BEGIN_NAMESPACE(std)
if (__width < 0)
__width = 0;
pos_type __ret = pos_type(off_type(-1));
pos_type __ret = pos_type(off_type(-1));
const bool __testfail = __off != 0 && __width <= 0;
if (this->is_open() && !__testfail)
{
// tellg and tellp queries do not affect any state, unless
// ! always_noconv and the put sequence is not empty.
// In that case, determining the position requires converting the
// put sequence. That doesn't use ext_buf, so requires a flush.
bool __no_movement = __way == ios_base::cur && __off == 0
&& (!_M_writing || _M_codecvt->always_noconv());
// Ditch any pback buffers to avoid confusion.
_M_destroy_pback();
if (!__no_movement)
_M_destroy_pback();
// Correct state at destination. Note that this is the correct
// state for the current position during output, because
@ -707,24 +739,23 @@ _GLIBCXX_BEGIN_NAMESPACE(std)
off_type __computed_off = __off * __width;
if (_M_reading && __way == ios_base::cur)
{
if (_M_codecvt->always_noconv())
__computed_off += this->gptr() - this->egptr();
else
__state = _M_state_last;
__computed_off += _M_get_ext_pos(__state);
}
if (!__no_movement)
__ret = _M_seek(__computed_off, __way, __state);
else
{
if (_M_writing)
__computed_off = this->pptr() - this->pbase();
off_type __file_off = _M_file.seekoff(0, ios_base::cur);
if (__file_off != off_type(-1))
{
// Calculate offset from _M_ext_buf that corresponds
// to gptr(). Note: uses _M_state_last, which
// corresponds to eback().
const int __gptr_off =
_M_codecvt->length(_M_state_last, _M_ext_buf, _M_ext_next,
this->gptr() - this->eback());
__computed_off += _M_ext_buf + __gptr_off - _M_ext_end;
// _M_state_last is modified by codecvt::length() so
// it now corresponds to gptr().
__state = _M_state_last;
__ret = __file_off + __computed_off;
__ret.state(__state);
}
}
__ret = _M_seek(__computed_off, __way, __state);
}
return __ret;
}
@ -756,21 +787,42 @@ _GLIBCXX_BEGIN_NAMESPACE(std)
pos_type __ret = pos_type(off_type(-1));
if (_M_terminate_output())
{
// Returns pos_type(off_type(-1)) in case of failure.
__ret = pos_type(_M_file.seekoff(__off, __way));
if (__ret != pos_type(off_type(-1)))
off_type __file_off = _M_file.seekoff(__off, __way);
if (__file_off != off_type(-1))
{
_M_reading = false;
_M_writing = false;
_M_ext_next = _M_ext_end = _M_ext_buf;
_M_set_buffer(-1);
_M_state_cur = __state;
__ret = __file_off;
__ret.state(_M_state_cur);
}
}
return __ret;
}
// Returns the distance from the end of the ext buffer to the point
// corresponding to gptr(). This is a negative value. Updates __state
// from eback() correspondence to gptr().
template<typename _CharT, typename _Traits>
int basic_filebuf<_CharT, _Traits>::
_M_get_ext_pos(__state_type& __state)
{
if (_M_codecvt->always_noconv())
return this->gptr() - this->egptr();
else
{
// Calculate offset from _M_ext_buf that corresponds to
// gptr(). Precondition: __state == _M_state_last, which
// corresponds to eback().
const int __gptr_off =
_M_codecvt->length(__state, _M_ext_buf, _M_ext_next,
this->gptr() - this->eback());
return _M_ext_buf + __gptr_off - _M_ext_end;
}
}
template<typename _CharT, typename _Traits>
bool
basic_filebuf<_CharT, _Traits>::

View File

@ -351,9 +351,12 @@ _GLIBCXX_BEGIN_NAMESPACE(std)
seekpos(pos_type __pos,
ios_base::openmode __mode = ios_base::in | ios_base::out);
// Common code for seekoff and seekpos
// Common code for seekoff, seekpos, and overflow
pos_type
_M_seek(off_type __off, ios_base::seekdir __way, __state_type __state);
int
_M_get_ext_pos(__state_type &__state);
virtual int
sync();

View File

@ -0,0 +1,103 @@
// Copyright (C) 2010 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-require-fileio "" }
#include <fstream>
#include <testsuite_hooks.h>
#include <testsuite_character.h>
const char name_01[] = "tmp_seekoff_45628.tst";
unsigned underflows, overflows;
class my_filebuf
: public std::basic_filebuf<__gnu_test::pod_uchar>
{
virtual int_type
underflow()
{
++underflows;
return std::basic_filebuf<__gnu_test::pod_uchar>::underflow();
}
virtual int_type
overflow(int_type c)
{
++overflows;
return std::basic_filebuf<__gnu_test::pod_uchar>::overflow(c);
}
};
// libstdc++/45628
void test01()
{
bool test __attribute__((unused)) = true;
using __gnu_test::pod_uchar;
std::locale loc(std::locale::classic(),
new std::codecvt<my_filebuf::traits_type::char_type, char,
my_filebuf::traits_type::state_type>);
my_filebuf::pos_type opos[3], ipos[3];
my_filebuf q;
q.pubimbue(loc);
q.open(name_01, std::ios_base::in | std::ios_base::out
| std::ios_base::trunc);
q.sputc(pod_uchar::from<char>('a'));
opos[0] = q.pubseekoff(0, std::ios_base::cur);
q.sputc(pod_uchar::from<char>('b'));
opos[1] = q.pubseekoff(0, std::ios_base::cur);
q.sputc(pod_uchar::from<char>('c'));
opos[2] = q.pubseekoff(0, std::ios_base::cur);
VERIFY( overflows <= 9 ); // pubseekoff calls overflow twice if converting.
// NB: checking opos==ipos is not very rigorous as long as it flushes, since
// all positions will be at initial state.
q.pubseekoff(0, std::ios_base::beg);
q.sbumpc();
VERIFY( underflows == 1 );
ipos[0] = q.pubseekoff(0, std::ios_base::cur);
VERIFY( ipos[0] == opos[0] );
q.sbumpc();
ipos[1] = q.pubseekoff(0, std::ios_base::cur);
VERIFY( ipos[1] == opos[1] );
q.sbumpc();
ipos[2] = q.pubseekoff(0, std::ios_base::cur);
VERIFY( ipos[2] == opos[2] );
VERIFY( underflows == 1 ); // pubseekoff never flushes get area
// Bonus test: check automatic mode switches.
q.sputc(pod_uchar::from<char>('d'));
q.pubseekpos( ipos[1] );
q.sputc(pod_uchar::from<char>('e'));
VERIFY( my_filebuf::traits_type::eq(
my_filebuf::traits_type::to_char_type(q.sgetc()),
pod_uchar::from<char>('d')) );
}
int main()
{
test01();
return 0;
}

View File

@ -31,7 +31,7 @@ void test01()
typedef filebuf::pos_type pos_type;
const char name[] = "tmp_seekoff-4.tst";
const size_t size = 10;
const size_t size = 12;
char buf[size];
streamsize n;
@ -46,7 +46,7 @@ void test01()
VERIFY( n == 3 );
VERIFY( !memcmp(buf, "abc", 3) );
fb.pubseekoff(0, ios_base::cur);
// Check read => write without pubseekoff(0, ios_base::cur)
n = fb.sputn("ef", 2);
VERIFY( n == 2 );
@ -58,9 +58,17 @@ void test01()
VERIFY( !memcmp(buf, "abcef", 5) );
fb.pubseekoff(0, ios_base::beg);
n = fb.sputn("ghijkl", 6);
VERIFY( n == 6 );
n = fb.sputn("gh", 2);
VERIFY( n == 2 );
// Check write => read without pubseekoff(0, ios_base::cur)
n = fb.sgetn( buf, 3 );
VERIFY( !memcmp(buf, "cef", 3) );
n = fb.sputn("ijkl", 4);
VERIFY( n == 4 );
fb.pubseekoff(0, ios_base::beg);
n = fb.sgetn(buf, 2);
VERIFY( n == 2 );
@ -72,8 +80,8 @@ void test01()
fb.pubseekoff(0, ios_base::beg);
n = fb.sgetn(buf, size);
VERIFY( n == 9 );
VERIFY( !memcmp(buf, "ghijklmno", 9) );
VERIFY( n == 12 );
VERIFY( !memcmp(buf, "ghcefijklmno", 12) );
fb.close();
}

View File

@ -0,0 +1,79 @@
// Copyright (C) 2010 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-require-fileio "" }
#include <fstream>
#include <testsuite_hooks.h>
const char name_01[] = "tmp_seekoff_45628.tst";
unsigned underflows, overflows;
class my_filebuf
: public std::filebuf
{
virtual int_type
underflow()
{
++underflows;
return std::filebuf::underflow();
}
virtual int_type
overflow(int_type c)
{
++overflows;
return std::filebuf::overflow(c);
}
};
// libstdc++/45628
void test01()
{
bool test __attribute__((unused)) = true;
my_filebuf q;
q.open(name_01, std::ios_base::in | std::ios_base::out
| std::ios_base::trunc);
q.sputc('a');
q.pubseekoff(0, std::ios_base::cur);
q.sputc('b');
q.pubseekoff(0, std::ios_base::cur);
q.sputc('c');
q.pubseekoff(0, std::ios_base::cur);
VERIFY( overflows <= 1 ); // only initial sputc allowed to overflow
q.pubseekoff(0, std::ios_base::beg);
q.sbumpc();
VERIFY( underflows == 1 );
q.pubseekoff(0, std::ios_base::cur);
q.sbumpc();
q.pubseekoff(0, std::ios_base::cur);
q.sbumpc();
q.pubseekoff(0, std::ios_base::cur);
VERIFY( underflows == 1 );
}
int main()
{
test01();
return 0;
}

View File

@ -31,7 +31,7 @@ void test01()
typedef wfilebuf::pos_type pos_type;
const char name[] = "tmp_seekoff-4.tst";
const size_t size = 10;
const size_t size = 12;
wchar_t buf[size];
streamsize n;
@ -46,7 +46,7 @@ void test01()
VERIFY( n == 3 );
VERIFY( !wmemcmp(buf, L"abc", 3) );
fb.pubseekoff(0, ios_base::cur);
// Check read => write without pubseekoff(0, ios_base::cur)
n = fb.sputn(L"ef", 2);
VERIFY( n == 2 );
@ -58,8 +58,16 @@ void test01()
VERIFY( !wmemcmp(buf, L"abcef", 5) );
fb.pubseekoff(0, ios_base::beg);
n = fb.sputn(L"ghijkl", 6);
VERIFY( n == 6 );
n = fb.sputn(L"gh", 2);
VERIFY( n == 2 );
// Check write => read without pubseekoff(0, ios_base::cur)
n = fb.sgetn( buf, 3 );
VERIFY( !memcmp(buf, L"cef", 3) );
n = fb.sputn(L"ijkl", 4);
VERIFY( n == 4 );
fb.pubseekoff(0, ios_base::beg);
n = fb.sgetn(buf, 2);
@ -72,8 +80,8 @@ void test01()
fb.pubseekoff(0, ios_base::beg);
n = fb.sgetn(buf, size);
VERIFY( n == 9 );
VERIFY( !wmemcmp(buf, L"ghijklmno", 9) );
VERIFY( n == 12 );
VERIFY( !wmemcmp(buf, L"ghcefijklmno", 12) );
fb.close();
}

View File

@ -1,51 +0,0 @@
// Copyright (C) 2003, 2009 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/>.
// 27.8.1.4 Overridden virtual functions
#include <fstream>
#include <testsuite_hooks.h>
void test01()
{
using namespace std;
bool test __attribute__((unused)) = true;
const char* name = "tmp_sync_1";
filebuf fb;
fb.open(name, ios_base::in | ios_base::out | ios_base::trunc);
fb.sputn("abc", 3);
fb.pubseekoff(0, ios_base::beg);
fb.sputc('1');
// Sync can't be used to switch from write mode to read mode.
fb.pubsync();
filebuf::int_type c = fb.sbumpc();
VERIFY( c == filebuf::traits_type::eof() );
fb.close();
}
int main()
{
test01();
return 0;
}

View File

@ -1,51 +0,0 @@
// Copyright (C) 2003, 2009 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/>.
// 27.8.1.4 Overridden virtual functions
#include <fstream>
#include <testsuite_hooks.h>
void test01()
{
using namespace std;
bool test __attribute__((unused)) = true;
const char* name = "tmp_sync_1";
wfilebuf fb;
fb.open(name, ios_base::in | ios_base::out | ios_base::trunc);
fb.sputn(L"abc", 3);
fb.pubseekoff(0, ios_base::beg);
fb.sputc(L'1');
// Sync can't be used to switch from write mode to read mode.
fb.pubsync();
wfilebuf::int_type c = fb.sbumpc();
VERIFY( c == wfilebuf::traits_type::eof() );
fb.close();
}
int main()
{
test01();
return 0;
}

View File

@ -294,7 +294,7 @@ namespace std
const extern_type* end, size_t max) const
{
const extern_type* beg = from;
while (from < end && max)
while (from < end)
{
unsigned char c = *from;
if (c & 0xc0)
@ -304,6 +304,8 @@ namespace std
++from;
continue;
}
if (max == 0) break;
unsigned char tmp;
if (state.value & 0x8)