diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index eb7a66d8357..30d0c0c049e 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,46 @@ +2003-02-24 Paolo Carlini + Nathan Myers + + PR libstdc++/9404, PR libstdc++/9701 (partial) + (aka pptr == epptr implies overflow) + * include/bits/fstream.tcc (_M_allocate_internal_buffer): + Consistently, _M_out_end points to the end of the buffer just + created. + (overflow): Tweak to use _M_out_buf_size(). + (_M_convert_to_external): The role of the old _M_out_end is + now played by _M_out_lim. + (_M_really_overflow): Likewise. + (seekoff): Likewise. + (setbuf): _M_out_end points to the end of the external buffer. + * include/bits/sstream.tcc (overflow): Rewrote, taking into + account the resolution of DR 169 (TC). + (seekoff): Use _M_string.capacity(); ios_base::end is now _M_out_lim. + (seekpos): Use _M_string.capacity(); tweak. + * include/bits/streambuf.tcc (sputc, xsputn): Remove comments. + * include/std/std_fstream.h (sync): The role of the old + _M_out_end is now played by _M_out_lim. + (_M_set_indeterminate): Use _M_set_determinate. + (_M_set_determinate): _M_out_end is now _M_out_lim. + (_M_is_indeterminate): Likewise. + * include/std/std_sstream.h (str()): _M_out_end is now _M_out_lim. + (_M_stringbuf_init): Don't set _M_buf_size, unused for sstreams, + which have the information readily available as _M_string.capacity(); + for ate and app modes, pass the string size to _M_really_sync. + (_M_really_sync): Consistently set _M_out_end and _M_out_lim, to + point to the end of the buffer (i.e., epptr) and to the string end, + respectively. + * include/std/std_streambuf.h: tweak comments, add _M_out_lim, + which points to the right limit of the used put area. + (_M_out_cur_move): The role of the old _M_out_end is now played + by _M_out_lim. + (_M_out_buf_size): Simplify: now (when _M_out_cur) return simply + _M_out_end - _M_out_cur (i.e., pptr), _very_ close to the letter + of the standard. + (basic_streambuf()): Initialize _M_out_lim too. + * testsuite/27_io/filebuf_virtuals.cc (test10): Trivial tweak. + * testsuite/27_io/filebuf_virtuals.cc (test11): Add. + * testsuite/27_io/stringbuf_virtuals.cc (test09): Add. + 2003-02-24 Benjamin Kosnik * testsuite/27_io/ios_base_storage.cc (main): Call diff --git a/libstdc++-v3/include/bits/fstream.tcc b/libstdc++-v3/include/bits/fstream.tcc index 0c6e7b04b6b..be4b0c02947 100644 --- a/libstdc++-v3/include/bits/fstream.tcc +++ b/libstdc++-v3/include/bits/fstream.tcc @@ -48,8 +48,10 @@ namespace std { this->_M_buf_size = this->_M_buf_size_opt; - // Allocate internal buffer. - this->_M_buf = new char_type[this->_M_buf_size]; + // Allocate internal buffer... + this->_M_buf = new char_type[this->_M_buf_size]; + // ... and consistently set the end of buffer pointer. + this->_M_out_end = this->_M_buf + this->_M_buf_size; _M_buf_allocated = true; } } @@ -125,7 +127,7 @@ namespace std { const int_type __eof = traits_type::eof(); bool __testput = this->_M_out_cur - && this->_M_out_beg < this->_M_out_end; + && this->_M_out_beg < this->_M_out_lim; if (__testput && traits_type::eq_int_type(_M_really_overflow(__eof), __eof)) return __ret; @@ -245,8 +247,7 @@ namespace std overflow(int_type __c) { int_type __ret = traits_type::eof(); - bool __testput = this->_M_out_cur - && this->_M_out_cur < this->_M_buf + this->_M_buf_size; + bool __testput = _M_out_buf_size(); bool __testout = this->_M_mode & ios_base::out; if (__testout) @@ -314,7 +315,7 @@ namespace std if (__r == codecvt_base::partial) { const char_type* __iresume = __iend; - streamsize __rlen = this->_M_out_end - __iend; + streamsize __rlen = this->_M_out_lim - __iend; __r = __cvt.out(_M_state_cur, __iresume, __iresume + __rlen, __iend, __buf, __buf + __blen, __bend); if (__r != codecvt_base::error) @@ -336,7 +337,7 @@ namespace std _M_really_overflow(int_type __c) { int_type __ret = traits_type::eof(); - bool __testput = this->_M_out_cur && this->_M_out_beg < this->_M_out_end; + bool __testput = this->_M_out_cur && this->_M_out_beg < this->_M_out_lim; bool __testunbuffered = _M_file.is_open() && !this->_M_buf_size_opt; if (__testput || __testunbuffered) @@ -358,7 +359,7 @@ namespace std // NB: In the unbuffered case, no internal buffer exists. if (!__testunbuffered) _M_convert_to_external(this->_M_out_beg, - this->_M_out_end - this->_M_out_beg, + this->_M_out_lim - this->_M_out_beg, __elen, __plen); // Convert pending sequence to external representation, output. @@ -398,12 +399,15 @@ namespace std // that an external char_type array of length (__s + __n) // exists and has been pre-allocated. If this is not the // case, things will quickly blow up. + // Step 1: Destroy the current internal array. _M_destroy_internal_buffer(); // Step 2: Use the external array. this->_M_buf = __s; this->_M_buf_size_opt = this->_M_buf_size = __n; + // Consistently set the end of buffer pointer. + this->_M_out_end = this->_M_buf + this->_M_buf_size; _M_set_indeterminate(); } _M_last_overflowed = false; @@ -437,7 +441,7 @@ namespace std bool __testget = this->_M_in_cur && this->_M_in_beg < this->_M_in_end; bool __testput = this->_M_out_cur - && this->_M_out_beg < this->_M_out_end; + && this->_M_out_beg < this->_M_out_lim; // Sync the internal and external streams. // out if (__testput || _M_last_overflowed) diff --git a/libstdc++-v3/include/bits/sstream.tcc b/libstdc++-v3/include/bits/sstream.tcc index 489f79f20a3..5f9f62e9342 100644 --- a/libstdc++-v3/include/bits/sstream.tcc +++ b/libstdc++-v3/include/bits/sstream.tcc @@ -80,38 +80,35 @@ namespace std basic_stringbuf<_CharT, _Traits, _Alloc>:: overflow(int_type __c) { - int_type __ret = traits_type::eof(); - bool __testeof = traits_type::eq_int_type(__c, __ret); - bool __testwrite = this->_M_out_cur < this->_M_buf + this->_M_buf_size; bool __testout = this->_M_mode & ios_base::out; + if (__builtin_expect(!__testout, false)) + return traits_type::eof(); + bool __testeof = traits_type::eq_int_type(__c, traits_type::eof()); + if (__builtin_expect(__testeof, false)) + return traits_type::not_eof(__c); + + // In virtue of DR 169 (TC) we are allowed to grow more than + // one char the first time and also... + __size_type __len = + std::max(_M_string.capacity() + 1, this->_M_buf_size_opt); + + bool __testwrite = _M_out_buf_size(); + if (__builtin_expect(!__testwrite && __len > _M_string.max_size(), false)) + return traits_type::eof(); // Try to append __c into output sequence in one of two ways. // Order these tests done in is unspecified by the standard. - if (__testout) + if (!__testwrite) { - if (!__testeof) - { - __size_type __len = std::max(this->_M_buf_size, - this->_M_buf_size_opt); - __len *= 2; - - if (__testwrite) - __ret = this->sputc(traits_type::to_char_type(__c)); - else if (__len <= _M_string.max_size()) - { - // Force-allocate, re-sync. - _M_string = this->str(); - _M_string.reserve(__len); - this->_M_buf_size = __len; - _M_really_sync(this->_M_in_cur - this->_M_in_beg, - this->_M_out_cur - this->_M_out_beg); - __ret = this->sputc(traits_type::to_char_type(__c)); - } - } - else - __ret = traits_type::not_eof(__c); + // Force-allocate, re-sync. + _M_string = this->str(); + // ... the next times. That's easy to implement thanks to the + // exponential growth policy builtin into basic_string. + _M_string.reserve(__len); + _M_really_sync(this->_M_in_cur - this->_M_in_beg, + this->_M_out_cur - this->_M_out_beg); } - return __ret; + return this->sputc(traits_type::to_char_type(__c)); } template @@ -126,7 +123,7 @@ namespace std __testin &= !(__mode & ios_base::out); __testout &= !(__mode & ios_base::in); - if (this->_M_buf_size && (__testin || __testout || __testboth)) + if (_M_string.capacity() && (__testin || __testout || __testboth)) { char_type* __beg = this->_M_buf; char_type* __curi = NULL; @@ -142,7 +139,9 @@ namespace std if (__testout || __testboth) { __curo = this->pptr(); - __endo = this->epptr(); + // Due to the resolution of DR169, ios_base::end + // is this->_M_out_lim, not epptr(). + __endo = this->_M_out_lim; } off_type __newoffi = 0; @@ -181,7 +180,7 @@ namespace std { pos_type __ret = pos_type(off_type(-1)); - if (this->_M_buf_size) + if (_M_string.capacity()) { off_type __pos = __sp; // Use streamoff operator to do conversion. char_type* __beg = NULL; @@ -205,7 +204,7 @@ namespace std if (__testout || __testboth) { __beg = this->pbase(); - __end = this->_M_buf + this->_M_buf_size; + __end = this->epptr(); if (0 <= __pos && __pos <= __end - __beg) __testposo = true; } diff --git a/libstdc++-v3/include/bits/streambuf.tcc b/libstdc++-v3/include/bits/streambuf.tcc index e721f25ed91..c14139988af 100644 --- a/libstdc++-v3/include/bits/streambuf.tcc +++ b/libstdc++-v3/include/bits/streambuf.tcc @@ -93,11 +93,6 @@ namespace std return __ret; } - // Don't test against _M_buf + _M_buf_size, because _M_buf reflects - // allocated space, and on certain (rare but entirely legal) - // situations, there will be no allocated space yet the internal - // buffers will still be valid. (This happens if setp is used to set - // the internal buffer to say some externally-allocated sequence.) template typename basic_streambuf<_CharT, _Traits>::int_type basic_streambuf<_CharT, _Traits>:: @@ -149,11 +144,6 @@ namespace std return __ret; } - // Don't test against _M_buf + _M_buf_size, because _M_buf reflects - // allocated space, and on certain (rare but entirely legal) - // situations, there will be no allocated space yet the internal - // buffers will still be valid. (This happens if setp is used to set - // the internal buffer to say some externally-allocated sequence.) template streamsize basic_streambuf<_CharT, _Traits>:: diff --git a/libstdc++-v3/include/std/std_fstream.h b/libstdc++-v3/include/std/std_fstream.h index d7a0e0956bd..6dd75dd1bba 100644 --- a/libstdc++-v3/include/std/std_fstream.h +++ b/libstdc++-v3/include/std/std_fstream.h @@ -312,14 +312,14 @@ namespace std sync() { bool __testput = this->_M_out_cur - && this->_M_out_beg < this->_M_out_end; + && this->_M_out_beg < this->_M_out_lim; // Make sure that the internal buffer resyncs its idea of // the file position with the external file. if (__testput) { // Need to restore current position after the write. - off_type __off = this->_M_out_cur - this->_M_out_end; + off_type __off = this->_M_out_cur - this->_M_out_lim; _M_really_overflow(); // _M_file.sync() will be called within if (__off) _M_file.seekoff(__off, ios_base::cur); @@ -387,11 +387,7 @@ namespace std void _M_set_indeterminate(void) { - if (this->_M_mode & ios_base::in) - this->setg(this->_M_buf, this->_M_buf, this->_M_buf); - if (this->_M_mode & ios_base::out) - this->setp(this->_M_buf, this->_M_buf); - _M_filepos = this->_M_buf; + _M_set_determinate(off_type(0)); } /** @@ -405,9 +401,15 @@ namespace std bool __testin = this->_M_mode & ios_base::in; bool __testout = this->_M_mode & ios_base::out; if (__testin) - this->setg(this->_M_buf, this->_M_buf, this->_M_buf + __off); + { + this->_M_in_beg = this->_M_in_cur = this->_M_buf; + this->_M_in_end = this->_M_buf + __off; + } if (__testout) - this->setp(this->_M_buf, this->_M_buf + __off); + { + this->_M_out_beg = this->_M_out_cur = this->_M_buf; + this->_M_out_lim = this->_M_buf + __off; + } _M_filepos = this->_M_buf + __off; } @@ -419,16 +421,18 @@ namespace std bool _M_is_indeterminate(void) { + bool __testin = this->_M_mode & ios_base::in; + bool __testout = this->_M_mode & ios_base::out; bool __ret = false; // Don't return true if unbuffered. if (this->_M_buf) { - if (this->_M_mode & ios_base::in) + if (__testin) __ret = this->_M_in_beg == this->_M_in_cur && this->_M_in_cur == this->_M_in_end; - if (this->_M_mode & ios_base::out) + if (__testout) __ret = this->_M_out_beg == this->_M_out_cur - && this->_M_out_cur == this->_M_out_end; + && this->_M_out_cur == this->_M_out_lim; } return __ret; } diff --git a/libstdc++-v3/include/std/std_sstream.h b/libstdc++-v3/include/std/std_sstream.h index 268478f3339..cb538b56304 100644 --- a/libstdc++-v3/include/std/std_sstream.h +++ b/libstdc++-v3/include/std/std_sstream.h @@ -140,8 +140,8 @@ namespace std // _M_string, and may not be the correct size of the // current stringbuf internal buffer. __size_type __len = _M_string.size(); - if (this->_M_out_end > this->_M_out_beg) - __len = std::max(__size_type(this->_M_out_end + if (this->_M_out_lim > this->_M_out_beg) + __len = std::max(__size_type(this->_M_out_lim - this->_M_out_beg), __len); return __string_type(this->_M_out_beg, this->_M_out_beg + __len); } @@ -174,13 +174,6 @@ namespace std void _M_stringbuf_init(ios_base::openmode __mode) { - // _M_buf_size is a convenient alias for "what the streambuf - // thinks the allocated size of the string really is." This is - // necessary as ostringstreams are implemented with the - // streambufs having control of the allocation and - // re-allocation of the internal string object, _M_string. - this->_M_buf_size = _M_string.size(); - // NB: Start ostringstream buffers at 512 bytes. This is an // experimental value (pronounced "arbitrary" in some of the // hipper english-speaking countries), and can be changed to @@ -188,7 +181,7 @@ namespace std this->_M_buf_size_opt = 512; this->_M_mode = __mode; if (this->_M_mode & (ios_base::ate | ios_base::app)) - _M_really_sync(0, this->_M_buf_size); + _M_really_sync(0, _M_string.size()); else _M_really_sync(0, 0); } @@ -268,7 +261,9 @@ namespace std this->setg(__base, __base + __i, __base + __len); if (__testout) { - this->setp(__base, __base + __len); + this->setp(__base, __base + _M_string.capacity()); + // _M_out_lim points to the string end. + this->_M_out_lim = __base + __len; this->_M_out_cur += __o; } return 0; diff --git a/libstdc++-v3/include/std/std_streambuf.h b/libstdc++-v3/include/std/std_streambuf.h index e7dca92d00b..850f2015530 100644 --- a/libstdc++-v3/include/std/std_streambuf.h +++ b/libstdc++-v3/include/std/std_streambuf.h @@ -172,7 +172,8 @@ namespace std /** * @if maint - * Actual size of allocated internal buffer, in bytes. + * Actual size of allocated internal buffer, in bytes. Unused + * for sstreams, which have readily available _M_string.capacity(). * @endif */ size_t _M_buf_size; @@ -202,12 +203,15 @@ namespace std * - put == output == write * @endif */ - char_type* _M_in_beg; // Start of get area. - char_type* _M_in_cur; // Current read area. - char_type* _M_in_end; // End of get area. - char_type* _M_out_beg; // Start of put area. - char_type* _M_out_cur; // Current put area. - char_type* _M_out_end; // End of put area. + char_type* _M_in_beg; // Start of get area. + char_type* _M_in_cur; // Current read area. + char_type* _M_in_end; // End of get area. + char_type* _M_out_beg; // Start of put area. + char_type* _M_out_cur; // Current put area. + char_type* _M_out_end; // End of put area. + + char_type* _M_out_lim; // Right limit of used put area. + //@} /** @@ -305,13 +309,13 @@ namespace std } // Correctly sets the _M_out_cur pointer, and bumps the - // appropriate _M_*_end pointers as well. Necessary for the - // un-tied stringbufs, in in|out mode. + // appropriate _M_out_lim and _M_in_end pointers as well. Necessary + // for the un-tied stringbufs, in in|out mode. // Invariant: - // __n + _M_out_[cur, end] <= _M_buf + _M_buf_size - // Assuming all _M_*_[beg, cur, end] pointers are operating on + // __n + _M_out_[cur, lim] <= _M_out_end + // Assuming all _M_out_[beg, cur, lim] pointers are operating on // the same range: - // _M_buf <= _M_*_ <= _M_buf + _M_buf_size + // _M_buf <= _M_*_ <= _M_out_end void _M_out_cur_move(off_type __n) // argument needs to be +- { @@ -320,32 +324,23 @@ namespace std _M_out_cur += __n; if (__testin && _M_buf_unified) _M_in_cur += __n; - if (_M_out_cur > _M_out_end) + if (_M_out_cur > _M_out_lim) { - _M_out_end = _M_out_cur; + _M_out_lim = _M_out_cur; // NB: in | out buffers drag the _M_in_end pointer along... if (__testin) _M_in_end += __n; } } - // Return the size of the output buffer. This depends on the - // buffer in use: allocated buffers have a stored size in - // _M_buf_size and setbuf() buffers don't. + // Returns zero if the output buffer is full (-> overflow). off_type _M_out_buf_size() { - off_type __ret = 0; if (_M_out_cur) - { - // Using allocated buffer. - if (_M_out_beg == _M_buf) - __ret = _M_out_beg + _M_buf_size - _M_out_cur; - // Using non-allocated buffer. - else - __ret = _M_out_end - _M_out_cur; - } - return __ret; + return _M_out_end - _M_out_cur; + else + return off_type(0); } public: @@ -568,8 +563,8 @@ namespace std */ basic_streambuf() : _M_buf(NULL), _M_buf_size(0), _M_buf_size_opt(BUFSIZ), - _M_buf_unified(false), _M_in_beg(0), _M_in_cur(0), _M_in_end(0), - _M_out_beg(0), _M_out_cur(0), _M_out_end(0), + _M_buf_unified(false), _M_in_beg(0), _M_in_cur(0), _M_in_end(0), + _M_out_beg(0), _M_out_cur(0), _M_out_end(0), _M_out_lim(0), _M_mode(ios_base::openmode(0)), _M_buf_locale(locale()), _M_pback_cur_save(0), _M_pback_end_save(0), _M_pback_init(false) @@ -666,7 +661,7 @@ namespace std setp(char_type* __pbeg, char_type* __pend) { _M_out_beg = _M_out_cur = __pbeg; - _M_out_end = __pend; + _M_out_end = __pend; if (!(_M_mode & ios_base::out) && __pbeg && __pend) _M_mode = _M_mode | ios_base::out; } diff --git a/libstdc++-v3/testsuite/27_io/filebuf_virtuals.cc b/libstdc++-v3/testsuite/27_io/filebuf_virtuals.cc index 843038aab48..fc9262f8e75 100644 --- a/libstdc++-v3/testsuite/27_io/filebuf_virtuals.cc +++ b/libstdc++-v3/testsuite/27_io/filebuf_virtuals.cc @@ -71,7 +71,8 @@ const char carray_02[] = "memphis, new orleans, and savanah"; const char name_01[] = "filebuf_virtuals-1.txt"; // file with data in it const char name_02[] = "filebuf_virtuals-2.txt"; // empty file, need to create const char name_03[] = "filebuf_virtuals-3.txt"; // empty file, need to create - +const char name_04[] = "filebuf_virtuals-4.txt"; // empty file, need to create +const char name_05[] = "filebuf_virtuals-5.txt"; // empty file, need to create class derived_filebuf: public std::filebuf { @@ -607,14 +608,14 @@ void test10() { ofstream out; out.imbue(loc); - out.open("filebuf_virtuals-4.txt"); + out.open(name_04); copy(str.begin(), str.end(), ostreambuf_iterator(out)); } { ifstream in; - in.open("filebuf_virtuals-4.txt"); + in.open(name_04); copy(istreambuf_iterator(in), istreambuf_iterator(), back_inserter(tmp)); @@ -624,6 +625,62 @@ void test10() VERIFY( tmp == str ); } +bool over_called; + +class Derived_filebuf : public std::filebuf +{ +public: + int_type overflow(int_type c) + { + over_called = true; + return std::filebuf::overflow(c); + } + + const char_type* pub_epptr() const + { + return epptr(); + } + + const char_type* pub_pptr() const + { + return pptr(); + } +}; + +// libstdc++/9701 (partial) +void test11() +{ + bool test = true; + + bool over_expected; + + // sputc + Derived_filebuf dfbuf_01; + dfbuf_01.open(name_05, std::ios_base::out); + over_called = false; + dfbuf_01.sputc('i'); + VERIFY( !over_called ); + over_expected = dfbuf_01.pub_epptr() == dfbuf_01.pub_pptr(); + over_called = false; + dfbuf_01.sputc('v'); + VERIFY( (!over_expected && !over_called) + || (over_expected && over_called) ); + dfbuf_01.close(); + + // sputn + Derived_filebuf dfbuf_02; + dfbuf_02.open(name_05, std::ios_base::out); + over_called = false; + dfbuf_02.sputn("sonne's", 7); + VERIFY( !over_called ); + over_expected = dfbuf_02.pub_epptr() == dfbuf_02.pub_pptr(); + over_called = false; + dfbuf_02.sputn(" peak", 5); + VERIFY( (!over_expected && !over_called) + || (over_expected && over_called) ); + dfbuf_02.close(); +} + main() { test01(); @@ -638,5 +695,6 @@ main() test08(); test09(); test10(); + test11(); return 0; } diff --git a/libstdc++-v3/testsuite/27_io/stringbuf_virtuals.cc b/libstdc++-v3/testsuite/27_io/stringbuf_virtuals.cc index 8a6add9dfc5..a2a8368abfc 100644 --- a/libstdc++-v3/testsuite/27_io/stringbuf_virtuals.cc +++ b/libstdc++-v3/testsuite/27_io/stringbuf_virtuals.cc @@ -92,6 +92,61 @@ void test08() VERIFY( ob.getloc() == loc_de ); } +bool over_called; + +class Derived_stringbuf : public std::stringbuf +{ +public: + int_type overflow(int_type c) + { + over_called = true; + return std::stringbuf::overflow(c); + } + + const char_type* pub_epptr() const + { + return epptr(); + } + + const char_type* pub_pptr() const + { + return pptr(); + } +}; + +// libstdc++/9404 +void test09() +{ + bool test = true; + + bool over_expected; + + // sputc + Derived_stringbuf dsbuf_01; + over_called = false; + dsbuf_01.sputc('i'); + VERIFY( over_called ); + over_expected = dsbuf_01.pub_epptr() == dsbuf_01.pub_pptr(); + over_called = false; + dsbuf_01.sputc('v'); + VERIFY( (!over_expected && !over_called) + || (over_expected && over_called) ); + dsbuf_01.sputc('i'); + VERIFY( dsbuf_01.str() == "ivi" ); // Sanity check. + + // sputn + Derived_stringbuf dsbuf_02; + over_called = false; + dsbuf_02.sputn("sonne's", 7); + VERIFY( over_called ); + over_expected = dsbuf_02.pub_epptr() == dsbuf_02.pub_pptr(); + over_called = false; + dsbuf_02.sputn(" peak", 5); + VERIFY( (!over_expected && !over_called) + || (over_expected && over_called) ); + VERIFY( dsbuf_02.str() == "sonne's peak" ); // Sanity check. +} + int main() { using namespace std; @@ -106,5 +161,6 @@ int main() test02(in3, false); test08(); + test09(); return 0; }