/*
 * Copyright (c) 1997-1999
 * Silicon Graphics Computer Systems, Inc.
 *
 * Copyright (c) 1999
 * Boris Fomitchev
 *
 * Copyright (c) 2003
 * Francois Dumont
 *
 * This material is provided "as is", with absolutely no warranty expressed
 * or implied. Any use is at your own risk.
 *
 * Permission to use or copy this software for any purpose is hereby granted
 * without fee, provided the above notices are retained on all copies.
 * Permission to modify the code and to distribute modified code is granted,
 * provided the above notices are retained, and a notice that the code was
 * modified is included with the above copyright notice.
 *
 */

#ifndef _STLP_STRING_BASE_H
#define _STLP_STRING_BASE_H

// ------------------------------------------------------------
// Class _String_base.

// _String_base is a helper class that makes it it easier to write an
// exception-safe version of basic_string.  The constructor allocates,
// but does not initialize, a block of memory.  The destructor
// deallocates, but does not destroy elements within, a block of
// memory.  The destructor assumes that _M_start either is null, or else
// points to a block of memory that was allocated using _String_base's
// allocator and whose size is _M_end_of_storage - _M_start_of_storage._M_data.

_STLP_BEGIN_NAMESPACE

_STLP_MOVE_TO_PRIV_NAMESPACE

template <class _Tp, class _Alloc>
class _String_base
{
    typedef _String_base<_Tp, _Alloc> _Self;
  protected:
    _STLP_FORCE_ALLOCATORS(_Tp, _Alloc)
  public:
    //dums: Some compiler(MSVC6) require it to be public not simply protected!
    enum {_DEFAULT_SIZE = 4 * sizeof( void * )};
    //This is needed by the full move framework
    typedef _Alloc allocator_type;
    typedef _STLP_alloc_proxy<_Tp*, _Tp, allocator_type> _AllocProxy;
    typedef size_t size_type;

  private:
#if defined (_STLP_USE_SHORT_STRING_OPTIM)
  union _Buffers
  {
      _Tp*  _M_end_of_storage;
      _Tp   _M_static_buf[_DEFAULT_SIZE];
  } _M_buffers;
#else
    _Tp* _M_end_of_storage;
#endif /* _STLP_USE_SHORT_STRING_OPTIM */

  protected:
#if defined (_STLP_USE_SHORT_STRING_OPTIM)
    bool _M_using_static_buf() const
      { return (_M_start_of_storage._M_data == _M_buffers._M_static_buf); }
    _Tp const* _M_Start() const
      { return _M_start_of_storage._M_data; }
    _Tp* _M_Start()
      { return _M_start_of_storage._M_data; }
    _Tp const* _M_End() const
      { return _M_using_static_buf() ? _M_buffers._M_static_buf + _DEFAULT_SIZE : _M_buffers._M_end_of_storage; }
  _Tp* _M_End()
  { return _M_using_static_buf() ? _M_buffers._M_static_buf + _DEFAULT_SIZE : _M_buffers._M_end_of_storage; }
  size_type _M_capacity() const
  { return _M_using_static_buf() ? _DEFAULT_SIZE : _M_buffers._M_end_of_storage - _M_start_of_storage._M_data; }
  size_type _M_rest() const
  { return  _M_using_static_buf() ? _DEFAULT_SIZE - (_M_finish - _M_buffers._M_static_buf) : _M_buffers._M_end_of_storage - _M_finish; }
#else
    _Tp const* _M_Start() const
      { return _M_start_of_storage._M_data; }
    _Tp* _M_Start()
      { return _M_start_of_storage._M_data; }
    _Tp const* _M_End()
      const { return _M_end_of_storage; }
    _Tp* _M_End()
      { return _M_end_of_storage; }
    size_type _M_capacity() const
      { return _M_end_of_storage - _M_start_of_storage._M_data; }
    size_type _M_rest() const
      { return _M_end_of_storage - _M_finish; }
#endif /* _STLP_USE_SHORT_STRING_OPTIM */

    _Tp* _M_finish;
    _AllocProxy _M_start_of_storage;

    _Tp const* _M_Finish() const
      {return _M_finish;}
    _Tp* _M_Finish()
      {return _M_finish;}

    // Precondition: 0 < __n <= max_size().
    void _M_allocate_block(size_t __n = _DEFAULT_SIZE);
    void _M_deallocate_block()
      {
#if defined (_STLP_USE_SHORT_STRING_OPTIM)
        if (!_M_using_static_buf() && (_M_start_of_storage._M_data != 0)) {
          _M_start_of_storage.deallocate(_M_start_of_storage._M_data, _M_buffers._M_end_of_storage - _M_start_of_storage._M_data);
        }
#else
        if (_M_start_of_storage._M_data != 0) {
          _M_start_of_storage.deallocate(_M_start_of_storage._M_data, _M_end_of_storage - _M_start_of_storage._M_data);
        }
#endif /* _STLP_USE_SHORT_STRING_OPTIM */
      }

    size_t max_size() const
      {
        const size_type __string_max_size = size_type(-1) / sizeof(_Tp);
        typename allocator_type::size_type __alloc_max_size = _M_start_of_storage.max_size();
        return (min)(__alloc_max_size, __string_max_size) - 1;
      }

    _String_base(const allocator_type& __a) :
#if defined (_STLP_USE_SHORT_STRING_OPTIM)
        _M_finish(_M_buffers._M_static_buf), _M_start_of_storage(__a, _M_buffers._M_static_buf)
#else
        _M_end_of_storage(0), _M_finish(0), _M_start_of_storage(__a, (_Tp*)0)
#endif
      { }

    _String_base(const allocator_type& __a, size_t __n) :
#if defined (_STLP_USE_SHORT_STRING_OPTIM)
        _M_finish(_M_buffers._M_static_buf), _M_start_of_storage(__a, _M_buffers._M_static_buf)
#else
        _M_end_of_storage(0), _M_finish(0), _M_start_of_storage(__a, (_Tp*)0)
#endif
      { _M_allocate_block(__n); }

#if !defined (_STLP_NO_MOVE_SEMANTIC)
    _String_base(__move_source<_Self> src) :
#if defined (_STLP_USE_SHORT_STRING_OPTIM)
        _M_finish(_M_buffers._M_static_buf),
        _M_start_of_storage( allocator_type(), _M_buffers._M_static_buf)
#else
        _M_end_of_storage(0),
        _M_finish(0),
        _M_start_of_storage( allocator_type(), (_Tp*)0)
#endif
      { _M_swap(src.get()); }
#endif

    ~_String_base()
      { _M_deallocate_block(); }

    void _M_reset(_Tp *__start, _Tp *__finish, _Tp *__end_of_storage)
      {
#if defined (_STLP_USE_SHORT_STRING_OPTIM)
        _M_buffers._M_end_of_storage = __end_of_storage;
#else
        _M_end_of_storage = __end_of_storage;
#endif
        _M_finish = __finish;
        _M_start_of_storage._M_data = __start;
      }

    void _M_swap( _Self& __s )
      {
#if defined (_STLP_USE_SHORT_STRING_OPTIM)
        // _Tp tmp[_DEFAULT_SIZE];
        // copy( s._M_buffers._M_static_buf, s._M_buffers._M_static_buf + _DEFAULT_SIZE, tmp );
        // copy( _M_buffers._M_static_buf, _M_buffers._M_static_buf + _DEFAULT_SIZE, s._M_buffers._M_static_buf );
        // copy( tmp, tmp + _DEFAULT_SIZE, _M_buffers._M_static_buf );
        // _STLP_STD::swap( _M_finish, s._M_finish );
        // _M_start_of_storage.swap(__s._M_start_of_storage);

        if (_M_using_static_buf()) {
          if (__s._M_using_static_buf()) {
            _STLP_STD::swap(_M_buffers, __s._M_buffers);
            _Tp *__tmp = _M_finish;
            _M_finish = _M_start_of_storage._M_data + (__s._M_finish - __s._M_start_of_storage._M_data);
            __s._M_finish = __s._M_buffers._M_static_buf + (__tmp - _M_start_of_storage._M_data);
            //We need to swap _M_start_of_storage for allocators with state:
            // _M_start_of_storage.swap(__s._M_start_of_storage);
            _STLP_STD::swap( _M_start_of_storage, __s._M_start_of_storage );
            _M_start_of_storage._M_data = _M_buffers._M_static_buf;
            __s._M_start_of_storage._M_data = __s._M_buffers._M_static_buf;
          } else {
            __s._M_swap(*this);
            return;
          }
        } else if (__s._M_using_static_buf()) {
          _Tp *__tmp = _M_start_of_storage._M_data;
          _Tp *__tmp_finish = _M_finish;
          _Tp *__tmp_end_data = _M_buffers._M_end_of_storage;
          _M_buffers = __s._M_buffers;
          //We need to swap _M_start_of_storage for allocators with state:
          // _M_start_of_storage.swap(__s._M_start_of_storage);
          _STLP_STD::swap( _M_start_of_storage, __s._M_start_of_storage );
          _M_start_of_storage._M_data = _M_buffers._M_static_buf;
          _M_finish = _M_buffers._M_static_buf + (__s._M_finish - __s._M_buffers._M_static_buf);
          __s._M_buffers._M_end_of_storage = __tmp_end_data;
          __s._M_start_of_storage._M_data = __tmp;
          __s._M_finish = __tmp_finish;
        } else {
          _STLP_STD::swap(_M_buffers._M_end_of_storage, __s._M_buffers._M_end_of_storage);
          // _M_start_of_storage.swap(__s._M_start_of_storage);
          _STLP_STD::swap( _M_start_of_storage, __s._M_start_of_storage );
          _STLP_STD::swap(_M_finish, __s._M_finish);
        }
#else
        _STLP_STD::swap(_M_end_of_storage, __s._M_end_of_storage);
        _M_start_of_storage.swap(__s._M_start_of_storage);
        _STLP_STD::swap(_M_finish, __s._M_finish);
#endif
      }

    void _STLP_FUNCTION_THROWS _M_throw_length_error() const;
    void _STLP_FUNCTION_THROWS _M_throw_out_of_range() const;
};

#if defined (_STLP_USE_TEMPLATE_EXPORT)
_STLP_EXPORT_TEMPLATE_CLASS _String_base<char, allocator<char> >;
#  if defined (_STLP_HAS_WCHAR_T)
_STLP_EXPORT_TEMPLATE_CLASS _String_base<wchar_t, allocator<wchar_t> >;
#  endif
#endif /* _STLP_USE_TEMPLATE_EXPORT */

_STLP_MOVE_TO_STD_NAMESPACE

_STLP_END_NAMESPACE

#endif /* _STLP_STRING_BASE_H */

/*
 * Local Variables:
 * mode:C++
 * End:
 */
