// scoped_allocator standard header -*-c++-*-
// Copyright 2014-2017 IAR Systems AB.
#ifndef _SCOPED_ALLOCATOR_
#define _SCOPED_ALLOCATOR_

#ifndef _SYSTEM_BUILD
  #pragma system_include
#endif

#include <tuple>
#include <xmemory0>

namespace std {

template<class _Outer,
         class... _Inner>
class scoped_allocator_adaptor;

// TEMPLATE STRUCT _Scoped_propagate_on_container_copy

template<class _Outer,
         class... _Inner>
struct _Scoped_propagate_on_container_copy
{       // determines scoped_allocator_adaptor<_Outer, _Inner...>
  // ::propagate_on_container_copy_assignment
  typedef typename _If<allocator_traits<_Outer>
                         ::propagate_on_container_copy_assignment::value,
                       true_type,
                       _Scoped_propagate_on_container_copy<_Inner...> >::type
  ::type type;
};

template<class _Outer>
struct _Scoped_propagate_on_container_copy<_Outer>
{       // determines scoped_allocator_adaptor<_Outer, _Inner...>
  // ::propagate_on_container_copy_assignment
  typedef typename allocator_traits<_Outer>
    ::propagate_on_container_copy_assignment::type type;
};

// TEMPLATE STRUCT _Scoped_propagate_on_container_move

template<class _Outer,
         class... _Inner>
struct _Scoped_propagate_on_container_move
{       // determines scoped_allocator_adaptor<_Outer, _Inner...>
  // ::propagate_on_container_move_assignment
  typedef typename _If<allocator_traits<_Outer>
                         ::propagate_on_container_move_assignment::value,
                       true_type,
                       _Scoped_propagate_on_container_move<_Inner...> >::type
  ::type type;
};

template<class _Outer>
struct _Scoped_propagate_on_container_move<_Outer>
{       // determines scoped_allocator_adaptor<_Outer, _Inner...>
  // ::propagate_on_container_move_assignment
  typedef typename allocator_traits<_Outer>
    ::propagate_on_container_move_assignment::type type;
};

// TEMPLATE STRUCT _Scoped_propagate_on_container_swap

template<class _Outer,
         class... _Inner>
struct _Scoped_propagate_on_container_swap
{       // determines scoped_allocator_adaptor<_Outer, _Inner...>
  // ::propagate_on_container_swap
  typedef typename _If<allocator_traits<_Outer>
                         ::propagate_on_container_swap::value,
                       true_type,
                       _Scoped_propagate_on_container_swap<_Inner...> >::type
  ::type type;
};

template<class _Outer>
struct _Scoped_propagate_on_container_swap<_Outer>
{       // determines scoped_allocator_adaptor<_Outer, _Inner...>
  // ::propagate_on_container_swap
  typedef typename allocator_traits<_Outer>
    ::propagate_on_container_swap::type type;
};

// TEMPLATE STRUCT _Scoped_is_always_equal
template<class _Outer,
         class... _Inner>
struct _Scoped_is_always_equal
{ // determines scoped_allocator_adaptor<_Outer, _Inner...>
  // ::is_always_equal
  typedef typename _If<!allocator_traits<_Outer>::is_always_equal::value,
                       false_type,
                       _Scoped_is_always_equal<_Inner...> >::type::type type;
};

template<class _Outer>
struct _Scoped_is_always_equal<_Outer>
{ // determines scoped_allocator_adaptor<_Outer, _Inner...>
  // ::is_always_equal
  typedef typename allocator_traits<_Outer>::is_always_equal::type type;
};

// TEMPLATE STRUCT _Scoped_outermost_allocator
template<class _Outer>
struct _Scoped_outermost_allocator
{       // chooses outermost x.outer_allocator() or x
  template<class _Xouter>
  static auto _Fn(int, _Xouter& _Alval)
    -> decltype(_Alval.outer_allocator())
  {       // returned nested allocator argument
    return _Alval.outer_allocator();
  }

  template<class _Xouter>
  static auto _Fn(_Wrap_int, _Xouter& _Alval)
    -> _Xouter&
  {       // returned allocator argument
    return _Alval;
  }

  static _Outer _Outer_obj;

  typedef typename remove_reference<
    decltype(_Fn<_Outer>(0, _Outer_obj))>::type type;
};

// TEMPLATE FUNCTION _Scoped_construct
template<class _Outermost,
         class _Inner,
         class _Ty,
         class... _Types,
         class _Dont_care>
void _Scoped_construct(_Outermost, _Inner,
                       false_type, _Dont_care,
                       _Ty *_Ptr, _Types&&... _Args)
{       // construct with optional arguments, no allocator
  ::new (static_cast<void *>(_Ptr)) _Ty(std::forward<_Types>(_Args)...);
}

template<class _Outermost,
         class _Inner,
         class _Ty,
         class... _Types>
void _Scoped_construct(_Outermost _Alval, _Inner _Inner_alval,
                       true_type, false_type,
                       _Ty *_Ptr, _Types&&... _Args)
{       // construct with optional arguments, leading allocator
  allocator_traits<_Outermost>::construct(_Alval, _Ptr,
                                          allocator_arg, _Inner_alval,
                                          std::forward<_Types>(_Args)...);
}

template<class _Outermost,
         class _Inner,
         class _Ty,
         class... _Types>
void _Scoped_construct(_Outermost _Alval, _Inner _Inner_alval,
                       true_type, true_type,
                       _Ty *_Ptr, _Types&&... _Args)
{       // construct with optional arguments, trailing allocator
  allocator_traits<_Outermost>::construct(_Alval, _Ptr,
                                          std::forward<_Types>(_Args)...,
                                          _Inner_alval);
}

// TEMPLATE FUNCTION _Scoped_construct_tuple
template<class _Alloc,
         class _Ty,
         class... _Atypes,
         class _Dont_care>
tuple<_Atypes...>
_Scoped_construct_tuple(_Alloc&, false_type, _Dont_care,
                        tuple<_Atypes...> _Val)
{       // construct tuple from tuple argument, no allocator
  return _Val;
}

template<class _Alloc,
         class _Ty,
         class... _Atypes>
tuple<allocator_arg_t, _Alloc&, _Atypes...>
_Scoped_construct_tuple(_Alloc& _Alty, true_type, false_type,
                        tuple<_Atypes...> _Val)
{       // construct tuple from tuple argument, leading allocator
  return tuple_cat(tuple<allocator_arg_t, _Alloc&>(_Tuple_alloc, _Alty), _Val);
}

template<class _Alloc,
         class _Ty,
         class... _Atypes>
tuple<_Atypes..., _Alloc&>
_Scoped_construct_tuple(_Alloc& _Alty, true_type, true_type,
                        tuple<_Atypes...> _Val)
{       // construct tuple from tuple argument, trailing allocator
  return tuple_cat(_Val, tuple<_Alloc&>(_Alty));
}

// TEMPLATE FUNCTION _Scoped_construct_pair
template<class _Inner,
         class _Ty1,
         class _Ty2,
         class... _Uy1,
         class... _Uy2>
void _Scoped_construct_pair(_Inner _Inner_alval,
                            pair<_Ty1, _Ty2> *_Ptr,
                            tuple<_Uy1...> _Val1, tuple<_Uy2...> _Val2)
{       // construct pair from tuples
  ::new (static_cast<void *>(_Ptr)) pair<_Ty1, _Ty2>(
    piecewise_construct,
    _Scoped_construct_tuple<_Inner, _Ty1, _Uy1...>(
      _Inner_alval,
      uses_allocator<_Ty1, _Inner>(),
      is_constructible<_Ty1, _Uy1..., _Inner>(),
      _Val1),
    _Scoped_construct_tuple<_Inner, _Ty2, _Uy2...>(
      _Inner_alval,
      uses_allocator<_Ty2, _Inner>(),
      is_constructible<_Ty2, _Uy2..., _Inner>(),
      _Val2));
}

// TEMPLATE CLASS _Scoped_base
template<class _Outer,
         class... _Inner>
struct _Scoped_base;

template<class _Outer,
         class _Inner0,
         class... _Inner>
struct _Scoped_base<_Outer, _Inner0, _Inner...>
  : public _Outer
{       // nest of allocators, arbitrary depth
  typedef scoped_allocator_adaptor<_Outer, _Inner0, _Inner...> _Myadaptor;
  typedef scoped_allocator_adaptor<_Inner0, _Inner...> inner_allocator_type;

  inner_allocator_type _Inner_obj;

  inner_allocator_type& _Get_inner_object(
    _Myadaptor&)
  {       // return _Inner_obj as inner_object
    return _Inner_obj;
  }

  const inner_allocator_type& _Get_inner_object(const _Myadaptor&) const
  {       // return _Inner_obj as inner_object
    return _Inner_obj;
  }

  _Scoped_base()
  {       // default construct
  }

  template<class _Other1,
           class _Other2>
  _Scoped_base(_Other1&& _Outer_arg,
               _Other2&& _Inner_arg)
    : _Inner_obj(std::forward<_Other2>(_Inner_arg))
  {       // construct from (outer, inner)
    (_Outer&)*this = std::forward<_Other1>(_Outer_arg);
  }

  template<class _Other>
  void operator=(scoped_allocator_adaptor<_Other, _Inner0, _Inner...>&&
                 _Right)
  {       // move from adaptor<_Other, _Inner0, _Inner...>
    (_Outer&)*this = std::forward<_Other>(_Right);
    _Inner_obj = std::forward<inner_allocator_type>(_Right._Inner_obj);
  }

  template<class _Other>
  void operator=(const scoped_allocator_adaptor<_Other, _Inner0, _Inner...>&
                 _Right)
  {       // assign from adaptor<_Other, _Inner0, _Inner...>
    (_Outer&)*this = (_Other&)_Right;
    _Inner_obj = _Right._Inner_obj;
  }

  _Myadaptor select_on_container_copy_construction() const
  {       // make new adaptor
    return _Myadaptor(allocator_traits<_Outer>
                        ::select_on_container_copy_construction(*this),
                      _Inner_obj.select_on_container_copy_construction());
  }
};

template<class _Outer>
struct _Scoped_base<_Outer>
  : public _Outer
{       // nest of allocators, one deep
  typedef scoped_allocator_adaptor<_Outer> _Myadaptor;
  typedef scoped_allocator_adaptor<_Outer> inner_allocator_type;

  inner_allocator_type& _Get_inner_object(_Myadaptor& _Self)
  {       // return self as inner_object
    return _Self;
  }

  const inner_allocator_type& _Get_inner_object(const _Myadaptor& _Self) const
  {       // return self as inner_object
    return _Self;
  }

  _Scoped_base()
  {       // default construct
  }

  template<class _Other1,
           class _Other2>
  _Scoped_base(_Other1&& _Outer_arg, _Other2&&)
    : _Outer(std::forward<_Other1>(_Outer_arg))
  {       // construct from (outer, inner)
  }

  template<class _Other>
  void operator=(scoped_allocator_adaptor<_Other>&& _Right)
  {       // move from adaptor<_Other>
    (_Outer&)*this = std::forward<_Other>((_Other&)_Right);
  }

  template<class _Other>
  void operator=(const scoped_allocator_adaptor<_Other>& _Right)
  {       // assign from adaptor<_Other>
    (_Outer&)*this = (_Other&)_Right;
  }

  _Myadaptor select_on_container_copy_construction() const
  {       // make new adaptor
    return _Myadaptor(allocator_traits<_Outer>
                        ::select_on_container_copy_construction(*this));
  }
};

// TEMPLATE CLASS scoped_allocator_adaptor
template<class _Outer,
         class... _Inner>
class scoped_allocator_adaptor
  : public _Scoped_base<_Outer, _Inner...>
{       // nest of allocators
  typedef _Scoped_base<_Outer, _Inner...> _Mybase;
  typedef allocator_traits<_Outer> _Outer_traits;
  typedef typename _Scoped_outermost_allocator<_Outer>::type _Outermost;
  typedef allocator_traits<_Outermost> _Outermost_traits;

public:
  typedef _Outer outer_allocator_type;
  typedef typename _Mybase::inner_allocator_type inner_allocator_type;

  typedef typename _Outer_traits::value_type value_type;
  typedef typename _Outer_traits::pointer pointer;
  typedef typename _Outer_traits::const_pointer const_pointer;
  typedef typename _Outer_traits::void_pointer void_pointer;
  typedef typename _Outer_traits::const_void_pointer const_void_pointer;

  typedef typename _Outer_traits::size_type size_type;
  typedef typename _Outer_traits::difference_type difference_type;

  template<class _Other>
  struct rebind
  {       // converts X<value_type> to X<_Other>
    typedef typename _Get_allocator_rebind_type<_Outer, _Other>::type _Other_alloc;
    typedef scoped_allocator_adaptor<_Other_alloc, _Inner...> other;
  };

  typedef typename
    _Scoped_propagate_on_container_copy<_Outermost, _Inner...>::type
    propagate_on_container_copy_assignment;
  typedef typename
    _Scoped_propagate_on_container_move<_Outermost, _Inner...>::type
    propagate_on_container_move_assignment;
  typedef typename
    _Scoped_propagate_on_container_swap<_Outermost, _Inner...>::type
    propagate_on_container_swap;

  typedef typename _Scoped_is_always_equal<_Outermost, _Inner...>::type
    is_always_equal;

  outer_allocator_type& outer_allocator() _NOEXCEPT
  {       // get reference to outer allocator
    return static_cast<_Outer&>(*this);
  }

  const outer_allocator_type& outer_allocator() const _NOEXCEPT
  {       // get reference to outer allocator
    return static_cast<const _Outer&>(*this);
  }

  inner_allocator_type& inner_allocator() _NOEXCEPT
  {       // get reference to inner allocator
    return const_cast<inner_allocator_type&>(this->_Get_inner_object(*this));
  }

  const inner_allocator_type& inner_allocator() const _NOEXCEPT
  {       // get reference to inner allocator
    return this->_Get_inner_object(*this);
  }

  scoped_allocator_adaptor()
  {       // default construct
  }

  scoped_allocator_adaptor(const scoped_allocator_adaptor& _Right) _NOEXCEPT
  : _Mybase(_Right.outer_allocator(), _Right.inner_allocator())
  {       // construct from _Right
  }

  template<class _Other>
  scoped_allocator_adaptor(_Other&& _Other_arg,
                           const _Inner&... _Inner_arg) _NOEXCEPT
    : _Mybase(std::forward<_Other>(_Other_arg),
             std::forward<inner_allocator_type>(inner_allocator_type(_Inner_arg...)))
  {       // construct from (_Other, _Inner...)
  }

  template<class _Other>
  scoped_allocator_adaptor(const scoped_allocator_adaptor<_Other,
                           _Inner...>& _Right) _NOEXCEPT
    : _Mybase(_Right.outer_allocator(), _Right.inner_allocator())
  {       // construct from const adaptor<_Other, _Inner...>&
  }

  template<class _Other>
  scoped_allocator_adaptor(scoped_allocator_adaptor<_Other,
                           _Inner...>&& _Right) _NOEXCEPT
    : _Mybase(std::forward<_Other>(_Right.outer_allocator()),
              _Right.inner_allocator())
  {       // construct from adaptor<_Other, _Inner...>&&
  }

  template<class _Other>
  scoped_allocator_adaptor& operator=(
    scoped_allocator_adaptor<_Other, _Inner...>&& _Right)
  {       // assign from adaptor<_Other, _Inner...>&&
    _Mybase::operator=(std::forward<_Other>(_Right));
    return *this;
  }

  template<class _Other>
  scoped_allocator_adaptor& operator=(
    const scoped_allocator_adaptor<_Other, _Inner...>& _Right)
  {       // assign from adaptor<_Other, _Inner...>&
    _Mybase::operator=(_Right);
    return *this;
  }

  pointer allocate(size_type _Count)
  {       // allocate array of _Count elements, ignore hint
    return _Outer_traits::allocate(outer_allocator(), _Count);
  }

  pointer allocate(size_type _Count, const_void_pointer _Hint)
  {       // allocate array of _Count elements, with hint
    return _Outer_traits::allocate(outer_allocator(), _Count, _Hint);
  }

  void deallocate(pointer _Ptr, size_type _Count)
  {       // deallocate object at _Ptr, with size
    return _Outer_traits::deallocate(outer_allocator(), _Ptr, _Count);
  }

  size_type max_size() const
  {       // estimate maximum array size
    return _Outer_traits::max_size(outer_allocator());
  }

  template<class _Ty,
           class... _Types>
  void construct(_Ty *_Ptr, _Types&&... _Args)
  {       // construct with varying allocator styles
    _Scoped_construct(_Scoped_outermost_allocator<_Outer>::_Fn(0, *this),
                      inner_allocator(),
                      uses_allocator<_Ty, inner_allocator_type>(),
                      is_constructible<_Ty, _Types..., inner_allocator_type>(),
                      _Ptr, std::forward<_Types>(_Args)...);
  }

  template<class _Ty>
  void destroy(_Ty *_Ptr)
  {       // destroy object at _Ptr
    _Outermost _Alval(_Scoped_outermost_allocator<_Outer>::_Fn(0, *this));
    _Outermost_traits::destroy(_Alval, _Ptr);
  }

  // CONSTRUCT A PAIR
  template<class _Ty1,
           class _Ty2,
           class _Tpl1,
           class _Tpl2>
  void construct(pair<_Ty1, _Ty2> *_Ptr, piecewise_construct_t,
                 _Tpl1 _Val1, _Tpl2 _Val2)
  {       // construct pair from tuples
    _Scoped_construct_pair(inner_allocator(), _Ptr, _Val1, _Val2);
  }

  template<class _Ty1,
           class _Ty2>
  void construct(pair<_Ty1, _Ty2> *_Ptr)
  {       // construct pair from pointer to other pair
    this->construct(_Ptr, piecewise_construct, tuple<>(), tuple<>());
  }

  template<class _Ty1,
           class _Ty2,
           class _Uy1,
           class _Uy2>
  void construct(pair<_Ty1, _Ty2> *_Ptr,
                 _Uy1&& _Val1, _Uy2&& _Val2)
  {       // construct pair from two movable values
    this->construct(_Ptr, piecewise_construct,
                    forward_as_tuple(std::forward<_Uy1>(_Val1)),
                    forward_as_tuple(std::forward<_Uy2>(_Val2)));
  }

  template<class _Ty1,
           class _Ty2,
           class _Uy1,
           class _Uy2>
  void construct(pair<_Ty1, _Ty2> *_Ptr,
                 const pair<_Uy1, _Uy2>& _Other)
  {       // construct pair from copyable pair
    this->construct(_Ptr, piecewise_construct,
                    forward_as_tuple(_Other.first),
                    forward_as_tuple(_Other.second));
  }

  template<class _Ty1,
           class _Ty2,
           class _Uy1,
           class _Uy2>
  void construct(pair<_Ty1, _Ty2> *_Ptr,
                 pair<_Uy1, _Uy2>&& _Other)
  {       // construct pair from movable pair
    this->construct(_Ptr, piecewise_construct,
                    forward_as_tuple(std::forward<_Uy1>(_Other.first)),
                    forward_as_tuple(std::forward<_Uy2>(_Other.second)));
  }
};

template<class _Outer1,
         class _Outer2,
         class _Inner1,
         class... _Inner> inline
bool operator==(
  const scoped_allocator_adaptor<_Outer1, _Inner1, _Inner...>& _Left,
  const scoped_allocator_adaptor<_Outer2, _Inner1, _Inner...>& _Right)
  _NOEXCEPT
{       // compare scoped_allocator_adaptors for equality
  return    _Left.outer_allocator() == _Right.outer_allocator()
         && _Left.inner_allocator() == _Right.inner_allocator();
}

template<class _Outer1,
         class _Outer2> inline
bool operator==(const scoped_allocator_adaptor<_Outer1>& _Left,
                const scoped_allocator_adaptor<_Outer2>& _Right) _NOEXCEPT
{       // compare scoped_allocator_adaptors for equality
  return _Left.outer_allocator() == _Right.outer_allocator();
}

template<class _Outer1,
         class _Outer2,
         class... _Inner> inline
bool operator!=(const scoped_allocator_adaptor<_Outer1, _Inner...>& _Left,
                const scoped_allocator_adaptor<_Outer2, _Inner...>& _Right)
  _NOEXCEPT
{       // compare scoped_allocator_adaptors for equality
  return !(_Left == _Right);
}

} /* namespace std */

#endif /* _SCOPED_ALLOCATOR_ */

/*
 * Copyright (c) by P.J. Plauger. All rights reserved.
 * Consult your license regarding permissions and restrictions.
V6.50:0576 */
