blob: 97a75166ddb1748f4aea24ee76c3d927198e2202 [file] [log] [blame]
/***************************************************************************
*
* strstream.cpp - Source for the Standard Library string stream classes
*
* $Id$
*
***************************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the License); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
* Copyright 1994-2006 Rogue Wave Software.
*
**************************************************************************/
#define _RWSTD_LIB_SRC
#if 3 == __GNUG__ && 3 > __GNUC_MINOR__ \
|| 3 == __GNUC_MINOR__ && 1 >__GNUC_PATCHLEVEL__
// working around a gcc bug (PR #29570)
# include <rw/_config.h>
# ifndef _RWSTD_NO_EXTERN_TEMPLATE
# define _RWSTD_NO_EXTERN_TEMPLATE
# endif
#endif // gcc >= 3.0 && gcc < 3.3.1
#include <rw/_defs.h>
#include <limits.h>
#include <ios>
#include <strstream>
_RWSTD_NAMESPACE (std) {
/* virtual */
strstreambuf::~strstreambuf ()
{
_RWSTD_ASSERT (_C_is_valid ());
if ( _C_buffer && _C_state & _C_allocated
&& !(_C_state & _C_frozen)) {
if (_C_pfree)
_C_pfree (_C_buffer);
else
delete[] _C_buffer;
}
}
/* virtual */ strstreambuf::int_type
strstreambuf::overflow (int_type c)
{
_RWSTD_ASSERT (_C_is_valid ());
if (!_C_is_out () || !(_C_state & _C_dynamic))
return traits_type::eof ();
if (_C_is_eof (c))
return traits_type::not_eof (c);
// reallocate space if necessary
if (!(pptr () < epptr ())) {
const streamsize new_size =
_C_bufsize ? _C_bufsize * 2 : _C_alsize ? _C_alsize : 128;
// take care to avoid calling the overridden setbuf(), if any
if (!strstreambuf::setbuf (0, new_size))
return traits_type::eof ();
}
return sputc (traits_type::to_char_type (c));
}
/* virtual */ strstreambuf::int_type
strstreambuf::pbackfail (int_type c)
{
_RWSTD_ASSERT (_C_is_valid ());
typedef traits_type Traits;
if (eback () < gptr ()) {
// putback position is available (see 27.5.1, p3, bullet 3)
if (Traits::eq_int_type (c, Traits::eof ())) {
// D.7.1.3, p6, bullet 3
gbump (-1);
return Traits::not_eof (c);
}
if (Traits::eq_int_type (Traits::to_int_type (*(gptr () - 1)), c)) {
// D.7.1.3, p6, bullet 1
gbump (-1);
return c;
}
if (!(_C_state & _C_constant)) {
// D.7.1.3, p6, bullet 2
gbump (-1);
Traits::assign (*gptr (), Traits::to_char_type (c));
return c;
}
}
// D.7.1.3, p7
return Traits::eof ();
}
/* virtual */ strstreambuf::int_type
strstreambuf::underflow ()
{
_RWSTD_ASSERT (_C_is_valid ());
if (gptr () < egptr ()) {
// read position is available (see 27.5.1, p3, bullet 4)
// D.7.1.3, p9, bullet 1
return traits_type::to_int_type (*gptr ());
}
if (pptr () > egptr ()) {
// D.7.1.3, p9, bullet 2
if (gptr ())
setg (eback (), gptr (), pptr ());
else
setg (pbase (), pbase (), pptr ());
return traits_type::to_int_type (*gptr ());
}
// D.7.1.3, p10
return traits_type::eof ();
}
/* virtual */ strstreambuf::pos_type
strstreambuf::seekoff (off_type off, ios::seekdir way, ios::openmode which)
{
_RWSTD_ASSERT (_C_is_valid ());
// should implicitly hold as long as ios::seekdir is an enum
_RWSTD_ASSERT (ios::beg == way || ios::cur == way || ios::end == way);
_RWSTD_ASSERT (_C_is_valid ());
// determine seekable area - D.7.1, p4
char* const seeklo = eback ();
char* const seekhi = epptr () ? epptr () : egptr ();
const char* const xnext = which & ios::in ? gptr () : pptr ();
const char* const xbeg = which & ios::in ? eback () : pbase ();
// D.7.1.3, p13
if (!xnext)
return pos_type (off_type (-1));
off_type saved_off = off;
// compute new offset - D.7.1.3, p13, table 105
if (ios::cur == way)
off += off_type (xnext - xbeg);
else if (ios::end == way)
off += off_type (seekhi - xbeg);
// test conditions in D.7.1.3, p13, table 105, row 4
if (off < seeklo - xbeg || off > seekhi - xbeg)
off = -1; // failure
else if (which & ios::in) { // position input sequence
// fail if `way' is `cur', otherwise position output sequence first
if ( which & ios::out
&& ( way == ios::cur
|| pos_type (-1) == seekoff (saved_off, way, ios::out)))
return pos_type (off_type (-1));
// adjust input sequence as necessary to maintain invariant
if (off <= egptr () - eback ())
setg (eback (), eback () + off, egptr ());
else if (off <= pptr () - egptr ()) // advance egptr()
setg (eback (), eback () + off, pptr ());
else // advance egptr() even further
setg (eback (), eback () + off, epptr ());
}
else if (which & ios::out) { // position output sequence
if (seeklo + off < pbase ()) {
// adjust beginning of output sequence, then increment pptr()
setp (seeklo, epptr ());
pbump (off);
}
else {
// reset pptr() first, then increment it by offset
setp (pbase (), epptr ());
pbump (off - (pbase () - seeklo));
}
}
else
off = -1; // failure
_RWSTD_ASSERT (_C_is_valid ());
return pos_type (off);
}
/* virtual */ strstreambuf::pos_type
strstreambuf::seekpos (pos_type sp, ios::openmode which)
{
_RWSTD_ASSERT (_C_is_valid ());
// call seekoff() defined by strstreambuf, and
// not an overridden() virtual if one exists
return strstreambuf::seekoff (sp - pos_type (), ios::beg, which);
}
/* virtual */ streambuf*
strstreambuf::setbuf (char_type* buf, streamsize bufsize)
{
_RWSTD_ASSERT (_C_is_valid ());
if ( !(_C_state & _C_dynamic) || (_C_state & _C_frozen)
|| !buf && !bufsize) {
// lwg issue 66
return 0;
}
// determine the existing (possibly disjoint) sequences
// in the (possibly two distinct) buffer(s) and copy
// them into the new buffer
// [.....] [.....] or [....[.]...]
// ^ ^ ^ ^ ^ ^
// | | | | | |
// | | | +- xend | +- xend, gap_beg, gap_end
// | | +------- gap_end +------------ xbeg
// | +---------- gap_beg
// +---------------- xbeg
const char_type* const xbeg =
eback () && eback () < pbase () ? eback () : pbase ();
const char_type* const xend =
epptr () && epptr () < egptr () ? egptr () : epptr ();
const char_type *gap_beg = pptr () < eback ()
? pptr () : egptr () < pbase () ? egptr () : pbase ();
const char_type *gap_end = pptr () < eback ()
? eback () : egptr () < pbase () ? egptr () : pbase ();
// if gap_end is before gap_beg, set both so as to make the gap zero size
if (gap_end <= gap_beg)
gap_beg = gap_end = xend;
// compute the cumulative size of both sequences minus the gap
const streamsize slen = streamsize ((xend - xbeg) - (gap_end - gap_beg));
if (bufsize < slen || !_C_is_out ())
return 0; // failure
const bool free_old_buf = _C_own_buf ();
if (!buf) {
_TRY {
// allocate a new buffer
buf = _C_palloc ?
_RWSTD_STATIC_CAST (char_type*, _C_palloc (bufsize))
: new char [bufsize];
}
_CATCH (...) {
// catch all exceptions, indicate failure by returning 0
}
if (!buf)
return 0;
_C_own_buf (true);
}
else
_C_own_buf (false);
// copy both sequences from the old buffer to the new buffer
traits_type::copy (buf, xbeg, gap_beg - xbeg);
traits_type::copy (buf + (gap_beg - xbeg), gap_end, xend - gap_end);
if (free_old_buf) {
if (_C_pfree)
_C_pfree (_C_buffer);
else
delete[] _C_buffer;
}
_C_buffer = buf;
_C_bufsize = bufsize;
const _RWSTD_PTRDIFF_T seq_1_size = gap_beg - xbeg;
// restore output sequence
const streamsize pptr_off = streamsize (pptr () - pbase ());
if (pbase () < gap_beg)
setp (_C_buffer, _C_buffer + _C_bufsize);
else
setp (_C_buffer + seq_1_size, _C_buffer + _C_bufsize);
pbump (pptr_off);
// restore input sequence
char_type* const gbeg = _C_buffer + (eback () < gap_beg ? 0 : seq_1_size);
setg (gbeg, gbeg + (gptr () - eback ()), gbeg + (egptr () - eback ()));
return this;
}
void strstreambuf::
_C_init (streamsize alsize,
const void *gnext_v,
streamsize n,
const void *pbeg_v,
void* (*palloc)(_RWSTD_SIZE_T),
void (*pdealloc)(void*),
int state)
{
_C_alsize = alsize;
_C_palloc = palloc;
_C_pfree = pdealloc;
char* const gnext =
_RWSTD_STATIC_CAST (char*, _RWSTD_CONST_CAST (void*, gnext_v));
char* const pbeg =
_RWSTD_STATIC_CAST (char*, _RWSTD_CONST_CAST (void*, pbeg_v));
// D.7.1.1, p4
if (n < 0)
n = _RWSTD_INT_MAX;
else if (!n && gnext)
n = streamsize (traits_type::length (gnext));
_C_buffer = gnext;
_C_state = state | ios_base::in | ios_base::out;
if (pbeg) {
// D.7.1.1, p6
setg (gnext, gnext, pbeg);
setp (pbeg, pbeg + n);
}
else
// D.7.1.1, p5
setg (gnext, gnext, gnext + n);
_RWSTD_ASSERT (_C_is_valid ());
}
} // namespace std