// xpolymorphic_allocator.h internal header
// Copyright (c) Microsoft Corporation. All rights reserved.
#pragma once
#ifndef _XPOLYMORPHIC_ALLOCATOR_H
#define _XPOLYMORPHIC_ALLOCATOR_H
#ifndef RC_INVOKED
#include <tuple>
#include <type_traits>
#include <utility>
#include <xmemory0>

 #pragma pack(push,_CRT_PACKING)
 #pragma warning(push,_STL_WARNING_LEVEL)
 #pragma warning(disable: _STL_DISABLED_WARNINGS)
 _STL_DISABLE_CLANG_WARNINGS
 #pragma push_macro("new")
 #undef new

_STD_BEGIN

		// FUNCTION TEMPLATE _Uses_allocator_construct
template<class _Ty,
	class _Outer_alloc,
	class _Inner_alloc,
	class... _Types> inline
	void _Uses_allocator_construct2(
		true_type, _Ty * const _Ptr, _Outer_alloc& _Outer, _Inner_alloc& _Inner, _Types&&... _Args)
	{	// uses-allocator construction of *_Ptr by alloc _Outer propagating alloc _Inner, allocator_arg_t case
	allocator_traits<_Outer_alloc>::construct(
		_Outer, _Ptr, allocator_arg, _Inner, _STD forward<_Types>(_Args)...);
	}

template<class _Ty,
	class _Outer_alloc,
	class _Inner_alloc,
	class... _Types> inline
	void _Uses_allocator_construct2(
		false_type, _Ty * const _Ptr, _Outer_alloc& _Outer, _Inner_alloc& _Inner, _Types&&... _Args)
	{	// uses-allocator construction of *_Ptr by alloc _Outer propagating alloc _Inner, non-allocator_arg_t case
	static_assert(is_constructible_v<_Ty, _Types..., _Inner_alloc&>,
		"N4700 23.10.7.2 [allocator.uses.construction]/1 requires "
		"is_constructible_v<T, Args..., Alloc&> when uses_allocator_v<T, Alloc> is true and "
		"is_constructible_v<T, allocator_arg_t, Alloc&, Args...> is false");
	allocator_traits<_Outer_alloc>::construct(_Outer, _Ptr, _STD forward<_Types>(_Args)..., _Inner);
	}

template<class _Ty,
	class _Outer_alloc,
	class _Inner_alloc,
	class... _Types> inline
	void _Uses_allocator_construct1(
		true_type, _Ty * const _Ptr, _Outer_alloc& _Outer, _Inner_alloc& _Inner, _Types&&... _Args)
	{	// uses-allocator construction of *_Ptr by alloc _Outer propagating alloc _Inner,
		//   uses_allocator_v<_Ty, _Inner_alloc> case
	using _IsConstructible = typename is_constructible<_Ty, allocator_arg_t, _Inner_alloc&, _Types...>::type;
	_Uses_allocator_construct2(_IsConstructible{}, _Ptr, _Outer, _Inner, _STD forward<_Types>(_Args)...);
	}

template<class _Ty,
	class _Outer_alloc,
	class _Inner_alloc,
	class... _Types> inline
	void _Uses_allocator_construct1(
		false_type, _Ty * const _Ptr, _Outer_alloc& _Outer, _Inner_alloc&, _Types&&... _Args)
	{	// uses-allocator construction of *_Ptr by alloc _Outer,
		//   !uses_allocator_v<_Ty, _Inner_alloc> case
	static_assert(is_constructible_v<_Ty, _Types...>,
		"N4700 23.10.7.2 [allocator.uses.construction]/1 requires "
		"is_constructible_v<T, Args...> when uses_allocator_v<T, Alloc> is false");
	allocator_traits<_Outer_alloc>::construct(_Outer, _Ptr, _STD forward<_Types>(_Args)...);
	}

template<class _Ty,
	class _Outer_alloc,
	class _Inner_alloc,
	class... _Types,
	enable_if_t<!_Is_specialization<_Ty, pair>::value, int> = 0> inline
	void _Uses_allocator_construct(
		_Ty * const _Ptr, _Outer_alloc& _Outer, _Inner_alloc& _Inner, _Types&&... _Args)
	{	// uses-allocator construction of *_Ptr by alloc _Outer propagating alloc _Inner, non-pair case
	_Uses_allocator_construct1(uses_allocator<_Ty, _Inner_alloc>(), _Ptr, _Outer,
		_Inner, _STD forward<_Types>(_Args)...); // TRANSITION, if constexpr
	}

template<class _Alloc,
	class... _Types> inline
	auto _Uses_allocator_piecewise2(true_type, _Alloc& _Al, tuple<_Types...>&& _Tuple)
	{
	return (_STD tuple_cat(tuple<allocator_arg_t, _Alloc&>(allocator_arg, _Al), _STD move(_Tuple)));
	}

template<class _Alloc,
	class... _Types> inline
	auto _Uses_allocator_piecewise2(false_type, _Alloc& _Al, tuple<_Types...>&& _Tuple)
	{
	return (_STD tuple_cat(_STD move(_Tuple), tuple<_Alloc&>(_Al)));
	}

template<class _Ty,
	class _Alloc,
	class... _Types> inline
	auto _Uses_allocator_piecewise(true_type, _Alloc& _Al, tuple<_Types...>&& _Tuple)
	{
	return (_Uses_allocator_piecewise2(
		is_constructible<_Ty, allocator_arg_t, _Alloc&, _Types...>(), _Al, _STD move(_Tuple)));
	}

template<class,
	class _Alloc,
	class... _Types> inline
	tuple<_Types...>&& _Uses_allocator_piecewise(false_type, _Alloc&, tuple<_Types...>&& _Tuple)
	{
	return (_STD move(_Tuple));
	}

template<class _Ty1,
	class _Ty2,
	class _Outer_alloc,
	class _Inner_alloc,
	class... _Types1,
	class... _Types2> inline
	void _Uses_allocator_construct_pair(pair<_Ty1, _Ty2> * const _Ptr, _Outer_alloc& _Outer, _Inner_alloc& _Inner,
		tuple<_Types1...>&& _Val1, tuple<_Types2...>&& _Val2)
	{	// uses-allocator construction of pair from _Val1 and _Val2 by alloc _Outer propagating alloc _Inner
	allocator_traits<_Outer_alloc>::construct(_Outer, _Ptr, piecewise_construct,
		_Uses_allocator_piecewise<_Ty1>(uses_allocator<_Ty1, _Inner_alloc>(), _Inner, _STD move(_Val1)),
		_Uses_allocator_piecewise<_Ty2>(uses_allocator<_Ty2, _Inner_alloc>(), _Inner, _STD move(_Val2)));
	}

template<class _Ty1,
	class _Ty2,
	class _Outer_alloc,
	class _Inner_alloc,
	class... _Types1,
	class... _Types2> inline
	void _Uses_allocator_construct(pair<_Ty1, _Ty2> * const _Ptr, _Outer_alloc& _Outer, _Inner_alloc& _Inner,
		piecewise_construct_t, tuple<_Types1...> _Val1, tuple<_Types2...> _Val2)
	{	// uses-allocator construction of pair by alloc _Outer propagating alloc _Inner, piecewise case
	_Uses_allocator_construct_pair(_Ptr, _Outer, _Inner, _STD move(_Val1), _STD move(_Val2));
	}

template<class _Ty1,
	class _Ty2,
	class _Outer_alloc,
	class _Inner_alloc> inline
	void _Uses_allocator_construct(pair<_Ty1, _Ty2> * const _Ptr, _Outer_alloc& _Outer, _Inner_alloc& _Inner)
	{	// uses-allocator construction of pair by alloc _Outer propagating alloc _Inner, zero-argument case
	_Uses_allocator_construct_pair(_Ptr, _Outer, _Inner, tuple<>{}, tuple<>{});
	}

template<class _Ty1,
	class _Ty2,
	class _Outer_alloc,
	class _Inner_alloc,
	class _Uty,
	class _Vty> inline
	void _Uses_allocator_construct(pair<_Ty1, _Ty2> * const _Ptr, _Outer_alloc& _Outer, _Inner_alloc& _Inner,
		_Uty&& _Arg1, _Vty&& _Arg2)
	{	// uses-allocator construction of pair by alloc _Outer propagating alloc _Inner, two-argument case
	_Uses_allocator_construct_pair(_Ptr, _Outer, _Inner, _STD forward_as_tuple(_STD forward<_Uty>(_Arg1)),
		_STD forward_as_tuple(_STD forward<_Vty>(_Arg2)));
	}

template<class _Ty1,
	class _Ty2,
	class _Outer_alloc,
	class _Inner_alloc,
	class _Uty,
	class _Vty> inline
	void _Uses_allocator_construct(pair<_Ty1, _Ty2> * const _Ptr, _Outer_alloc& _Outer, _Inner_alloc& _Inner,
		const pair<_Uty, _Vty>& _Pair)
	{	// uses-allocator construction of pair by alloc _Outer propagating alloc _Inner, lvalue pair argument
	_Uses_allocator_construct_pair(_Ptr, _Outer, _Inner, _STD forward_as_tuple(_Pair.first),
		_STD forward_as_tuple(_Pair.second));
	}

template<class _Ty1,
	class _Ty2,
	class _Outer_alloc,
	class _Inner_alloc,
	class _Uty,
	class _Vty> inline
	void _Uses_allocator_construct(pair<_Ty1, _Ty2> * const _Ptr, _Outer_alloc& _Outer, _Inner_alloc& _Inner,
		pair<_Uty, _Vty>&& _Pair)
	{	// uses-allocator construction of pair by alloc _Outer propagating alloc _Inner, rvalue pair argument
	_Uses_allocator_construct_pair(_Ptr, _Outer, _Inner, _STD forward_as_tuple(_STD forward<_Uty>(_Pair.first)),
		_STD forward_as_tuple(_STD forward<_Vty>(_Pair.second)));
	}

 #if _HAS_CXX17
namespace pmr {

		// CLASS memory_resource
class __declspec(novtable) memory_resource
	{
public:
	virtual ~memory_resource() _NOEXCEPT
		{}

	_NODISCARD _DECLSPEC_ALLOCATOR void * allocate(_CRT_GUARDOVERFLOW const size_t _Bytes,
		const size_t _Align = alignof(max_align_t))
		{	// allocate _Bytes bytes of memory with alignment _Align
		if (!_Is_pow_2(_Align))
			{
			_DEBUG_ERROR("memory_resource::allocate(): Alignment must be a power of two.");
			}

		return (do_allocate(_Bytes, _Align));
		}

	void deallocate(void * const _Ptr, const size_t _Bytes, const size_t _Align = alignof(max_align_t))
		{	// deallocate _Ptr, which was returned from allocate(_Bytes, _Align)
		if (!_Is_pow_2(_Align))
			{
			_DEBUG_ERROR("memory_resource::deallocate(): Alignment must be a power of two.");
			}

		return (do_deallocate(_Ptr, _Bytes, _Align));
		}

	_NODISCARD bool is_equal(const memory_resource& _That) const _NOEXCEPT
		{	// determine if *this and _That can both deallocate memory allocated by either
		return (do_is_equal(_That));
		}

private:
	virtual void * do_allocate(size_t _Bytes, size_t _Align) = 0;
	virtual void do_deallocate(void * _Ptr, size_t _Bytes, size_t _Align) = 0;
	virtual bool do_is_equal(const memory_resource& _That) const _NOEXCEPT = 0;
	};

_NODISCARD inline bool operator==(const memory_resource& _Left, const memory_resource& _Right) _NOEXCEPT
	{
	return (&_Left == &_Right || _Left.is_equal(_Right));
	}

_NODISCARD inline bool operator!=(const memory_resource& _Left, const memory_resource& _Right) _NOEXCEPT
	{
	return (!(_Left == _Right));
	}

		// FUNCTION get_default_resource
extern "C" _CRTIMP3 memory_resource * __cdecl _Aligned_get_default_resource() _NOEXCEPT;
extern "C" _CRTIMP3 memory_resource * __cdecl _Unaligned_get_default_resource() _NOEXCEPT;

_NODISCARD inline memory_resource * get_default_resource() _NOEXCEPT
	{
#if _HAS_ALIGNED_NEW
	return (_Aligned_get_default_resource());
#else /* ^^^ _HAS_ALIGNED_NEW / !_HAS_ALIGNED_NEW vvv */
	return (_Unaligned_get_default_resource());
#endif /* _HAS_ALIGNED_NEW */
	}

		// CLASS TEMPLATE polymorphic_allocator
template<class _Ty>
	class polymorphic_allocator
	{
public:
	template<class>
		friend class polymorphic_allocator;

	using value_type = _Ty;

	polymorphic_allocator() _NOEXCEPT = default;

	/* implicit */ polymorphic_allocator(memory_resource * const _Resource_) _NOEXCEPT // strengthened
		: _Resource{_Resource_}
		{	// initialize with _Resource_
		if (_Resource_ == nullptr)
			{
			_DEBUG_ERROR(
				"Cannot initialize polymorphic_allocator with null resource "
				"(N4700 23.12.3.1 [mem.poly.allocator.ctor]/2)");
			}
		}

	polymorphic_allocator(const polymorphic_allocator&) = default;

	template<class _Uty>
		polymorphic_allocator(const polymorphic_allocator<_Uty>& _That) _NOEXCEPT
		: _Resource{_That._Resource}
		{	// initialize with _That's resource
		}

	polymorphic_allocator& operator=(const polymorphic_allocator&) = delete;

	_NODISCARD _DECLSPEC_ALLOCATOR _Ty * allocate(_CRT_GUARDOVERFLOW const size_t _Count)
		{	// get space for _Count objects of type _Ty from _Resource
		void * const _Vp = _Resource->allocate(_Get_size_of_n<sizeof(_Ty)>(_Count), alignof(_Ty));
		return (static_cast<_Ty *>(_Vp));
		}

	void deallocate(_Ty * const _Ptr, const size_t _Count) _NOEXCEPT // strengthened
		{	// return space for _Count objects of type _Ty to _Resource
		_Resource->deallocate(_Ptr, _Get_size_of_n<sizeof(_Ty)>(_Count), alignof(_Ty));
		}

	template<class _Uty,
		class... _Types>
		void construct(_Uty * const _Ptr, _Types&&... _Args)
		{	// propagate allocator *this if uses_allocator_v<_Uty, polymorphic_allocator>
		allocator<char> _Al{};
		_Uses_allocator_construct(_Ptr, _Al, *this, _STD forward<_Types>(_Args)...);
		}

	_NODISCARD polymorphic_allocator select_on_container_copy_construction() const _NOEXCEPT // strengthened
		{	// don't propagate on copy
		return {};
		}

	_NODISCARD memory_resource * resource() const _NOEXCEPT // strengthened
		{	// retrieve this allocator's memory_resource
		return (_Resource);
		}

private:
	memory_resource * _Resource = _STD pmr::get_default_resource();
	};

template<class _Ty1,
	class _Ty2>
	_NODISCARD inline bool operator==(
		const polymorphic_allocator<_Ty1>& _Left,
		const polymorphic_allocator<_Ty2>& _Right) _NOEXCEPT
	{	// polymorphic_allocators with the same resource are compatible
	return (*_Left.resource() == *_Right.resource());
	}

template<class _Ty1,
	class _Ty2>
	_NODISCARD inline bool operator!=(
		const polymorphic_allocator<_Ty1>& _Left,
		const polymorphic_allocator<_Ty2>& _Right) _NOEXCEPT
	{
	return (!(_Left == _Right));
	}

} // namespace pmr

 #endif /* _HAS_CXX17 */

_STD_END

 #pragma pop_macro("new")
 _STL_RESTORE_CLANG_WARNINGS
 #pragma warning(pop)
 #pragma pack(pop)
#endif /* RC_INVOKED */
#endif /* _XPOLYMORPHIC_ALLOCATOR_H */
