// memory_resource standard header
// Copyright (c) Microsoft Corporation. All rights reserved.
#pragma once
#ifndef _MEMORY_RESOURCE_
#define _MEMORY_RESOURCE_
#ifndef RC_INVOKED
#include <yvals.h>

#if _HAS_CXX17
#include <vector>
#include <xbit_ops.h>
#include <xpolymorphic_allocator.h>
#include <xutility>

 #ifndef _M_CEE
  #include <mutex>
 #endif /* _M_CEE */

 #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

namespace pmr {

		// FUNCTION set_default_resource
extern "C" _CRTIMP3 memory_resource * __cdecl _Aligned_set_default_resource(memory_resource *) noexcept;
extern "C" _CRTIMP3 memory_resource * __cdecl _Unaligned_set_default_resource(memory_resource *) noexcept;

inline memory_resource * set_default_resource(memory_resource * const _Resource) noexcept
	{
#if _HAS_ALIGNED_NEW
	return (_Aligned_set_default_resource(_Resource));
#else /* ^^^ _HAS_ALIGNED_NEW / !_HAS_ALIGNED_NEW vvv */
	return (_Unaligned_set_default_resource(_Resource));
#endif /* _HAS_ALIGNED_NEW */
	}

extern "C" _CRTIMP3 _NODISCARD memory_resource * __cdecl null_memory_resource() noexcept;

		// FUNCTION new_delete_resource
class _Identity_equal_resource
	: public memory_resource
	{
protected:
	virtual bool do_is_equal(const memory_resource& _That) const noexcept override
		{
		return (this == &_That);
		}
	};

class _Unaligned_new_delete_resource_impl final
	: public _Identity_equal_resource
	{	// implementation for new_delete_resource with /Zc:alignedNew-
	virtual void * do_allocate(const size_t _Bytes, const size_t _Align) override
		{
		if (_Align > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
			{
			_Xbad_alloc();
			}

		return (::operator new(_Bytes));
		}

	virtual void do_deallocate(
		void * const _Ptr, const size_t _Bytes, size_t) noexcept override // strengthened
		{
		::operator delete(_Ptr, _Bytes);
		}
	};

extern "C" _CRTIMP3 _Unaligned_new_delete_resource_impl * __cdecl _Unaligned_new_delete_resource() noexcept;

#if _HAS_ALIGNED_NEW
class _Aligned_new_delete_resource_impl final
	: public _Identity_equal_resource
	{	// implementation for new_delete_resource with aligned new support
	virtual void * do_allocate(const size_t _Bytes, const size_t _Align) override
		{
		if (_Align > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
			{
			return (::operator new(_Bytes, align_val_t{_Align}));
			}

		return (::operator new(_Bytes));
		}

	virtual void do_deallocate(
		void * const _Ptr, const size_t _Bytes, const size_t _Align) noexcept override // strengthened
		{
		if (_Align > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
			{
			return (::operator delete(_Ptr, _Bytes, align_val_t{_Align}));
			}

		::operator delete(_Ptr, _Bytes);
		}
	};

extern "C" _CRTIMP3 _Aligned_new_delete_resource_impl * __cdecl _Aligned_new_delete_resource() noexcept;

_NODISCARD inline memory_resource * new_delete_resource() noexcept
	{
	return (_Aligned_new_delete_resource());
	}

#else /* ^^^ _HAS_ALIGNED_NEW / !_HAS_ALIGNED_NEW vvv */

_NODISCARD inline memory_resource * new_delete_resource() noexcept
	{
	return (_Unaligned_new_delete_resource());
	}
#endif /* _HAS_ALIGNED_NEW */

		// CLASS unsynchronized_pool_resource
template<class _Tag = void>
	struct _Double_link
	{	// base class for intrusive doubly-linked structures
	_Double_link * _Next;
	_Double_link * _Prev;
	};

template<class _Ty,
	class _Tag = void>
	struct _Intrusive_list
	{	// intrusive circular list of _Ty (which must derive from _Double_link<_Tag>)
	using _Link_type = _Double_link<_Tag>;

	constexpr _Intrusive_list() noexcept // TRANSITION, VSO#517878
		{	// initialize this list to the empty state
		}

	_Intrusive_list(const _Intrusive_list&) = delete;
	_Intrusive_list& operator=(const _Intrusive_list&) = delete;

	static constexpr _Link_type * _As_link(_Ty * const _Ptr) noexcept
		{	// extract the link from the item denoted by _Ptr
		static_assert(is_base_of_v<_Link_type, _Ty>);
		return (static_cast<_Link_type *>(_Ptr));
		}

	static constexpr _Ty * _As_item(_Link_type * const _Ptr) noexcept
		{	// get the item whose link is denoted by _Ptr
		static_assert(is_base_of_v<_Link_type, _Ty>);
		return (static_cast<_Ty *>(_Ptr));
		}

	constexpr void _Push_front(_Ty * const _Item) noexcept
		{	// insert _Item at the head of this list
		static_assert(is_base_of_v<_Link_type, _Ty>);
		const auto _Ptr = static_cast<_Link_type *>(_Item);
		_Ptr->_Next = _Head._Next;
		_Head._Next->_Prev = _Ptr;
		_Ptr->_Prev = &_Head;
		_Head._Next = _Ptr;
		}

	static constexpr void _Remove(_Ty * const _Item) noexcept
		{	// unlink _Item from this list
		static_assert(is_base_of_v<_Link_type, _Ty>);
		const auto _Ptr = static_cast<_Link_type *>(_Item);
		_Ptr->_Next->_Prev = _Ptr->_Prev;
		_Ptr->_Prev->_Next = _Ptr->_Next;
		}

	constexpr void _Clear() noexcept
		{	// make this list empty
		_Head._Next = &_Head;
		_Head._Prev = &_Head;
		}

	_Link_type _Head{&_Head, &_Head};
	};

template<class _Tag = void>
	struct _Single_link
	{	// base class for intrusive singly-linked structures
	_Single_link * _Next;
	};

template<class _Ty,
	class _Tag = void>
	struct _Intrusive_stack
	{	// intrusive stack of _Ty, which must derive from _Single_link<_Tag>
	using _Link_type = _Single_link<_Tag>;

	constexpr _Intrusive_stack() noexcept = default;
	constexpr _Intrusive_stack(_Intrusive_stack&& _That) noexcept
		: _Head{_That._Head}
		{
		_That._Head = nullptr;
		}
	constexpr _Intrusive_stack& operator=(_Intrusive_stack&& _That) noexcept
		{
		_Head = _That._Head;
		_That._Head = nullptr;
		return (*this);
		}

	static constexpr _Link_type * _As_link(_Ty * const _Ptr) noexcept
		{
		static_assert(is_base_of_v<_Link_type, _Ty>);
		return (static_cast<_Link_type *>(_Ptr));
		}

	static constexpr _Ty * _As_item(_Link_type * const _Ptr) noexcept
		{
		static_assert(is_base_of_v<_Link_type, _Ty>);
		return (static_cast<_Ty *>(_Ptr));
		}

	constexpr bool _Empty() const noexcept
		{
		return (_Head == nullptr);
		}

	constexpr _Ty * _Top() const noexcept
		{
		return (_As_item(_Head));
		}

	constexpr void _Push(_Ty * const _Item) noexcept
		{
		const auto _Ptr = _As_link(_Item);
		_Ptr->_Next = _Head;
		_Head = _Ptr;
		}

	constexpr _Ty * _Pop() noexcept
		{	// pre: _Head != nullptr
		const auto _Result = _Head;
		_Head = _Head->_Next;
		return (_As_item(_Result));
		}

	constexpr void _Remove(_Ty * const _Item) noexcept
		{
		const auto _Ptr = _As_link(_Item);
		for (_Link_type ** _Foo = _STD addressof(_Head); *_Foo != nullptr; _Foo = _STD addressof((*_Foo)->_Next))
			{
			if (*_Foo == _Ptr)
				{
				*_Foo = _Ptr->_Next;
				break;
				}
			}
		}

	_Link_type * _Head = nullptr;
	};

struct pool_options
	{
	size_t max_blocks_per_chunk = 0;
	size_t largest_required_pool_block = 0;
	};

inline void _Check_alignment(void * const _Ptr, const size_t _Align) noexcept
	{
	_STL_ASSERT((reinterpret_cast<uintptr_t>(_Ptr) & (_Align - 1)) == 0,
		"Upstream resource did not respect alignment requirement.");
	(void)_Ptr;
	(void)_Align;
	}

struct unsynchronized_pool_resource
	: _Identity_equal_resource
	{
	unsynchronized_pool_resource() noexcept
		{	// initialize pool with default options and default upstream
		_Setup_options();
		}
	unsynchronized_pool_resource(const pool_options& _Opts, memory_resource * const _Resource) noexcept
		: _Options(_Opts), _Pools{_Resource}
		{	// initialize pool with options _Opts and upstream _Resource
		_STL_ASSERT(_Resource != nullptr, "Upstream memory resource must be a valid resource "
			"(N4700 23.12.5.3 [mem.res.pool.ctor]/1)");
		_Setup_options();
		}
	explicit unsynchronized_pool_resource(memory_resource * const _Resource) noexcept
		: _Pools{_Resource}
		{	// initialize pool with default options and upstream _Resource
		_STL_ASSERT(_Resource != nullptr, "Upstream memory resource must be a valid resource "
			"(N4700 23.12.5.3 [mem.res.pool.ctor]/1)");
		_Setup_options();
		}
	explicit unsynchronized_pool_resource(const pool_options& _Opts) noexcept
		: _Options(_Opts)
		{	// initialize pool with options _Opts and default upstream
		_Setup_options();
		}

	unsynchronized_pool_resource(const unsynchronized_pool_resource&) = delete;
	unsynchronized_pool_resource& operator=(const unsynchronized_pool_resource&) = delete;

	virtual ~unsynchronized_pool_resource() noexcept override
		{	// destroy this pool resource, releasing all allocations back upstream
		release();
		}

	_NODISCARD memory_resource * upstream_resource() const noexcept // strengthened
		{	// retrieve this pool resource's upstream resource
		return (_Pools.get_allocator().resource());
		}

	_NODISCARD pool_options options() const noexcept // strengthened
		{	// retrieve the adjusted/actual option values
		return (_Options);
		}

	void release() noexcept // strengthened
		{	// release all allocations back upstream
		for (auto& _Al : _Pools)
			{
			_Al._Clear(*this);
			}
		_Pools.clear();
		_Pools.shrink_to_fit();

		auto * _Ptr = _Chunks._Head._Next;
		_Chunks._Clear();
		memory_resource * const _Resource = upstream_resource();
		while (_Ptr != &_Chunks._Head)
			{
			const auto _Chunk = _Chunks._As_item(_Ptr);
			_Ptr = _Ptr->_Next;
			_Resource->deallocate(_Chunk->_Base_address(), _Chunk->_Size, _Chunk->_Align);
			}
		}

protected:
	virtual void * do_allocate(size_t _Bytes, const size_t _Align) override
		{	// allocate a block from the appropriate pool, or directly from upstream if too large
		if (_Bytes <= _Options.largest_required_pool_block)
			{
			auto _Result = _Find_pool(_Bytes, _Align);
			if (_Result.first == _Pools.end() || _Result.first->_Log_of_size != _Result.second)
				{
				_Result.first = _Pools.emplace(_Result.first, _Result.second);
				}

			return (_Result.first->_Allocate(*this));
			}

		return (_Allocate_oversized(_Bytes, _Align));
		}

	virtual void do_deallocate(void * const _Ptr, const size_t _Bytes, const size_t _Align) override
		{	// deallocate a block from the appropriate pool, or directly from upstream if too large
		if (_Bytes <= _Options.largest_required_pool_block)
			{
			const auto _Result = _Find_pool(_Bytes, _Align);
			if (_Result.first != _Pools.end() && _Result.first->_Log_of_size == _Result.second)
				{
				_Result.first->_Deallocate(*this, _Ptr);
				}
			}
		else
			{
			_Deallocate_oversized(_Ptr, _Bytes, _Align);
			}
		}

private:
	struct _Oversized_header
		: _Double_link<>
		{	// tracks an allocation that was obtained directly from the upstream resource
		size_t _Size;
		size_t _Align;

		void * _Base_address() const
			{	// headers are stored at the end of the allocated memory block
			return (const_cast<char *>(reinterpret_cast<const char *>(this + 1) - _Size));
			}
		};

	static_assert(alignof(_Oversized_header) == alignof(void *));
	static_assert(sizeof(_Oversized_header) == 4 * sizeof(void *));

	static constexpr bool _Prepare_oversized(size_t& _Bytes, size_t& _Align) noexcept
		{	// adjust size and alignment to allow for an _Oversized_header
		_Align = _Max_value(_Align, alignof(_Oversized_header));

		if (_Bytes > SIZE_MAX - sizeof(_Oversized_header) - alignof(_Oversized_header) + 1)
			{	// no room for header + alignment padding
			return (false);
			}

		// adjust _Bytes to the smallest multiple of alignof(_Oversized_header) that is >=
		// _Bytes + sizeof(_Oversized_header), which guarantees that the end of the allocated space
		// is properly aligned for an _Oversized_header.
		_Bytes = (_Bytes + sizeof(_Oversized_header) + alignof(_Oversized_header) - 1)
			& ~(alignof(_Oversized_header) - 1);

		return (true);
		}

	void * _Allocate_oversized(size_t _Bytes, size_t _Align)
		{	// allocate a block directly from the upstream resource
		if (!_Prepare_oversized(_Bytes, _Align))
			{	// no room for header + alignment padding
			_Xbad_alloc();
			}

		memory_resource * const _Resource = upstream_resource();
		void * const _Ptr = _Resource->allocate(_Bytes, _Align);
		_Check_alignment(_Ptr, _Align);

		_Oversized_header * const _Hdr = reinterpret_cast<_Oversized_header *>(
			reinterpret_cast<char *>(_Ptr) + _Bytes) - 1;

		_Hdr->_Size = _Bytes;
		_Hdr->_Align = _Align;
		_Chunks._Push_front(_Hdr);

		return (_Ptr);
		}

	void _Deallocate_oversized(void * _Ptr, size_t _Bytes, size_t _Align) noexcept
		{	// deallocate a block directly from the upstream resource
		if (!_Prepare_oversized(_Bytes, _Align))
			{	// no room for header + alignment padding; this memory WAS NOT allocated by this pool resource
#ifdef _DEBUG
			_STL_REPORT_ERROR("Cannot deallocate memory not allocated by this memory pool.");
#endif /* _DEBUG */
			}

		_Oversized_header * _Hdr = reinterpret_cast<_Oversized_header *>(
			reinterpret_cast<char *>(_Ptr) + _Bytes) - 1;

		_STL_ASSERT(_Hdr->_Size == _Bytes && _Hdr->_Align == _Align,
			"Cannot deallocate memory not allocated by this memory pool.");
		_Chunks._Remove(_Hdr);
		upstream_resource()->deallocate(_Ptr, _Bytes, _Align);
		}

	struct _Pool
		{	// manager for a collection of chunks comprised of blocks of a single size
		struct _Chunk
			: _Single_link<>
			{	// a memory allocation consisting of a number of fixed-size blocks to be parceled out
			_Intrusive_stack<_Single_link<>> _Free_blocks{}; // list of free blocks
			size_t _Free_count;                              // # of unallocated blocks
			size_t _Capacity;                                // total # of blocks
			char * _Base;                                    // address of first block
			size_t _Next_available = 0;                      // index of first never-allocated block
			size_t _Id;                                      // unique identifier; increasing order of allocation

			_Chunk(_Pool& _Al, void * const _Base_, const size_t _Capacity_) noexcept
				: _Free_count{_Capacity_}, _Capacity{_Capacity_},
					_Base{static_cast<char *>(_Base_)},
					_Id{_Al._All_chunks._Empty() ? 0 : _Al._All_chunks._Top()->_Id + 1}
				{	// initialize a chunk of _Capacity blocks, all initially free
				}

			_Chunk(const _Chunk&) = delete;
			_Chunk& operator=(const _Chunk&) = delete;
			};

		_Chunk * _Unfull_chunk = nullptr;				// largest _Chunk with free blocks
		_Intrusive_stack<_Chunk> _All_chunks{};			// all chunks (ordered by decreasing _Id)
		size_t _Next_capacity = _Default_next_capacity;	// # of blocks to allocate in next _Chunk
														// in (1, (PTRDIFF_MAX - sizeof(_Chunk)) >> _Log_of_size]
		size_t _Block_size;								// size of allocated blocks
		size_t _Log_of_size;							// _Block_size == 1 << _Log_of_size
		_Chunk * _Empty_chunk = nullptr;				// only _Chunk with all free blocks

		static constexpr size_t _Default_next_capacity = 4;
		static_assert(_Default_next_capacity > 1);

		explicit _Pool(const size_t _Log_of_size_) noexcept
			: _Block_size{size_t{1} << _Log_of_size_}, _Log_of_size{_Log_of_size_}
			{	// initialize a pool that manages blocks of the indicated size
			}

		_Pool(_Pool&& _That) noexcept
			: _Unfull_chunk{_STD exchange(_That._Unfull_chunk, nullptr)},
				_All_chunks{_STD move(_That._All_chunks)},
				_Next_capacity{_STD exchange(_That._Next_capacity, _Default_next_capacity)},
				_Block_size{_That._Block_size},
				_Log_of_size{_That._Log_of_size},
				_Empty_chunk{_STD exchange(_That._Empty_chunk, nullptr)}
			{}

		_Pool& operator=(_Pool&& _That) noexcept
			{
			_Unfull_chunk = _STD exchange(_That._Unfull_chunk, nullptr);
			_All_chunks = _STD move(_That._All_chunks);
			_Next_capacity = _STD exchange(_That._Next_capacity, _Default_next_capacity);
			_Block_size = _That._Block_size;
			_Log_of_size = _That._Log_of_size;
			_Empty_chunk = _STD exchange(_That._Empty_chunk, nullptr);
			return (*this);
			}

		void _Clear(unsynchronized_pool_resource& _Pool_resource) noexcept
			{	// release all chunks in the pool back upstream
			_Intrusive_stack<_Chunk> _Tmp{};
			_STD swap(_Tmp, _All_chunks);
			memory_resource * const _Resource = _Pool_resource.upstream_resource();
			while (!_Tmp._Empty())
				{
				const auto _Ptr = _Tmp._Pop();
				_Resource->deallocate(_Ptr->_Base, _Size_for_capacity(_Ptr->_Capacity), _Block_size);
				}

			_Unfull_chunk = nullptr;
			_Next_capacity = _Default_next_capacity;
			_Empty_chunk = nullptr;
			}

		void * _Allocate(unsynchronized_pool_resource& _Pool_resource)
			{	// allocate a block from this pool
			for (;; _Unfull_chunk = _All_chunks._As_item(_Unfull_chunk->_Next))
				{
				if (!_Unfull_chunk)
					{
					_Increase_capacity(_Pool_resource);
					}
				else if (!_Unfull_chunk->_Free_blocks._Empty())
					{
					if (_Unfull_chunk == _Empty_chunk)
						{	// this chunk is no longer empty
						_Empty_chunk = nullptr;
						}
					--_Unfull_chunk->_Free_count;
					return (_Unfull_chunk->_Free_blocks._Pop());
					}

				if (_Unfull_chunk->_Next_available < _Unfull_chunk->_Capacity)
					{
					if (_Unfull_chunk == _Empty_chunk)
						{	// this chunk is no longer empty
						_Empty_chunk = nullptr;
						}
					--_Unfull_chunk->_Free_count;
					char * const _Block = _Unfull_chunk->_Base + _Unfull_chunk->_Next_available * _Block_size;
					++_Unfull_chunk->_Next_available;
					*(reinterpret_cast<_Chunk **>(_Block + _Block_size) - 1) = _Unfull_chunk;
					return (_Block);
					}
				}
			}

		void _Deallocate(unsynchronized_pool_resource& _Pool_resource, void * const _Ptr) noexcept
			{	// return a block to this pool
			_Chunk * _Current = *(reinterpret_cast<_Chunk **>(static_cast<char *>(_Ptr) + _Block_size) - 1);

			_Current->_Free_blocks._Push(::new (_Ptr) _Single_link<>);

			if (_Current->_Free_count++ == 0)
				{
				// prefer to allocate from newer/larger chunks...
				if (!_Unfull_chunk || _Unfull_chunk->_Id < _Current->_Id)
					{
					_Unfull_chunk = _Current;
					}

				return;
				}

			if (_Current->_Free_count < _Current->_Capacity)
				{
				return;
				}

			if (_Empty_chunk == nullptr)
				{
				_Empty_chunk = _Current;
				return;
				}

			// ...and release older/smaller chunks to keep the list lengths short.
			if (_Empty_chunk->_Id < _Current->_Id)
				{
				_STD swap(_Current, _Empty_chunk);
				}

			_All_chunks._Remove(_Current);
			_Pool_resource.upstream_resource()->deallocate(_Current->_Base,
				_Size_for_capacity(_Current->_Capacity), _Block_size);
			}

		size_t _Size_for_capacity(const size_t _Capacity) const noexcept
			{	// return the size of a chunk that holds _Capacity blocks
			return ((_Capacity << _Log_of_size) + sizeof(_Chunk));
			}

		void _Increase_capacity(unsynchronized_pool_resource& _Pool_resource)
			{	// this pool has no free blocks; get a new chunk from upstream
			const size_t _Size = _Size_for_capacity(_Next_capacity);
			memory_resource * const _Resource = _Pool_resource.upstream_resource();
			void * const _Ptr = _Resource->allocate(_Size, _Block_size);
			_Check_alignment(_Ptr, _Block_size);

			void * const _Tmp = static_cast<char *>(_Ptr) + _Size - sizeof(_Chunk);
			_Unfull_chunk = ::new (_Tmp) _Chunk{*this, _Ptr, _Next_capacity};
			_Empty_chunk = _Unfull_chunk;
			_All_chunks._Push(_Unfull_chunk);

			// scale _Next_capacity by 2, saturating so that _Size_for_capacity(_Next_capacity) cannot overflow
			_Next_capacity = _Min_value(_Next_capacity << 1,
				_Min_value((PTRDIFF_MAX - sizeof(_Chunk)) >> _Log_of_size,
					_Pool_resource._Options.max_blocks_per_chunk));
			}
		};

	void _Setup_options() noexcept
		{	// configure pool options
		constexpr auto _Max_blocks_per_chunk_limit = static_cast<size_t>(PTRDIFF_MAX);
		constexpr auto _Largest_required_pool_block_limit =
			static_cast<size_t>((PTRDIFF_MAX >> 4) + 1); // somewhat arbitrary power of 2
		static_assert(_Is_pow_2(_Largest_required_pool_block_limit));

		if (_Options.max_blocks_per_chunk - 1 >= _Max_blocks_per_chunk_limit)
			{
			_Options.max_blocks_per_chunk = _Max_blocks_per_chunk_limit;
			}

		if (_Options.largest_required_pool_block - 1 < sizeof(void *))
			{
			_Options.largest_required_pool_block = sizeof(void *);
			}
		else if (_Options.largest_required_pool_block - 1 >= _Largest_required_pool_block_limit)
			{
			_Options.largest_required_pool_block = _Largest_required_pool_block_limit;
			}
		else
			{
			_Options.largest_required_pool_block = static_cast<size_t>(1)
				<< _Ceiling_of_log_2(_Options.largest_required_pool_block);
			}
		}

	pair<pmr::vector<_Pool>::iterator, unsigned char>
		_Find_pool(const size_t _Bytes, const size_t _Align) noexcept
		{	// find the pool from which to allocate a block with size _Bytes and alignment _Align
		const size_t _Size = _Max_value(_Bytes + sizeof(void *), _Align);
		const auto _Log_of_size = static_cast<unsigned char>(_Ceiling_of_log_2(_Size));
		return {_STD lower_bound(_Pools.begin(), _Pools.end(), _Log_of_size,
			[](const _Pool& _Al, const unsigned char _Log)
				{
				return (_Al._Log_of_size < _Log);
				}),
			_Log_of_size};
		}

	pool_options _Options{};                       // parameters that control the behavior of this pool resource
	_Intrusive_list<_Oversized_header> _Chunks{};  // list of oversized allocations obtained directly from upstream
	pmr::vector<_Pool> _Pools{};                   // pools in order of increasing block size
	};

#ifndef _M_CEE
class synchronized_pool_resource
	: public unsynchronized_pool_resource
	{
public:
	using unsynchronized_pool_resource::unsynchronized_pool_resource;

	void release() noexcept // strengthened
		{
		lock_guard<mutex> _Guard{_Mtx};
		this->unsynchronized_pool_resource::release();
		}

protected:
	virtual void * do_allocate(const size_t _Bytes, const size_t _Align) override
		{
		lock_guard<mutex> _Guard{_Mtx};
		return (this->unsynchronized_pool_resource::do_allocate(_Bytes, _Align));
		}

	virtual void do_deallocate(void * const _Ptr, const size_t _Bytes, const size_t _Align) override
		{
		lock_guard<mutex> _Guard{_Mtx};
		this->unsynchronized_pool_resource::do_deallocate(_Ptr, _Bytes, _Align);
		}

private:
	mutable mutex _Mtx;
	};
#endif /* _M_CEE */

		// CLASS TEMPLATE monotonic_buffer_resource
class monotonic_buffer_resource
	: public _Identity_equal_resource
	{
public:
	explicit monotonic_buffer_resource(memory_resource * const _Upstream) noexcept // strengthened
		: _Resource{_Upstream}
		{	// initialize this resource with upstream
		}

	monotonic_buffer_resource(const size_t _Initial_size, memory_resource * const _Upstream) noexcept // strengthened
		: _Next_buffer_size(_Round(_Initial_size)), _Resource{_Upstream}
		{	// initialize this resource with upstream and initial allocation size
		}

	monotonic_buffer_resource(void * const _Buffer, const size_t _Buffer_size,
		memory_resource * const _Upstream) noexcept // strengthened
		: _Current_buffer(_Buffer), _Space_available(_Buffer_size),
			_Next_buffer_size(_Buffer_size ? _Scale(_Buffer_size) : _Min_allocation),
			_Resource{_Upstream}
		{	// initialize this resource with upstream and initial buffer
		}

	monotonic_buffer_resource() = default;

	explicit monotonic_buffer_resource(const size_t _Initial_size) noexcept // strengthened
		: _Next_buffer_size(_Round(_Initial_size))
		{	// initialize this resource with initial allocation size
		}

	monotonic_buffer_resource(void * const _Buffer, const size_t _Buffer_size) noexcept // strengthened
		: _Current_buffer(_Buffer), _Space_available(_Buffer_size),
			_Next_buffer_size(_Buffer_size ? _Scale(_Buffer_size) : _Min_allocation)
		{	// initialize this resource with initial buffer
		}

	virtual ~monotonic_buffer_resource() noexcept override
		{
		release();
		}

	monotonic_buffer_resource(const monotonic_buffer_resource&) = delete;
	monotonic_buffer_resource& operator=(const monotonic_buffer_resource&) = delete;

	void release() noexcept // strengthened
		{	// return allocated memory back upstream
		if (_Chunks._Empty())
			{
			// nothing to release; potentially continues to use an initial block provided at construction
			return;
			}

		_Current_buffer = nullptr;
		_Space_available = 0;

		// unscale _Next_buffer_size so the next allocation will be the same size as the most recent allocation
		// (keep synchronized with monotonic_buffer_resource::_Scale)
		const size_t _Unscaled = (_Next_buffer_size / 3 * 2 + alignof(_Header) - 1) & _Max_allocation;
		_Next_buffer_size = _Max_value(_Unscaled, _Min_allocation);

		_Intrusive_stack<_Header> _Tmp{};
		_STD swap(_Tmp, _Chunks);
		while (!_Tmp._Empty())
			{
			const auto _Ptr = _Tmp._Pop();
			_Resource->deallocate(_Ptr->_Base_address(), _Ptr->_Size, _Ptr->_Align);
			}
		}

	_NODISCARD memory_resource * upstream_resource() const noexcept // strengthened
		{	// retrieve the upstream resource
		return (_Resource);
		}

protected:
	virtual void * do_allocate(const size_t _Bytes, const size_t _Align) override
		{	// allocate from the current buffer or a new larger buffer from upstream
		if (!_STD align(_Align, _Bytes, _Current_buffer, _Space_available))
			{
			_Increase_capacity(_Bytes, _Align);
			}

		void * const _Result = _Current_buffer;
		_Current_buffer = reinterpret_cast<char *>(_Current_buffer) + _Bytes;
		_Space_available -= _Bytes;
		return (_Result);
		}

	virtual void do_deallocate(void *, size_t, size_t) override
		{	// nothing to do
		}

private:
	struct _Header
		: _Single_link<>
		{	// track the size and alignment of an allocation from upstream
		size_t _Size;
		size_t _Align;

		_Header(const size_t _Size_, const size_t _Align_)
			: _Size{_Size_}, _Align{_Align_}
		{}

		void * _Base_address() const
			{	// header is stored at the end of the allocated memory block
			return (const_cast<char *>(reinterpret_cast<const char *>(this + 1) - _Size));
			}
		};

	static constexpr size_t _Min_allocation = 2 * sizeof(_Header);
	static constexpr size_t _Max_allocation = 0 - alignof(_Header);

	static constexpr size_t _Round(const size_t _Size) noexcept
		{	// return the smallest multiple of alignof(_Header) greater than _Size,
			// clamped to the range [_Min_allocation, _Max_allocation]
		if (_Size < _Min_allocation)
			{
			return (_Min_allocation);
			}

		if (_Size >= _Max_allocation)
			{
			return (_Max_allocation);
			}

		// Since _Max_allocation == -alignof(_Header), _Size < _Max_allocation implies that
		// (_Size + alignof(_Header) - 1) does not overflow.
		return ((_Size + alignof(_Header) - 1) & _Max_allocation);
		}

	static constexpr size_t _Scale(const size_t _Size) noexcept
		{	// scale _Size by 1.5, rounding up to a multiple of alignof(_Header),
			// saturating to _Max_allocation
			// (keep synchronized with monotonic_buffer_resource::release)
		constexpr auto _Max_size = (_Max_allocation - alignof(_Header) + 1) / 3 * 2;
		if (_Size >= _Max_size)
			{
			return (_Max_allocation);
			}

		return ((_Size + ((_Size + 1) >> 1) + alignof(_Header) - 1) & _Max_allocation);
		}

	void _Increase_capacity(const size_t _Bytes, const size_t _Align)
		{	// obtain a new buffer from upstream
		if (_Bytes > _Max_allocation - sizeof(_Header))
			{
			_Xbad_alloc();
			}

		size_t _New_size = _Next_buffer_size;
		if (_New_size < _Bytes + sizeof(_Header))
			{
			_New_size = (_Bytes + sizeof(_Header) + alignof(_Header) - 1) & _Max_allocation;
			}

		const size_t _New_align = _Max_value(alignof(_Header), _Align);

		void * _New_buffer = _Resource->allocate(_New_size, _New_align);
		_Check_alignment(_New_buffer, _New_align);

		_Current_buffer = _New_buffer;
		_Space_available = _New_size - sizeof(_Header);
		_New_buffer = static_cast<char *>(_New_buffer) + _Space_available;
		_Chunks._Push(::new (_New_buffer) _Header{_New_size, _New_align});

		_Next_buffer_size = _Scale(_New_size);
		}

	void * _Current_buffer = nullptr;                               // current memory block to parcel out to callers
	size_t _Space_available = 0;                                    // space remaining in current block
	size_t _Next_buffer_size = _Min_allocation;                     // size of next block to allocate from upstream
	_Intrusive_stack<_Header> _Chunks{};                            // list of memory blocks allocated from upstream
	memory_resource * _Resource = _STD pmr::get_default_resource(); // upstream resource from which to allocate
	};
} // namespace pmr

_STD_END

 #pragma pop_macro("new")
 _STL_RESTORE_CLANG_WARNINGS
 #pragma warning(pop)
 #pragma pack(pop)
#else /* ^^^ _HAS_CXX17 / !_HAS_CXX17 vvv */
 #pragma message("The contents of <memory_resource> are only available with C++17 or later.")
#endif /* _HAS_CXX17 */
#endif /* RC_INVOKED */
#endif /* _MEMORY_RESOURCE_ */
