/***************************************************************************
 *
 * _strref.h - Declarations for the Standard Library string_ref classes
 *
 * This is an internal header file used to implement the C++ Standard
 * Library. It should never be #included directly by a program.
 *
 * $Id: _strref.h 585996 2007-10-18 14:59:39Z sebor $
 *
 ***************************************************************************
 *
 * 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-2007 Rogue Wave Software, Inc.
 * 
 **************************************************************************/

#ifndef _RWSTD_RW_STRREF_H_INCLUDED
#define _RWSTD_RW_STRREF_H_INCLUDED

#ifndef _RWSTD_RW_ALLOCATOR_H_INCLUDED
#  include <rw/_allocator.h>
#endif   // _RWSTD_RW_ALLOCATOR_H_INCLUDED

#ifndef _RWSTD_RW_MUTEX_H_INCLUDED
#  include <rw/_mutex.h>
#endif   // _RWSTD_RW_MUTEX_H_INCLUDED

#ifndef _RWSTD_RW_SPECIALIZED_H_INCLUDED
#  include <rw/_specialized.h>
#endif   // _RWSTD_RW_SPECIALIZED_H_INCLUDED

#ifndef _RWSTD_RW_TRAITS_H_INCLUDED
#  include <rw/_traits.h>
#endif   // _RWSTD_RW_TRAITS_H_INCLUDED


#if !defined(_RWSTD_STRING_REF_OFFSET)
#  define _RWSTD_STRING_REF_OFFSET 1
#endif

#if !defined(_RWSTD_STRING_REF_INT)
#  define _RWSTD_STRING_REF_INT long
#endif

#if !defined (_RWSTD_NO_ATOMIC_OPS) && !defined (_RWSTD_NO_STRING_ATOMIC_OPS)
   // disable string mutex when atomic operations are available
#  ifndef _RWSTD_NO_STRING_MUTEX
#    define _RWSTD_NO_STRING_MUTEX
#  endif   // _RWSTD_NO_STRING_MUTEX
#endif   // _RWSTD_NO_ATOMIC_OPS && !_RWSTD_NO_STRING_ATOMIC_OPS


_RWSTD_NAMESPACE (std) { 


// chooses either a single global mutex or a mutex per string object
// or no mutex at all when atomic test-and-set instruction is available
#if !defined (_RWSTD_REENTRANT) || defined (_RWSTD_NO_STRING_MUTEX) 
#  define _RWSTD_STRING_MUTEX(ignore)   false
#elif defined (_RWSTD_ONE_STRING_MUTEX)
#  define _RWSTD_STRING_MUTEX(ignore)   __rw_string_mutex
#else
#  define _RWSTD_STRING_MUTEX(pstr)     pstr->_C_mutex
#endif


template <class _CharT,
          class _Traits = char_traits<_CharT>, 
          class _Allocator = allocator<_CharT> >
class basic_string;


}   // namespace std


_RWSTD_NAMESPACE (__rw) { 


#if    defined (_RWSTD_REENTRANT)        \
    && defined (_RWSTD_ONE_STRING_MUTEX) \
    && !defined (_RWSTD_NO_STRING_MUTEX)

extern  __rw_mutex _RWSTD_EXPORT __rw_string_mutex;

#endif   // _REENTRANT && _ONE_STRING_MUTEX && !NO_STRING_MUTEX


template <class _CharT, class _Traits , class _Allocator>
struct __string_ref
{
    typedef _CharT                                            char_type;
    typedef _Allocator                                        allocator_type;
    typedef _TYPENAME allocator_type::size_type               size_type;
    typedef _STD::basic_string<_CharT, _Traits, _Allocator>   string_type;

#if     defined (_RWSTD_REENTRANT)              \
    && !defined (_RWSTD_ONE_STRING_MUTEX)       \
    && !defined (_RWSTD_NO_STRING_MUTEX)        \
    && !defined (_RWSTD_NO_STATIC_MUTEX_INIT)

    void _C_init (size_type __cap, size_type __size) {

        _C_cap          = __cap;
        _C_size._C_size = __size;

#  ifndef _RWSTD_NO_STRING_REF_COUNT

        _C_refs = 1 - _RWSTD_STRING_REF_OFFSET;

        if (0 != _RWSTD_MUTEX_INIT (this->_C_mutex._C_mutex))
            _RW::__rw_throw (_RWSTD_ERROR_RUNTIME_ERROR,
                             "synchronization error");

#  endif   // _RWSTD_NO_STRING_REF_COUNT

    }

    void _C_destroy () {
#  ifndef _RWSTD_NO_STRING_REF_COUNT
        _RWSTD_MUTEX_DESTROY (this->_C_mutex._C_mutex);
#  endif
    }

#else

    void _C_init (size_type __cap, size_type __size) {

#  ifndef _RWSTD_NO_STRING_REF_COUNT
        _C_refs = 1 - _RWSTD_STRING_REF_OFFSET;
#  endif   // _RWSTD_NO_STRING_REF_COUNT

        _C_cap          = __cap;
        _C_size._C_size = __size;
    }

    void _C_destroy () { }

#endif // _RWSTD_REENTRANT && !_RWSTD_ONE_STRING_MUTEX && 
       // !_RWSTD_NO_STRING_MUTEX && !_RWSTD_NO_STATIC_MUTEX_INIT


    int _C_get_ref () const {

#  ifndef _RWSTD_NO_STRING_REF_COUNT

        return _C_refs + _RWSTD_STRING_REF_OFFSET;

#else   // if defined (_RWSTD_NO_STRING_REF_COUNT)

        return 0;

#endif   // _RWSTD_NO_STRING_REF_COUNT

    }

    void _C_unref () {

#ifndef _RWSTD_NO_STRING_REF_COUNT

        // not thread-safe (see caller)
        if (this != string_type::_C_nullref ())
            _C_refs = -_RWSTD_STRING_REF_OFFSET;

#endif   // _RWSTD_NO_STRING_REF_COUNT
    }

    int _C_inc_ref () {

#ifndef _RWSTD_NO_STRING_REF_COUNT

        return this == string_type::_C_nullref () ? 1 :
            _RWSTD_STRING_ATOMIC_PREINCREMENT (_C_refs,
                _RWSTD_STRING_MUTEX (this));
#else   // if defined (_RWSTD_NO_STRING_REF_COUNT)
        return 0 + (this != string_type::_C_nullref ());
#endif   // _RWSTD_NO_STRING_REF_COUNT

    }

    int _C_dec_ref () {

#ifndef _RWSTD_NO_STRING_REF_COUNT

        return this == string_type::_C_nullref () ? 1 :
              _RWSTD_STRING_REF_OFFSET
            + _RWSTD_STRING_ATOMIC_PREDECREMENT (_C_refs,
                  _RWSTD_STRING_MUTEX (this));

#else   // if defined (_RWSTD_NO_STRING_REF_COUNT)
        return 0 + (this == string_type::_C_nullref ());
#endif   // _RWSTD_NO_STRING_REF_COUNT

    }

    size_type size () const {
        return _C_size._C_size;
    }

    size_type capacity () const {
        return _C_cap;
    }

    char_type* data () {
        return _RWSTD_REINTERPRET_CAST (char_type*, this + 1);
    }

    const char_type* data () const {
        return _RWSTD_REINTERPRET_CAST (const char_type*, this + 1);
    }

#ifndef _RWSTD_NO_STRING_REF_COUNT

#  if     defined (_RWSTD_REENTRANT)            \
      && !defined (_RWSTD_ONE_STRING_MUTEX)     \
      && !defined (_RWSTD_NO_STRING_MUTEX)

    __rw_mutex_base _C_mutex;

#  endif   // _REENTRANT && !_ONE_STRING_MUTEX && !_NO_STRING_MUTEX

    // reference count:
    // 1 less than the number of references to the string body
    // _RWSTD_STRING_REF_OFFSET less if _RWSTD_NO_STRING_MUTEX is defined
    // -1 if reference counting is disabled
    int _C_refs;

#endif   // _RWSTD_NO_STRING_REF_COUNT

    size_type _C_cap;       // Size of allocated memory

    union {
        size_type _C_size;    // Number of actual data values stored
        _CharT    _C_dummy;   // force the alignment of the first char
    } _C_size;                // named to work around an HP aCC 3.30 bug
};


// representation of the null string; will be a POD wherever possible
// (will not be POD if the reference contains a mutex with a UD-ctor)
template <class _CharT, class _Traits , class _Allocator>
struct __null_ref
    : __string_ref<_CharT, _Traits , _Allocator>
{
    // string reference immediately followed by a single terminating null
    _CharT _C_eos;
};


#ifdef _RWSTD_NO_COLLAPSE_TEMPLATE_STATICS

extern _RWSTD_EXPORT unsigned long __nullref [];

#endif   // _RWSTD_NO_COLLAPSE_TEMPLATE_STATICS

}   // namespace __rw


#endif   // _RWSTD_RW_STRREF_H_INCLUDED
