| /************************************************************************** |
| * |
| * 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 |