// memory_resource standard header

// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef _MEMORY_RESOURCE_
#define _MEMORY_RESOURCE_
#include <yvals.h>
#if _STL_COMPILER_PREPROCESSOR

#if !_HAS_CXX17
_EMIT_STL_WARNING(STL4038, "The contents of <memory_resource> are available only with C++17 or later.");
#else // ^^^ !_HAS_CXX17 / _HAS_CXX17 vvv
#include <vector>
#include <xbit_ops.h>
#include <xpolymorphic_allocator.h>
#include <xutility>

#ifndef _M_CEE_PURE
#include <mutex>
#endif // !defined(_M_CEE_PURE)

#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 {
    extern "C" _CRT_SATELLITE_1 memory_resource* __cdecl _Aligned_set_default_resource(memory_resource*) noexcept;
    extern "C" _CRT_SATELLITE_1 memory_resource* __cdecl _Unaligned_set_default_resource(memory_resource*) noexcept;

    _EXPORT_STD inline memory_resource* set_default_resource(memory_resource* const _Resource) noexcept {
#ifdef __cpp_aligned_new
        return _Aligned_set_default_resource(_Resource);
#else // ^^^ defined(__cpp_aligned_new) / !defined(__cpp_aligned_new) vvv
        return _Unaligned_set_default_resource(_Resource);
#endif // ^^^ !defined(__cpp_aligned_new) ^^^
    }

    _EXPORT_STD extern "C" _NODISCARD _CRT_SATELLITE_1 memory_resource* __cdecl null_memory_resource() noexcept;

    class _Identity_equal_resource : public memory_resource {
    protected:
        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-
        void* do_allocate(const size_t _Bytes, const size_t _Align) override {
            if (_Align > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
                _Xbad_alloc();
            }

            return ::operator new(_Bytes);
        }

        void do_deallocate(void* const _Ptr, const size_t _Bytes, size_t) noexcept override /* strengthened */ {
            ::operator delete(_Ptr, _Bytes);
        }
    };

    extern "C" _CRT_SATELLITE_1 _Unaligned_new_delete_resource_impl* __cdecl _Unaligned_new_delete_resource() noexcept;

#ifdef __cpp_aligned_new
    class _Aligned_new_delete_resource_impl final
        : public _Identity_equal_resource { // implementation for new_delete_resource with aligned new support
        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);
        }

        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" _CRT_SATELLITE_1 _Aligned_new_delete_resource_impl* __cdecl _Aligned_new_delete_resource() noexcept;

    _EXPORT_STD _NODISCARD inline memory_resource* new_delete_resource() noexcept {
        return _Aligned_new_delete_resource();
    }

#else // ^^^ defined(__cpp_aligned_new) / !defined(__cpp_aligned_new) vvv

    _EXPORT_STD _NODISCARD inline memory_resource* new_delete_resource() noexcept {
        return _Unaligned_new_delete_resource();
    }
#endif // ^^^ !defined(__cpp_aligned_new) ^^^

    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>;

        _STL_INTERNAL_STATIC_ASSERT(is_base_of_v<_Link_type, _Ty>);

        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
            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
            return static_cast<_Ty*>(_Ptr);
        }

        constexpr void _Push_front(_Ty* const _Item) noexcept { // insert _Item at the head of this list
            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
            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>;

        _STL_INTERNAL_STATIC_ASSERT(is_base_of_v<_Link_type, _Ty>);

        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 {
            return static_cast<_Link_type*>(_Ptr);
        }

        static constexpr _Ty* _As_item(_Link_type* const _Ptr) noexcept {
            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** _Pnext = &_Head; *_Pnext; _Pnext = &(*_Pnext)->_Next) {
                if (*_Pnext == _Ptr) {
                    *_Pnext = _Ptr->_Next;
                    break;
                }
            }
        }

        _Link_type* _Head = nullptr;
    };

    _EXPORT_STD 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;
    }

    _EXPORT_STD 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 // strengthened
            : _Options(_Opts), _Pools{_Resource} { // initialize pool with options _Opts and upstream _Resource
            _STL_ASSERT(_Resource, "Upstream memory resource must be a valid resource (N4950 [mem.res.pool.ctor]/1)");
            _Setup_options();
        }
        explicit unsynchronized_pool_resource(memory_resource* const _Resource) noexcept // strengthened
            : _Pools{_Resource} { // initialize pool with default options and upstream _Resource
            _STL_ASSERT(_Resource, "Upstream memory resource must be a valid resource (N4950 [mem.res.pool.ctor]/1)");
            _Setup_options();
        }
        explicit unsynchronized_pool_resource(const pool_options& _Opts) noexcept // strengthened
            : _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;

        ~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:
        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);
        }

        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 = (_STD max)(_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*>(static_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
            [[maybe_unused]] const bool _Has_room_for_padding = _Prepare_oversized(_Bytes, _Align);

            // If there's no room for header + alignment padding, this memory WAS NOT allocated by this pool resource
            _STL_ASSERT(_Has_room_for_padding, "Cannot deallocate memory not allocated by this memory pool.");

            _Oversized_header* _Hdr = reinterpret_cast<_Oversized_header*>(static_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) {
                    _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
                if (_Next_capacity > _Pool_resource._Options.max_blocks_per_chunk) {
                    // This is a fresh pool, _Next_capacity hasn't yet been bounded by max_blocks_per_chunk:
                    _Next_capacity = _Pool_resource._Options.max_blocks_per_chunk;
                }
                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 =
                    (_STD min)(_Next_capacity << 1, (_STD min)((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      = (_STD max)(_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) _STATIC_LAMBDA { 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_PURE
    _EXPORT_STD 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};
            unsynchronized_pool_resource::release();
        }

    protected:
        void* do_allocate(const size_t _Bytes, const size_t _Align) override {
            lock_guard<mutex> _Guard{_Mtx};
            return unsynchronized_pool_resource::do_allocate(_Bytes, _Align);
        }

        void do_deallocate(void* const _Ptr, const size_t _Bytes, const size_t _Align) override {
            lock_guard<mutex> _Guard{_Mtx};
            unsynchronized_pool_resource::do_deallocate(_Ptr, _Bytes, _Align);
        }

    private:
        mutable mutex _Mtx;
    };
#endif // !defined(_M_CEE_PURE)

    _EXPORT_STD 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
        }

        ~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 */ {
            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      = (_STD max)(_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:
        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     = static_cast<char*>(_Current_buffer) + _Bytes;
            _Space_available -= _Bytes;
            return _Result;
        }

        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)
#pragma warning(push)
#pragma warning(disable : 26450) // TRANSITION, VSO-1828677
            constexpr auto _Max_size = (_Max_allocation - alignof(_Header) + 1) / 3 * 2;
#pragma warning(pop)
            if (_Size >= _Max_size) {
                return _Max_allocation;
            }

            return (_Size + (_Size + 1) / 2 + 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 = (_STD max)(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)
#endif // ^^^ _HAS_CXX17 ^^^
#endif // _STL_COMPILER_PREPROCESSOR
#endif // _MEMORY_RESOURCE_
