X-Git-Url: http://git.sourceforge.jp/view?p=pf3gnuchains%2Fgcc-fork.git;a=blobdiff_plain;f=libstdc%2B%2B-v3%2Finclude%2Fbits%2Ffstream.tcc;h=bd0fa4be0101a2cc84590e8c9451849e1f28864f;hp=9bbb5cc1a52927f28322110aff6ccce51bd70ca1;hb=dee74d2ba3c045ae57d2be35f6e3a0be61e5434a;hpb=748d903e34527d9876c8a1a26fb22be0066b0fe5 diff --git a/libstdc++-v3/include/bits/fstream.tcc b/libstdc++-v3/include/bits/fstream.tcc index 9bbb5cc1a52..bd0fa4be010 100644 --- a/libstdc++-v3/include/bits/fstream.tcc +++ b/libstdc++-v3/include/bits/fstream.tcc @@ -1,12 +1,13 @@ // File based streams -*- C++ -*- -// Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003 +// Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, +// 2007, 2008, 2009, 2010, 2011 // 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 2, or (at your option) +// 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, @@ -14,45 +15,47 @@ // 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 COPYING. If not, write to the Free -// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, -// USA. +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. -// As a special exception, you may use this file as part of a free software -// library without restriction. Specifically, if other files instantiate -// templates or use macros or inline functions from this file, or you compile -// this file and link it with other files to produce an executable, this -// file does not by itself cause the resulting executable to be covered by -// the GNU General Public License. This exception does not however -// invalidate any other reasons why the executable file might be covered by -// the GNU General Public License. +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// . + +/** @file bits/fstream.tcc + * This is an internal header file, included by other library headers. + * Do not attempt to use it directly. @headername{fstream} + */ // // ISO C++ 14882: 27.8 File-based streams // -#ifndef _CPP_BITS_FSTREAM_TCC -#define _CPP_BITS_FSTREAM_TCC 1 +#ifndef _FSTREAM_TCC +#define _FSTREAM_TCC 1 #pragma GCC system_header -namespace std -{ +#include + +_GLIBCXX_BEGIN_NAMESPACE(std) + template void basic_filebuf<_CharT, _Traits>:: _M_allocate_internal_buffer() { - if (!_M_buf_allocated && this->_M_buf_size) + // Allocate internal buffer only if one doesn't already exist + // (either allocated or provided by the user via setbuf). + if (!_M_buf_allocated && !_M_buf) { - // Allocate internal buffer. - this->_M_buf = new char_type[this->_M_buf_size]; + _M_buf = new char_type[_M_buf_size]; _M_buf_allocated = true; } } - // Both close and setbuf need to deallocate internal buffers, if it exists. template void basic_filebuf<_CharT, _Traits>:: @@ -60,48 +63,57 @@ namespace std { if (_M_buf_allocated) { - delete [] this->_M_buf; - this->_M_buf = NULL; + delete [] _M_buf; + _M_buf = 0; _M_buf_allocated = false; - this->setg(NULL, NULL, NULL); - this->setp(NULL, NULL); } + delete [] _M_ext_buf; + _M_ext_buf = 0; + _M_ext_buf_size = 0; + _M_ext_next = 0; + _M_ext_end = 0; } template basic_filebuf<_CharT, _Traits>:: - basic_filebuf() : __streambuf_type(), _M_file(&_M_lock), - _M_state_cur(__state_type()), _M_state_beg(__state_type()), - _M_buf(NULL), _M_buf_size(BUFSIZ), _M_buf_allocated(false), - _M_last_overflowed(false), _M_filepos(0), _M_pback_cur_save(0), - _M_pback_end_save(0), _M_pback_init(false), _M_codecvt(0) - { - this->_M_buf_unified = true; - if (__builtin_expect(has_facet<__codecvt_type>(this->_M_buf_locale), - true)) + basic_filebuf() : __streambuf_type(), _M_lock(), _M_file(&_M_lock), + _M_mode(ios_base::openmode(0)), _M_state_beg(), _M_state_cur(), + _M_state_last(), _M_buf(0), _M_buf_size(BUFSIZ), + _M_buf_allocated(false), _M_reading(false), _M_writing(false), _M_pback(), + _M_pback_cur_save(0), _M_pback_end_save(0), _M_pback_init(false), + _M_codecvt(0), _M_ext_buf(0), _M_ext_buf_size(0), _M_ext_next(0), + _M_ext_end(0) + { + if (has_facet<__codecvt_type>(this->_M_buf_locale)) _M_codecvt = &use_facet<__codecvt_type>(this->_M_buf_locale); } template - typename basic_filebuf<_CharT, _Traits>::__filebuf_type* + typename basic_filebuf<_CharT, _Traits>::__filebuf_type* basic_filebuf<_CharT, _Traits>:: open(const char* __s, ios_base::openmode __mode) { - __filebuf_type *__ret = NULL; + __filebuf_type *__ret = 0; if (!this->is_open()) { _M_file.open(__s, __mode); if (this->is_open()) { _M_allocate_internal_buffer(); - this->_M_mode = __mode; + _M_mode = __mode; - // Setup initial position of buffer. - _M_set_buffer(0); + // Setup initial buffer to 'uncommitted' mode. + _M_reading = false; + _M_writing = false; + _M_set_buffer(-1); + + // Reset to initial state. + _M_state_last = _M_state_cur = _M_state_beg; - if ((__mode & ios_base::ate) - && this->seekoff(0, ios_base::end, __mode) < 0) - // 27.8.1.3,4 + // 27.8.1.3,4 + if ((__mode & ios_base::ate) + && this->seekoff(0, ios_base::end, __mode) + == pos_type(off_type(-1))) this->close(); else __ret = this; @@ -111,290 +123,389 @@ namespace std } template - typename basic_filebuf<_CharT, _Traits>::__filebuf_type* + typename basic_filebuf<_CharT, _Traits>::__filebuf_type* basic_filebuf<_CharT, _Traits>:: - close() throw() + close() { - __filebuf_type* __ret = NULL; - if (this->is_open()) - { - bool __testfail = false; - try - { - const int_type __eof = traits_type::eof(); - const bool __testput = this->_M_out_beg < this->_M_out_lim; - - if (__testput - && traits_type::eq_int_type(this->overflow(), __eof)) - __testfail = true; + if (!this->is_open()) + return 0; -#if 0 - // XXX not done - if (_M_last_overflowed) - { - _M_output_unshift(); - this->overflow(); - } -#endif - } - catch(...) - { + bool __testfail = false; + { + // NB: Do this here so that re-opened filebufs will be cool... + struct __close_sentry + { + basic_filebuf *__fb; + __close_sentry (basic_filebuf *__fbi): __fb(__fbi) { } + ~__close_sentry () + { + __fb->_M_mode = ios_base::openmode(0); + __fb->_M_pback_init = false; + __fb->_M_destroy_internal_buffer(); + __fb->_M_reading = false; + __fb->_M_writing = false; + __fb->_M_set_buffer(-1); + __fb->_M_state_last = __fb->_M_state_cur = __fb->_M_state_beg; + } + } __cs (this); + + __try + { + if (!_M_terminate_output()) __testfail = true; - } - - // NB: Do this here so that re-opened filebufs will be cool... - this->_M_mode = ios_base::openmode(0); - this->_M_pback_init = false; - _M_destroy_internal_buffer(); - - if (!_M_file.close()) - __testfail = true; - - if (!__testfail) - __ret = this; - } - _M_last_overflowed = false; - return __ret; + } + __catch(__cxxabiv1::__forced_unwind&) + { + _M_file.close(); + __throw_exception_again; + } + __catch(...) + { __testfail = true; } + } + + if (!_M_file.close()) + __testfail = true; + + if (__testfail) + return 0; + else + return this; } template - streamsize + streamsize basic_filebuf<_CharT, _Traits>:: showmanyc() { streamsize __ret = -1; - const bool __testin = this->_M_mode & ios_base::in; - + const bool __testin = _M_mode & ios_base::in; if (__testin && this->is_open()) { // For a stateful encoding (-1) the pending sequence might be just // shift and unshift prefixes with no actual character. - __ret = this->_M_in_end - this->_M_in_cur; + __ret = this->egptr() - this->gptr(); + +#if _GLIBCXX_HAVE_DOS_BASED_FILESYSTEM + // About this workaround, see libstdc++/20806. + const bool __testbinary = _M_mode & ios_base::binary; + if (__check_facet(_M_codecvt).encoding() >= 0 + && __testbinary) +#else if (__check_facet(_M_codecvt).encoding() >= 0) +#endif __ret += _M_file.showmanyc() / _M_codecvt->max_length(); } - - _M_last_overflowed = false; return __ret; } - + template - typename basic_filebuf<_CharT, _Traits>::int_type + typename basic_filebuf<_CharT, _Traits>::int_type basic_filebuf<_CharT, _Traits>:: - _M_underflow(bool __bump) + underflow() { int_type __ret = traits_type::eof(); - const bool __testin = this->_M_mode & ios_base::in; - const bool __testout = this->_M_mode & ios_base::out; - + const bool __testin = _M_mode & ios_base::in; if (__testin) { - // Check for pback madness, and if so swich back to the + if (_M_writing) + { + if (overflow() == 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... _M_destroy_pback(); - if (this->_M_in_cur < this->_M_in_end) - { - __ret = traits_type::to_int_type(*this->_M_in_cur); - if (__bump) - _M_move_in_cur(1); - return __ret; - } + if (this->gptr() < this->egptr()) + return traits_type::to_int_type(*this->gptr()); - // Sync internal and external buffers. - if (__testout && this->_M_out_beg < this->_M_out_lim) - this->overflow(); + // Get and convert input sequence. + const size_t __buflen = _M_buf_size > 1 ? _M_buf_size - 1 : 1; - if (_M_buf_size > 1) + // Will be set to true if ::read() returns 0 indicating EOF. + bool __got_eof = false; + // Number of internal characters produced. + streamsize __ilen = 0; + codecvt_base::result __r = codecvt_base::ok; + if (__check_facet(_M_codecvt).always_noconv()) + { + __ilen = _M_file.xsgetn(reinterpret_cast(this->eback()), + __buflen); + if (__ilen == 0) + __got_eof = true; + } + else { - streamsize __elen = 0; - streamsize __ilen = 0; + // Worst-case number of external bytes. + // XXX Not done encoding() == -1. + const int __enc = _M_codecvt->encoding(); + streamsize __blen; // Minimum buffer size. + streamsize __rlen; // Number of chars to read. + if (__enc > 0) + __blen = __rlen = __buflen * __enc; + else + { + __blen = __buflen + _M_codecvt->max_length() - 1; + __rlen = __buflen; + } + const streamsize __remainder = _M_ext_end - _M_ext_next; + __rlen = __rlen > __remainder ? __rlen - __remainder : 0; + + // An imbue in 'read' mode implies first converting the external + // chars already present. + if (_M_reading && this->egptr() == this->eback() && __remainder) + __rlen = 0; - if (__check_facet(_M_codecvt).always_noconv()) + // Allocate buffer if necessary and move unconverted + // bytes to front. + if (_M_ext_buf_size < __blen) { - __elen = _M_file.xsgetn(reinterpret_cast(this->_M_in_beg), _M_buf_size - 1); - __ilen = __elen; + char* __buf = new char[__blen]; + if (__remainder) + __builtin_memcpy(__buf, _M_ext_next, __remainder); + + delete [] _M_ext_buf; + _M_ext_buf = __buf; + _M_ext_buf_size = __blen; } - else + else if (__remainder) + __builtin_memmove(_M_ext_buf, _M_ext_next, __remainder); + + _M_ext_next = _M_ext_buf; + _M_ext_end = _M_ext_buf + __remainder; + _M_state_last = _M_state_cur; + + do { - char* __buf = static_cast(__builtin_alloca(_M_buf_size - 1)); - __elen = _M_file.xsgetn(__buf, _M_buf_size - 1); - - const char* __eend; - char_type* __iend; - codecvt_base::result __r; - __r = _M_codecvt->in(_M_state_cur, __buf, __buf + __elen, - __eend, this->_M_in_beg, - this->_M_in_beg + _M_buf_size - 1, - __iend); - if (__r == codecvt_base::ok) - __ilen = __iend - this->_M_in_beg; - else if (__r == codecvt_base::noconv) + if (__rlen > 0) { - traits_type::copy(this->_M_in_beg, - reinterpret_cast(__buf), - __elen); - __ilen = __elen; + // Sanity check! + // This may fail if the return value of + // codecvt::max_length() is bogus. + if (_M_ext_end - _M_ext_buf + __rlen > _M_ext_buf_size) + { + __throw_ios_failure(__N("basic_filebuf::underflow " + "codecvt::max_length() " + "is not valid")); + } + streamsize __elen = _M_file.xsgetn(_M_ext_end, __rlen); + if (__elen == 0) + __got_eof = true; + else if (__elen == -1) + break; + _M_ext_end += __elen; } - else + + char_type* __iend = this->eback(); + if (_M_ext_next < _M_ext_end) + __r = _M_codecvt->in(_M_state_cur, _M_ext_next, + _M_ext_end, _M_ext_next, + this->eback(), + this->eback() + __buflen, __iend); + if (__r == codecvt_base::noconv) { - // Unwind. - __ilen = 0; - _M_file.seekoff(-__elen, ios_base::cur, ios_base::in); + size_t __avail = _M_ext_end - _M_ext_buf; + __ilen = std::min(__avail, __buflen); + traits_type::copy(this->eback(), + reinterpret_cast + (_M_ext_buf), __ilen); + _M_ext_next = _M_ext_buf + __ilen; } + else + __ilen = __iend - this->eback(); + + // _M_codecvt->in may return error while __ilen > 0: this is + // ok, and actually occurs in case of mixed encodings (e.g., + // XML files). + if (__r == codecvt_base::error) + break; + + __rlen = 1; } + while (__ilen == 0 && !__got_eof); + } - if (0 < __ilen) - { - _M_set_buffer(__ilen); - __ret = traits_type::to_int_type(*this->_M_in_cur); - if (__bump) - _M_move_in_cur(1); - } + if (__ilen > 0) + { + _M_set_buffer(__ilen); + _M_reading = true; + __ret = traits_type::to_int_type(*this->gptr()); + } + else if (__got_eof) + { + // If the actual end of file is reached, set 'uncommitted' + // mode, thus allowing an immediate write without an + // intervening seek. + _M_set_buffer(-1); + _M_reading = false; + // However, reaching it while looping on partial means that + // the file has got an incomplete character. + if (__r == codecvt_base::partial) + __throw_ios_failure(__N("basic_filebuf::underflow " + "incomplete character in file")); } + else if (__r == codecvt_base::error) + __throw_ios_failure(__N("basic_filebuf::underflow " + "invalid byte sequence in file")); + else + __throw_ios_failure(__N("basic_filebuf::underflow " + "error reading the file")); } - _M_last_overflowed = false; return __ret; } template - typename basic_filebuf<_CharT, _Traits>::int_type + typename basic_filebuf<_CharT, _Traits>::int_type basic_filebuf<_CharT, _Traits>:: pbackfail(int_type __i) { int_type __ret = traits_type::eof(); - const bool __testin = this->_M_mode & ios_base::in; - + const bool __testin = _M_mode & ios_base::in; if (__testin) { - const bool __testpb = this->_M_in_beg < this->_M_in_cur; - char_type __c = traits_type::to_char_type(__i); + if (_M_writing) + { + if (overflow() == 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; const bool __testeof = traits_type::eq_int_type(__i, __ret); - - if (__testpb) + int_type __tmp; + if (this->eback() < this->gptr()) { - const bool __testout = this->_M_mode & ios_base::out; - const bool __testeq = traits_type::eq(__c, this->_M_in_cur[-1]); - - --this->_M_in_cur; - if (__testout) - --this->_M_out_cur; - // Try to put back __c into input sequence in one of three ways. - // Order these tests done in is unspecified by the standard. - if (!__testeof && __testeq) - __ret = __i; - else if (__testeof) - __ret = traits_type::not_eof(__i); - else - { - _M_create_pback(); - *this->_M_in_cur = __c; - __ret = __i; - } + this->gbump(-1); + __tmp = traits_type::to_int_type(*this->gptr()); + } + else if (this->seekoff(-1, ios_base::cur) != pos_type(off_type(-1))) + { + __tmp = this->underflow(); + if (traits_type::eq_int_type(__tmp, __ret)) + return __ret; } else - { - // At the beginning of the buffer, need to make a - // putback position available. - // But the seek may fail (f.i., at the beginning of - // a file, see libstdc++/9439) and in that case - // we return traits_type::eof() - if (this->seekoff(-1, ios_base::cur) >= 0) - { - this->underflow(); - if (!__testeof) - { - if (!traits_type::eq(__c, *this->_M_in_cur)) - { - _M_create_pback(); - *this->_M_in_cur = __c; - } - __ret = __i; - } - else - __ret = traits_type::not_eof(__i); - } - } + { + // At the beginning of the buffer, need to make a + // putback position available. But the seek may fail + // (f.i., at the beginning of a file, see + // libstdc++/9439) and in that case we return + // traits_type::eof(). + return __ret; + } + + // Try to put back __i into input sequence in one of three ways. + // Order these tests done in is unspecified by the standard. + if (!__testeof && traits_type::eq_int_type(__i, __tmp)) + __ret = __i; + else if (__testeof) + __ret = traits_type::not_eof(__i); + else if (!__testpb) + { + _M_create_pback(); + _M_reading = true; + *this->gptr() = traits_type::to_char_type(__i); + __ret = __i; + } } - _M_last_overflowed = false; return __ret; } template - typename basic_filebuf<_CharT, _Traits>::int_type + typename basic_filebuf<_CharT, _Traits>::int_type basic_filebuf<_CharT, _Traits>:: overflow(int_type __c) { int_type __ret = traits_type::eof(); const bool __testeof = traits_type::eq_int_type(__c, __ret); - const bool __testout = this->_M_mode & ios_base::out; - + const bool __testout = _M_mode & ios_base::out; if (__testout) { - if (this->_M_out_beg < this->_M_out_lim) + 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()) { - // Need to restore current position. The position of the - // external byte sequence (_M_file) corresponds to - // _M_filepos, and we need to move it to _M_out_beg for - // the write. - if (_M_filepos != this->_M_out_beg) - _M_file.seekoff(this->_M_out_beg - _M_filepos, ios_base::cur); - // If appropriate, append the overflow char. if (!__testeof) - *this->_M_out_lim++ = traits_type::to_char_type(__c); - + { + *this->pptr() = traits_type::to_char_type(__c); + this->pbump(1); + } + // Convert pending sequence to external representation, - // output. - if (_M_convert_to_external(this->_M_out_beg, - this->_M_out_lim - this->_M_out_beg) - && (!__testeof || (__testeof && !_M_file.sync()))) + // and output. + if (_M_convert_to_external(this->pbase(), + this->pptr() - this->pbase())) { _M_set_buffer(0); __ret = traits_type::not_eof(__c); } } + else if (_M_buf_size > 1) + { + // Overflow in 'uncommitted' mode: set _M_writing, set + // the buffer to the initial 'write' mode, and put __c + // into the buffer. + _M_set_buffer(0); + _M_writing = true; + if (!__testeof) + { + *this->pptr() = traits_type::to_char_type(__c); + this->pbump(1); + } + __ret = traits_type::not_eof(__c); + } else { // Unbuffered. char_type __conv = traits_type::to_char_type(__c); - if (!__testeof && _M_convert_to_external(&__conv, 1)) - __ret = __c; + if (__testeof || _M_convert_to_external(&__conv, 1)) + { + _M_writing = true; + __ret = traits_type::not_eof(__c); + } } } - _M_last_overflowed = true; return __ret; } - + template bool basic_filebuf<_CharT, _Traits>:: _M_convert_to_external(_CharT* __ibuf, streamsize __ilen) { // Sizes of external and pending output. - streamsize __elen = 0; - streamsize __plen = 0; - - if (__check_facet(_M_codecvt).always_noconv() && __ilen) + streamsize __elen; + streamsize __plen; + if (__check_facet(_M_codecvt).always_noconv()) { - __elen += _M_file.xsputn(reinterpret_cast(__ibuf), __ilen); - __plen += __ilen; + __elen = _M_file.xsputn(reinterpret_cast(__ibuf), __ilen); + __plen = __ilen; } else { // Worst-case number of external bytes needed. - int __ext_multiplier = _M_codecvt->encoding(); - if (__ext_multiplier == -1 || __ext_multiplier == 0) - __ext_multiplier = sizeof(char_type); - streamsize __blen = __ilen * __ext_multiplier; + // XXX Not done encoding() == -1. + streamsize __blen = __ilen * _M_codecvt->max_length(); char* __buf = static_cast(__builtin_alloca(__blen)); + char* __bend; const char_type* __iend; codecvt_base::result __r; __r = _M_codecvt->out(_M_state_cur, __ibuf, __ibuf + __ilen, __iend, __buf, __buf + __blen, __bend); - + if (__r == codecvt_base::ok || __r == codecvt_base::partial) __blen = __bend - __buf; else if (__r == codecvt_base::noconv) @@ -404,184 +515,470 @@ namespace std __blen = __ilen; } else - { - // Result == error - __blen = 0; - } - - if (__blen) - { - __elen += _M_file.xsputn(__buf, __blen); - __plen += __blen; - } - + __throw_ios_failure(__N("basic_filebuf::_M_convert_to_external " + "conversion error")); + + __elen = _M_file.xsputn(__buf, __blen); + __plen = __blen; + // Try once more for partial conversions. - if (__r == codecvt_base::partial) + if (__r == codecvt_base::partial && __elen == __plen) { const char_type* __iresume = __iend; - streamsize __rlen = this->_M_out_lim - __iend; + streamsize __rlen = this->pptr() - __iend; __r = _M_codecvt->out(_M_state_cur, __iresume, - __iresume + __rlen, __iend, __buf, + __iresume + __rlen, __iend, __buf, __buf + __blen, __bend); if (__r != codecvt_base::error) { __rlen = __bend - __buf; - __elen += _M_file.xsputn(__buf, __rlen); - __plen += __rlen; + __elen = _M_file.xsputn(__buf, __rlen); + __plen = __rlen; } + else + __throw_ios_failure(__N("basic_filebuf::_M_convert_to_external " + "conversion error")); } } - return __elen && __elen == __plen; + return __elen == __plen; } template - typename basic_filebuf<_CharT, _Traits>::__streambuf_type* + streamsize + basic_filebuf<_CharT, _Traits>:: + xsgetn(_CharT* __s, streamsize __n) + { + // Clear out pback buffer before going on to the real deal... + streamsize __ret = 0; + if (_M_pback_init) + { + if (__n > 0 && this->gptr() == this->eback()) + { + *__s++ = *this->gptr(); // emulate non-underflowing sbumpc + this->gbump(1); + __ret = 1; + --__n; + } + _M_destroy_pback(); + } + else if (_M_writing) + { + if (overflow() == traits_type::eof()) + return __ret; + _M_set_buffer(-1); + _M_writing = false; + } + + // Optimization in the always_noconv() case, to be generalized in the + // future: when __n > __buflen we read directly instead of using the + // buffer repeatedly. + const bool __testin = _M_mode & ios_base::in; + const streamsize __buflen = _M_buf_size > 1 ? _M_buf_size - 1 : 1; + + if (__n > __buflen && __check_facet(_M_codecvt).always_noconv() + && __testin) + { + // First, copy the chars already present in the buffer. + const streamsize __avail = this->egptr() - this->gptr(); + if (__avail != 0) + { + if (__avail == 1) + *__s = *this->gptr(); + else + traits_type::copy(__s, this->gptr(), __avail); + __s += __avail; + this->gbump(__avail); + __ret += __avail; + __n -= __avail; + } + + // Need to loop in case of short reads (relatively common + // with pipes). + streamsize __len; + for (;;) + { + __len = _M_file.xsgetn(reinterpret_cast(__s), + __n); + if (__len == -1) + __throw_ios_failure(__N("basic_filebuf::xsgetn " + "error reading the file")); + if (__len == 0) + break; + + __n -= __len; + __ret += __len; + if (__n == 0) + break; + + __s += __len; + } + + if (__n == 0) + { + _M_set_buffer(0); + _M_reading = true; + } + else if (__len == 0) + { + // If end of file is reached, set 'uncommitted' + // mode, thus allowing an immediate write without + // an intervening seek. + _M_set_buffer(-1); + _M_reading = false; + } + } + else + __ret += __streambuf_type::xsgetn(__s, __n); + + return __ret; + } + + template + streamsize + basic_filebuf<_CharT, _Traits>:: + xsputn(const _CharT* __s, streamsize __n) + { + streamsize __ret = 0; + // Optimization in the always_noconv() case, to be generalized in the + // future: when __n is sufficiently large we write directly instead of + // using the buffer. + const bool __testout = _M_mode & ios_base::out; + if (__check_facet(_M_codecvt).always_noconv() + && __testout && !_M_reading) + { + // Measurement would reveal the best choice. + const streamsize __chunk = 1ul << 10; + streamsize __bufavail = this->epptr() - this->pptr(); + + // Don't mistake 'uncommitted' mode buffered with unbuffered. + if (!_M_writing && _M_buf_size > 1) + __bufavail = _M_buf_size - 1; + + const streamsize __limit = std::min(__chunk, __bufavail); + if (__n >= __limit) + { + const streamsize __buffill = this->pptr() - this->pbase(); + const char* __buf = reinterpret_cast(this->pbase()); + __ret = _M_file.xsputn_2(__buf, __buffill, + reinterpret_cast(__s), + __n); + if (__ret == __buffill + __n) + { + _M_set_buffer(0); + _M_writing = true; + } + if (__ret > __buffill) + __ret -= __buffill; + else + __ret = 0; + } + else + __ret = __streambuf_type::xsputn(__s, __n); + } + else + __ret = __streambuf_type::xsputn(__s, __n); + return __ret; + } + + template + typename basic_filebuf<_CharT, _Traits>::__streambuf_type* basic_filebuf<_CharT, _Traits>:: setbuf(char_type* __s, streamsize __n) { - if (!this->is_open() && __s == 0 && __n == 0) - this->_M_buf_size = 0; - else if (__s && __n > 1) + if (!this->is_open()) { - // This is implementation-defined behavior, and assumes 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. The length argument __n must - // be greater than 1 because __n - 1 positions will be used - // for the get and put areas, and 1 position is needed to - // host the overflow char of a full put area. - - // 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 = __n; - _M_set_buffer(0); + if (__s == 0 && __n == 0) + _M_buf_size = 1; + else if (__s && __n > 0) + { + // This is implementation-defined behavior, and assumes that + // an external char_type array of length __n exists and has + // been pre-allocated. If this is not the case, things will + // quickly blow up. When __n > 1, __n - 1 positions will be + // used for the get area, __n - 1 for the put area and 1 + // position to host the overflow char of a full put area. + // When __n == 1, 1 position will be used for the get area + // and 0 for the put area, as in the unbuffered case above. + _M_buf = __s; + _M_buf_size = __n; + } } - _M_last_overflowed = false; - return this; + return this; } - + + + // According to 27.8.1.4 p11 - 13, seekoff should ignore the last + // argument (of type openmode). template typename basic_filebuf<_CharT, _Traits>::pos_type basic_filebuf<_CharT, _Traits>:: - seekoff(off_type __off, ios_base::seekdir __way, ios_base::openmode __mode) + seekoff(off_type __off, ios_base::seekdir __way, ios_base::openmode) { - pos_type __ret = pos_type(off_type(-1)); - const bool __testin = (ios_base::in & this->_M_mode & __mode) != 0; - const bool __testout = (ios_base::out & this->_M_mode & __mode) != 0; - int __width = 0; if (_M_codecvt) - __width = _M_codecvt->encoding(); + __width = _M_codecvt->encoding(); if (__width < 0) __width = 0; - const bool __testfail = __off != 0 && __width <= 0; - if (this->is_open() && !__testfail && (__testin || __testout)) + pos_type __ret = pos_type(off_type(-1)); + const bool __testfail = __off != 0 && __width <= 0; + if (this->is_open() && !__testfail) { - // Ditch any pback buffers to avoid confusion. - _M_destroy_pback(); - - if (__way != ios_base::cur || __off != 0) - { - // Sync the internal and external streams. - const bool __testget = this->_M_in_beg < this->_M_in_end; - const bool __testput = this->_M_out_beg < this->_M_out_lim; - off_type __computed_off = __width * __off; + // 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()); - if (__testput || _M_last_overflowed) - { - // Part one: update the output sequence. - this->sync(); - - // Part two: output unshift sequence. - _M_output_unshift(); - } - else if (__testget && __way == ios_base::cur) - __computed_off += this->_M_in_cur - _M_filepos; - - // Return pos_type(off_type(-1)) in case of failure. - __ret = _M_file.seekoff(__computed_off, __way, __mode); - _M_set_buffer(0); + // Ditch any pback buffers to avoid confusion. + if (!__no_movement) + _M_destroy_pback(); + + // Correct state at destination. Note that this is the correct + // state for the current position during output, because + // codecvt::unshift() returns the state to the initial state. + // This is also the correct state at the end of the file because + // an unshift sequence should have been written at the end. + __state_type __state = _M_state_beg; + off_type __computed_off = __off * __width; + if (_M_reading && __way == ios_base::cur) + { + __state = _M_state_last; + __computed_off += _M_get_ext_pos(__state); } + if (!__no_movement) + __ret = _M_seek(__computed_off, __way, __state); else { - // NB: Need to do this in case _M_file in indeterminate - // state, ie _M_file._offset == -1 - pos_type __tmp = _M_file.seekoff(__off, ios_base::cur, __mode); - if (__tmp >= 0) + 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)) { - // Seek successful. - __ret = __tmp; - __ret += std::max(this->_M_out_cur, this->_M_in_cur) - - _M_filepos; + __ret = __file_off + __computed_off; + __ret.state(__state); } } } - _M_last_overflowed = false; return __ret; } + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 171. Strange seekpos() semantics due to joint position + // According to the resolution of DR 171, seekpos should ignore the last + // argument (of type openmode). template typename basic_filebuf<_CharT, _Traits>::pos_type basic_filebuf<_CharT, _Traits>:: - seekpos(pos_type __pos, ios_base::openmode __mode) + seekpos(pos_type __pos, ios_base::openmode) { -#ifdef _GLIBCPP_RESOLVE_LIB_DEFECTS -// 171. Strange seekpos() semantics due to joint position - return this->seekoff(off_type(__pos), ios_base::beg, __mode); -#endif + pos_type __ret = pos_type(off_type(-1)); + if (this->is_open()) + { + // Ditch any pback buffers to avoid confusion. + _M_destroy_pback(); + __ret = _M_seek(off_type(__pos), ios_base::beg, __pos.state()); + } + return __ret; } template - void + typename basic_filebuf<_CharT, _Traits>::pos_type basic_filebuf<_CharT, _Traits>:: - _M_output_unshift() - { } + _M_seek(off_type __off, ios_base::seekdir __way, __state_type __state) + { + pos_type __ret = pos_type(off_type(-1)); + if (_M_terminate_output()) + { + 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 + 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 + bool + basic_filebuf<_CharT, _Traits>:: + _M_terminate_output() + { + // Part one: update the output sequence. + bool __testvalid = true; + if (this->pbase() < this->pptr()) + { + const int_type __tmp = this->overflow(); + if (traits_type::eq_int_type(__tmp, traits_type::eof())) + __testvalid = false; + } + + // Part two: output unshift sequence. + if (_M_writing && !__check_facet(_M_codecvt).always_noconv() + && __testvalid) + { + // Note: this value is arbitrary, since there is no way to + // get the length of the unshift sequence from codecvt, + // without calling unshift. + const size_t __blen = 128; + char __buf[__blen]; + codecvt_base::result __r; + streamsize __ilen = 0; + + do + { + char* __next; + __r = _M_codecvt->unshift(_M_state_cur, __buf, + __buf + __blen, __next); + if (__r == codecvt_base::error) + __testvalid = false; + else if (__r == codecvt_base::ok || + __r == codecvt_base::partial) + { + __ilen = __next - __buf; + if (__ilen > 0) + { + const streamsize __elen = _M_file.xsputn(__buf, __ilen); + if (__elen != __ilen) + __testvalid = false; + } + } + } + while (__r == codecvt_base::partial && __ilen > 0 && __testvalid); + + if (__testvalid) + { + // This second call to overflow() is required by the standard, + // but it's not clear why it's needed, since the output buffer + // should be empty by this point (it should have been emptied + // in the first call to overflow()). + const int_type __tmp = this->overflow(); + if (traits_type::eq_int_type(__tmp, traits_type::eof())) + __testvalid = false; + } + } + return __testvalid; + } + + template + int + basic_filebuf<_CharT, _Traits>:: + sync() + { + // Make sure that the internal buffer resyncs its idea of + // the file position with the external file. + int __ret = 0; + if (this->pbase() < this->pptr()) + { + const int_type __tmp = this->overflow(); + if (traits_type::eq_int_type(__tmp, traits_type::eof())) + __ret = -1; + } + return __ret; + } template void basic_filebuf<_CharT, _Traits>:: imbue(const locale& __loc) { - const bool __testbeg = !this->seekoff(0, ios_base::cur, this->_M_mode); - const bool __teststate = __check_facet(_M_codecvt).encoding() == -1; + bool __testvalid = true; - if (this->_M_buf_locale != __loc - && (!this->is_open() || (__testbeg && !__teststate))) + const __codecvt_type* _M_codecvt_tmp = 0; + if (__builtin_expect(has_facet<__codecvt_type>(__loc), true)) + _M_codecvt_tmp = &use_facet<__codecvt_type>(__loc); + + if (this->is_open()) { - this->_M_buf_locale = __loc; - if (__builtin_expect(has_facet<__codecvt_type>(__loc), true)) - _M_codecvt = &use_facet<__codecvt_type>(__loc); - - // NB This may require the reconversion of previously - // converted chars. This in turn may cause the - // reconstruction of the original file. YIKES!! This - // implementation interprets this requirement as requiring - // the file position be at the beginning, and a stateless - // encoding, or that the filebuf be closed. Opinions may differ. + // encoding() == -1 is ok only at the beginning. + if ((_M_reading || _M_writing) + && __check_facet(_M_codecvt).encoding() == -1) + __testvalid = false; + else + { + if (_M_reading) + { + if (__check_facet(_M_codecvt).always_noconv()) + { + if (_M_codecvt_tmp + && !__check_facet(_M_codecvt_tmp).always_noconv()) + __testvalid = this->seekoff(0, ios_base::cur, _M_mode) + != pos_type(off_type(-1)); + } + else + { + // External position corresponding to gptr(). + _M_ext_next = _M_ext_buf + + _M_codecvt->length(_M_state_last, _M_ext_buf, + _M_ext_next, + this->gptr() - this->eback()); + const streamsize __remainder = _M_ext_end - _M_ext_next; + if (__remainder) + __builtin_memmove(_M_ext_buf, _M_ext_next, __remainder); + + _M_ext_next = _M_ext_buf; + _M_ext_end = _M_ext_buf + __remainder; + _M_set_buffer(-1); + _M_state_last = _M_state_cur = _M_state_beg; + } + } + else if (_M_writing && (__testvalid = _M_terminate_output())) + _M_set_buffer(-1); + } } - _M_last_overflowed = false; + + if (__testvalid) + _M_codecvt = _M_codecvt_tmp; + else + _M_codecvt = 0; } // Inhibit implicit instantiations for required instantiations, - // which are defined via explicit instantiations elsewhere. + // which are defined via explicit instantiations elsewhere. // NB: This syntax is a GNU extension. -#if _GLIBCPP_EXTERN_TEMPLATE +#if _GLIBCXX_EXTERN_TEMPLATE extern template class basic_filebuf; extern template class basic_ifstream; extern template class basic_ofstream; extern template class basic_fstream; -#ifdef _GLIBCPP_USE_WCHAR_T +#ifdef _GLIBCXX_USE_WCHAR_T extern template class basic_filebuf; extern template class basic_ifstream; extern template class basic_ofstream; extern template class basic_fstream; #endif #endif -} // namespace std -#endif +_GLIBCXX_END_NAMESPACE + +#endif