blob: 3659fa1d1290c307fb0f922c03ba8d4320a1f643 [file] [log] [blame]
/**************************************************************************
*
* iostore.cpp - source of the C++ Standard Library
* ios_base storage functions [lib.ios.base.storage]
*
* $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-2008 Rogue Wave Software, Inc.
*
**************************************************************************/
#define _RWSTD_LIB_SRC
#include <string.h> // memcpy(), memset()
#include <rw/_rawiter.h> // get_temporary_buffer()
#include "iosdata.h"
#include <rw/_defs.h>
_RWSTD_NAMESPACE (__rw) {
// will grow an array of POD elements, never shrinks
static void*
__rw_realloc (void *array, size_t size, size_t &n, int inx)
{
if (inx < 0)
return 0; // bad index
if (_RWSTD_STATIC_CAST (size_t, inx) < n)
return array; // no need to reallocate
void *tmp;
_TRY {
// make sure index is dereferencable (cast prevents HP aCC warnings)
tmp = operator new (_RWSTD_STATIC_CAST (size_t, ++inx) * size);
}
_CATCH (...) {
return 0;
}
_RWSTD_ASSERT (0 != tmp);
// copy elements from array to tmp
if (array)
memcpy (tmp, array, n * size);
// zero out newly allocated storage
memset (_RWSTD_STATIC_CAST (char*, tmp) + n * size, 0, (inx - n) * size);
// modify passed-in size
n = _RWSTD_STATIC_CAST (size_t, inx);
operator delete (array);
return tmp;
}
} // namespace __rw
_RWSTD_NAMESPACE (std) {
/* static */ ios_base::_C_usr_data*
ios_base::_C_usr_data::_C_alloc (_C_fire_fun pfire)
{
_TRY {
// rely on zero-initialization of PODs
_C_usr_data* const pdata = new _C_usr_data ();
#ifdef _RWSTD_NO_NEW_THROWS
if (!pdata)
return 0;
#endif // _RWSTD_NO_NEW_THROWS
_RWSTD_ASSERT (0 != pdata);
#ifndef _RWSTD_NO_POD_ZERO_INIT
// assert that the POD ctor above zeroed out all members
_RWSTD_ASSERT (!pdata->_C_tie);
_RWSTD_ASSERT (!pdata->_C_iarray);
_RWSTD_ASSERT (!pdata->_C_parray);
_RWSTD_ASSERT (!pdata->_C_cbarray);
_RWSTD_ASSERT (!pdata->_C_isize);
_RWSTD_ASSERT (!pdata->_C_psize);
_RWSTD_ASSERT (!pdata->_C_cbsize);
#else // if defined (_RWSTD_NO_POD_ZERO_INIT)
memset (pdata, 0, sizeof *pdata);
#endif // _RWSTD_NO_POD_ZERO_INIT
pdata->_C_fire = pfire;
return pdata;
}
_CATCH (...) {
return 0;
}
}
/* static */ void
ios_base::_C_usr_data::_C_dealloc (_C_usr_data *ptr)
{
if (ptr) {
operator delete (ptr->_C_iarray);
operator delete (ptr->_C_parray);
operator delete (ptr->_C_cbarray);
if ( ptr != _C_usr_data::_C_std_usr_data
&& ptr != _C_usr_data::_C_std_usr_data + 1)
delete ptr;
}
}
/* static */ int ios_base::xalloc ()
{
// outlined to hide implementation details
static int inx /* = 0 */;
return _RWSTD_ATOMIC_PREINCREMENT (inx, false) - 1;
}
void ios_base::_C_fire_event (event ev, bool reentrant)
{
if (!_C_usr)
return;
_C_usr_data::_C_event_cb *cba = _C_usr->_C_cbarray;
size_t cbsize = _C_usr->_C_cbsize;
// verify consistency (either both are 0 or neither is)
_RWSTD_ASSERT (!!cba == !!cbsize);
if (!cba)
return;
// caller expected to lock if necessary
_TRY {
// protect `cba' from an exception (copy only necessary
// when `reentrant' is non-zero in which case we unlock
// the mutex and allow registered callbacks to call the
// stream member functions recursively)
if (reentrant) {
// never throws
cba = _GET_TEMP_BUFFER (_C_usr_data::_C_event_cb, cbsize).first;
if (!cba) {
// clear may throw...
_C_set (_C_state | badbit, _C_except | _RW::__rw_nolock,
_C_rdbuf);
return;
}
// verify consistency (neither must be 0 at this point)
_RWSTD_ASSERT (_C_usr->_C_cbarray && _C_usr->_C_cbsize);
// copy into temporary buffer
memcpy (cba, _C_usr->_C_cbarray, cbsize * sizeof *cba);
// unlock only if stream state allows it
if (!(flags () & _RW::__rw_nolock))
_C_unlock ();
}
// 27.4.2.6, p1 - call callbacks in opposite order of registration
for (size_t i = cbsize; i-- > 0; ) {
_RWSTD_ASSERT (cba && cba [i]._C_fn);
// ok if callback calls locking functions, we're unlocked
// and working with a local copy of callack array
cba [i]._C_fn (ev, *this, cba [i]._C_index);
}
if (reentrant) {
return_temporary_buffer (cba);
// lock only if stream state allows it (stream state
// may have been modified by one of the callbacks)
if (!(flags () & _RW::__rw_nolock))
_C_lock ();
}
}
_CATCH (...) {
if (reentrant) {
return_temporary_buffer (cba);
// lock only if `cba' was successfully allocated (or copied)
// and the stream state allows it (stream state may have been
// modified by one of the callbacks)
if (cba && !(flags () & _RW::__rw_nolock))
_C_lock ();
}
_RETHROW;
}
}
// succeeds irrespective of rdstate()
void ios_base::
_C_copyfmt (const ios_base &rhs, void *dst, const void *src, size_t size)
{
if (this == &rhs)
return;
typedef _C_usr_data::_C_event_cb EventCB;
// allocate sufficient storage
long *ia = 0;
void* *pa = 0;
EventCB *cba = 0;
size_t a_size [3]; // sizes of arrays above
unsigned fmtfl; // formatting flags
streamsize prec; // new precision
streamsize wide; // new width
unsigned except; // new exceptions
void* ptie; // tied ostream
locale loc; // new locale
char srcbuf [16]; // buffer to copy `src' to
// flags to copy from `rhs'
const fmtflags flagmask = ~(_RW::__rw_nolock | _RW::__rw_nolockbuf);
_TRY {
// lock `rhs', *this not locked yet
_RWSTD_MT_GUARD (rhs.flags () & _RW::__rw_nolock
? 0 : &_RWSTD_CONST_CAST (ios_base&, rhs)._C_mutex);
if (rhs._C_usr) {
// copy pointer to the tied ostream, if any
ptie = rhs._C_usr->_C_tie;
// for convenience
const _C_usr_data* const rusr = rhs._C_usr;
// verify that either both are 0 or neither is
_RWSTD_ASSERT (!!rusr->_C_iarray == !!rusr->_C_isize);
_RWSTD_ASSERT (!!rusr->_C_parray == !!rusr->_C_psize);
_RWSTD_ASSERT (!!rusr->_C_cbarray == !!rusr->_C_cbsize);
a_size [0] = rusr->_C_isize;
a_size [1] = rusr->_C_psize;
a_size [2] = rusr->_C_cbsize;
ia = _RWSTD_STATIC_CAST (long*,
a_size [0] ? operator new (a_size [0] * sizeof *ia) : 0);
pa = _RWSTD_STATIC_CAST (void**,
a_size [1] ? operator new (a_size [1] * sizeof *pa) : 0);
cba = _RWSTD_STATIC_CAST (EventCB*,
a_size [2] ? operator new (a_size [2] * sizeof *cba) : 0);
// copy rhs into allocated storage and assign (27.4.4.2, p15)
memcpy (ia, rusr->_C_iarray, a_size [0] * sizeof *ia);
memcpy (pa, rusr->_C_parray, a_size [1] * sizeof *pa);
memcpy (cba, rusr->_C_cbarray, a_size [2] * sizeof *cba);
}
else {
// zero out array to prevent (bogus) gcc warnings
// about the variable being used uninitialized
memset (a_size, 0, sizeof a_size);
ptie = 0;
}
// copy the rest of rhs state (save for exceptions)
// while it's locked to guarantee atomicity
fmtfl = rhs._C_fmtfl;
prec = rhs._C_prec;
wide = rhs._C_wide;
except = rhs._C_except;
loc = rhs._C_loc;
// copy additional data (rhs's fill char) to the small
// temporary buffer only if it fits
if (size <= sizeof srcbuf)
src = memcpy (srcbuf, src, size);
}
_CATCH (...) {
// *this is unmodified
operator delete (ia);
operator delete (pa);
operator delete (cba);
_RETHROW;
}
// `rhs' unlocked, lock *this
_RWSTD_MT_GUARD (flags () & _RW::__rw_nolock ? 0 : &_C_mutex);
_TRY {
if (_C_usr) {
// fire erase events (27.4.4.2, p17) - may throw
if (_C_usr->_C_fire)
(this->*_C_usr->_C_fire)(erase_event, true /* reentrant */);
// delete existing arrays, if any; _C_usr will only be deleted
// if `rhs' contains no user data (see below)
operator delete (_C_usr->_C_iarray);
_C_usr->_C_iarray = 0;
_C_usr->_C_isize = 0;
operator delete (_C_usr->_C_parray);
_C_usr->_C_parray = 0;
_C_usr->_C_psize = 0;
operator delete (_C_usr->_C_cbarray);
_C_usr->_C_cbarray = 0;
_C_usr->_C_cbsize = 0;
}
else if (ia || pa || cba || ptie) {
// allocation may throw
_C_usr = new _C_usr_data ();
#ifdef _RWSTD_NO_POD_ZERO_INIT
# ifdef _RWSTD_NO_NEW_THROWS
if (!_C_usr)
_RW::__rw_throw (_RWSTD_ERROR_FIRST + 3 /* BAD_ALLOC */);
# endif // _RWSTD_NO_NEW_THROWS
_RWSTD_ASSERT (0 != _C_usr);
memset (_C_usr, 0, sizeof *_C_usr);
#endif // _RWSTD_NO_POD_ZERO_INIT
}
}
_CATCH (...) {
// *this is unmodified
operator delete (ia);
operator delete (pa);
operator delete (cba);
_RETHROW;
}
if (ia || pa || cba || ptie) {
// assing allocated and copied arrays and their sizes
_C_usr->_C_iarray = ia;
_C_usr->_C_parray = pa;
_C_usr->_C_cbarray = cba;
_C_usr->_C_isize = a_size [0];
_C_usr->_C_psize = a_size [1];
_C_usr->_C_cbsize = a_size [2];
_C_usr->_C_tie = ptie;
_C_usr->_C_fire = &ios_base::_C_fire_event;
}
else {
// `rhs' contains no user data, delete _C_usr
_C_usr_data::_C_dealloc (_C_usr);
_C_usr = 0;
}
// copy all but masked flags(), leave masked flags alone
_C_fmtfl = (fmtfl & flagmask) | (_C_fmtfl & ~flagmask);
_C_prec = prec;
_C_wide = wide;
_C_loc = loc;
// copy additional data (fill character) -- will be atomic
// only if size is sufficiently small; since this is used
// for the fill character represented in derived classes
// as int_type this will be true for all standard iostreams
memcpy (dst, src, size);
if (_C_usr && _C_usr->_C_fire) {
_TRY {
// fire copy events (27.4.4.2, p17), may temporarily unlock
// or throw
(this->*_C_usr->_C_fire)(copyfmt_event, true /* reentrant */);
}
_CATCH (...) {
// must still assign exceptions
_C_except = except;
// just rethrow caught exception (don't call clear())
_RETHROW;
}
}
// assign exceptions last (27.4.4.2, p15, bullet 2)
_C_except = except;
// leave state alone but throw an exception if necessary
_C_set (_C_state, _C_except | _RW::__rw_nolock, _C_rdbuf);
}
long& ios_base::iword (int inx)
{
_RWSTD_MT_GUARD (flags () & _RW::__rw_nolock ? 0 : &_C_mutex);
if (!_C_usr)
_C_usr = _C_usr_data::_C_alloc (&ios_base::_C_fire_event);
long* const ia = _C_usr
? _RWSTD_STATIC_CAST (long*, _RW::__rw_realloc (_C_usr->_C_iarray,
sizeof (long),
_C_usr->_C_isize,
inx))
: 0;
if (ia) {
_RWSTD_ASSERT ( _C_usr->_C_isize
> _RWSTD_STATIC_CAST (size_t, inx));
return (_C_usr->_C_iarray = ia)[inx];
}
_C_set (_C_state | badbit, _C_except | _RW::__rw_nolock, _C_rdbuf);
// returns a reference to a dummy object on failure (27.4.2.5, p3)
static long dummy_iword;
return dummy_iword = 0;
}
void*& ios_base::pword (int inx)
{
_RWSTD_MT_GUARD (flags () & _RW::__rw_nolock ? 0 : &_C_mutex);
if (!_C_usr)
_C_usr = _C_usr_data::_C_alloc (&ios_base::_C_fire_event);
void** const pa = _C_usr
? _RWSTD_STATIC_CAST (void**, _RW::__rw_realloc (_C_usr->_C_parray,
sizeof (void*),
_C_usr->_C_psize,
inx))
: 0;
if (pa) {
_RWSTD_ASSERT ( _C_usr->_C_psize
> _RWSTD_STATIC_CAST (size_t, inx));
return (_C_usr->_C_parray = pa)[inx];
}
_C_set (_C_state | badbit, _C_except | _RW::__rw_nolock, _C_rdbuf);
// returns a reference to a dummy object on failure (27.4.2.5, p3)
static void *dummy_pword;
return dummy_pword = 0;
}
void ios_base::
register_callback (event_callback fun, int inx)
{
_RWSTD_ASSERT (0 != fun);
_RWSTD_MT_GUARD (flags () & _RW::__rw_nolock ? 0 : &_C_mutex);
if (!_C_usr)
_C_usr = _C_usr_data::_C_alloc (&ios_base::_C_fire_event);
typedef _C_usr_data::_C_event_cb EventCB;
size_t sz = 0;
EventCB *cba = 0;
if (_C_usr) {
sz = _C_usr->_C_cbsize;
cba = _RWSTD_STATIC_CAST (EventCB*,
_RW::__rw_realloc (_C_usr->_C_cbarray,
sizeof (EventCB),
_C_usr->_C_cbsize,
int (sz)));
}
if (cba) {
// array must have grown by at least one element
_RWSTD_ASSERT (sz < _C_usr->_C_cbsize);
_C_usr->_C_cbarray = cba;
_C_usr->_C_cbarray [sz]._C_index = inx;
_C_usr->_C_cbarray [sz]._C_fn = fun;
}
else {
// not required by 27.4.2.6
_C_set (_C_state | badbit, _C_except | _RW::__rw_nolock, _C_rdbuf);
}
}
} // namespace std