1// <memory_resource> -*- C++ -*-
2
3// Copyright (C) 2018-2021 Free Software Foundation, Inc.
4//
5// This file is part of the GNU ISO C++ Library.  This library is free
6// software; you can redistribute it and/or modify it under the
7// terms of the GNU General Public License as published by the
8// Free Software Foundation; either version 3, or (at your option)
9// any later version.
10
11// This library is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14// GNU General Public License for more details.
15
16// Under Section 7 of GPL version 3, you are granted additional
17// permissions described in the GCC Runtime Library Exception, version
18// 3.1, as published by the Free Software Foundation.
19
20// You should have received a copy of the GNU General Public License and
21// a copy of the GCC Runtime Library Exception along with this program;
22// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
23// <http://www.gnu.org/licenses/>.
24
25/** @file include/memory_resource
26 *  This is a Standard C++ Library header.
27 */
28
29#ifndef _GLIBCXX_MEMORY_RESOURCE
30#define _GLIBCXX_MEMORY_RESOURCE 1
31
32#pragma GCC system_header
33
34#if __cplusplus >= 201703L
35
36#include <vector>			// vector
37#include <cstddef>			// size_t, max_align_t, byte
38#include <shared_mutex>			// shared_mutex
39#include <bits/align.h>			// align
40#include <bits/functexcept.h>		// __throw_bad_array_new_length
41#include <bits/uses_allocator.h>	// __use_alloc
42#include <bits/uses_allocator_args.h>	// uninitialized_construct_using_alloc
43#include <ext/numeric_traits.h>
44#include <debug/assertions.h>
45
46#if ! __cpp_lib_make_obj_using_allocator
47# include <utility>			// pair, index_sequence
48# include <tuple>			// tuple, forward_as_tuple
49#endif
50
51namespace std _GLIBCXX_VISIBILITY(default)
52{
53_GLIBCXX_BEGIN_NAMESPACE_VERSION
54namespace pmr
55{
56#ifdef _GLIBCXX_HAS_GTHREADS
57  // Header and all contents are present.
58# define __cpp_lib_memory_resource 201603L
59#else
60  // The pmr::synchronized_pool_resource type is missing.
61# define __cpp_lib_memory_resource 1
62#endif
63
64  class memory_resource;
65
66#if __cplusplus == 201703L
67  template<typename _Tp>
68    class polymorphic_allocator;
69#else // C++20
70# define __cpp_lib_polymorphic_allocator 201902L
71  template<typename _Tp = std::byte>
72    class polymorphic_allocator;
73#endif
74
75  // Global memory resources
76  memory_resource* new_delete_resource() noexcept;
77  memory_resource* null_memory_resource() noexcept;
78  memory_resource* set_default_resource(memory_resource* __r) noexcept;
79  memory_resource* get_default_resource() noexcept
80    __attribute__((__returns_nonnull__));
81
82  // Pool resource classes
83  struct pool_options;
84#ifdef _GLIBCXX_HAS_GTHREADS
85  class synchronized_pool_resource;
86#endif
87  class unsynchronized_pool_resource;
88  class monotonic_buffer_resource;
89
90  /// Class memory_resource
91  class memory_resource
92  {
93    static constexpr size_t _S_max_align = alignof(max_align_t);
94
95  public:
96    memory_resource() = default;
97    memory_resource(const memory_resource&) = default;
98    virtual ~memory_resource(); // key function
99
100    memory_resource& operator=(const memory_resource&) = default;
101
102    [[nodiscard]]
103    void*
104    allocate(size_t __bytes, size_t __alignment = _S_max_align)
105    __attribute__((__returns_nonnull__,__alloc_size__(2),__alloc_align__(3)))
106    { return do_allocate(__bytes, __alignment); }
107
108    void
109    deallocate(void* __p, size_t __bytes, size_t __alignment = _S_max_align)
110    __attribute__((__nonnull__))
111    { return do_deallocate(__p, __bytes, __alignment); }
112
113    bool
114    is_equal(const memory_resource& __other) const noexcept
115    { return do_is_equal(__other); }
116
117  private:
118    virtual void*
119    do_allocate(size_t __bytes, size_t __alignment) = 0;
120
121    virtual void
122    do_deallocate(void* __p, size_t __bytes, size_t __alignment) = 0;
123
124    virtual bool
125    do_is_equal(const memory_resource& __other) const noexcept = 0;
126  };
127
128  inline bool
129  operator==(const memory_resource& __a, const memory_resource& __b) noexcept
130  { return &__a == &__b || __a.is_equal(__b); }
131
132#if __cpp_impl_three_way_comparison < 201907L
133  inline bool
134  operator!=(const memory_resource& __a, const memory_resource& __b) noexcept
135  { return !(__a == __b); }
136#endif
137
138  // C++17 23.12.3 Class template polymorphic_allocator
139  template<typename _Tp>
140    class polymorphic_allocator
141    {
142      // _GLIBCXX_RESOLVE_LIB_DEFECTS
143      // 2975. Missing case for pair construction in polymorphic allocators
144      template<typename _Up>
145	struct __not_pair { using type = void; };
146
147      template<typename _Up1, typename _Up2>
148	struct __not_pair<pair<_Up1, _Up2>> { };
149
150    public:
151      using value_type = _Tp;
152
153      polymorphic_allocator() noexcept
154      : _M_resource(get_default_resource())
155      { }
156
157      polymorphic_allocator(memory_resource* __r) noexcept
158      __attribute__((__nonnull__))
159      : _M_resource(__r)
160      { _GLIBCXX_DEBUG_ASSERT(__r); }
161
162      polymorphic_allocator(const polymorphic_allocator& __other) = default;
163
164      template<typename _Up>
165	polymorphic_allocator(const polymorphic_allocator<_Up>& __x) noexcept
166	: _M_resource(__x.resource())
167	{ }
168
169      polymorphic_allocator&
170      operator=(const polymorphic_allocator&) = delete;
171
172      [[nodiscard]]
173      _Tp*
174      allocate(size_t __n)
175      __attribute__((__returns_nonnull__))
176      {
177	if ((__gnu_cxx::__int_traits<size_t>::__max / sizeof(_Tp)) < __n)
178	  std::__throw_bad_array_new_length();
179	return static_cast<_Tp*>(_M_resource->allocate(__n * sizeof(_Tp),
180						       alignof(_Tp)));
181      }
182
183      void
184      deallocate(_Tp* __p, size_t __n) noexcept
185      __attribute__((__nonnull__))
186      { _M_resource->deallocate(__p, __n * sizeof(_Tp), alignof(_Tp)); }
187
188#if __cplusplus > 201703L
189      [[nodiscard]] void*
190      allocate_bytes(size_t __nbytes,
191		     size_t __alignment = alignof(max_align_t))
192      { return _M_resource->allocate(__nbytes, __alignment); }
193
194      void
195      deallocate_bytes(void* __p, size_t __nbytes,
196		       size_t __alignment = alignof(max_align_t))
197      { _M_resource->deallocate(__p, __nbytes, __alignment); }
198
199      template<typename _Up>
200	[[nodiscard]] _Up*
201	allocate_object(size_t __n = 1)
202	{
203	  if ((__gnu_cxx::__int_traits<size_t>::__max / sizeof(_Up)) < __n)
204	    std::__throw_bad_array_new_length();
205	  return static_cast<_Up*>(allocate_bytes(__n * sizeof(_Up),
206						  alignof(_Up)));
207	}
208
209      template<typename _Up>
210	void
211	deallocate_object(_Up* __p, size_t __n = 1)
212	{ deallocate_bytes(__p, __n * sizeof(_Up), alignof(_Up)); }
213
214      template<typename _Up, typename... _CtorArgs>
215	[[nodiscard]] _Up*
216	new_object(_CtorArgs&&... __ctor_args)
217	{
218	  _Up* __p = allocate_object<_Up>();
219	  __try
220	    {
221	      construct(__p, std::forward<_CtorArgs>(__ctor_args)...);
222	    }
223	  __catch (...)
224	    {
225	      deallocate_object(__p);
226	      __throw_exception_again;
227	    }
228	  return __p;
229	}
230
231      template<typename _Up>
232	void
233	delete_object(_Up* __p)
234	{
235	  destroy(__p);
236	  deallocate_object(__p);
237	}
238#endif // C++2a
239
240#if ! __cpp_lib_make_obj_using_allocator
241      template<typename _Tp1, typename... _Args>
242	__attribute__((__nonnull__))
243	typename __not_pair<_Tp1>::type
244	construct(_Tp1* __p, _Args&&... __args)
245	{
246	  // _GLIBCXX_RESOLVE_LIB_DEFECTS
247	  // 2969. polymorphic_allocator::construct() shouldn't pass resource()
248	  using __use_tag
249	    = std::__uses_alloc_t<_Tp1, polymorphic_allocator, _Args...>;
250	  if constexpr (is_base_of_v<__uses_alloc0, __use_tag>)
251	    ::new(__p) _Tp1(std::forward<_Args>(__args)...);
252	  else if constexpr (is_base_of_v<__uses_alloc1_, __use_tag>)
253	    ::new(__p) _Tp1(allocator_arg, *this,
254			    std::forward<_Args>(__args)...);
255	  else
256	    ::new(__p) _Tp1(std::forward<_Args>(__args)..., *this);
257	}
258
259      template<typename _Tp1, typename _Tp2,
260	       typename... _Args1, typename... _Args2>
261	__attribute__((__nonnull__))
262	void
263	construct(pair<_Tp1, _Tp2>* __p, piecewise_construct_t,
264		  tuple<_Args1...> __x, tuple<_Args2...> __y)
265	{
266	  auto __x_tag =
267	    __use_alloc<_Tp1, polymorphic_allocator, _Args1...>(*this);
268	  auto __y_tag =
269	    __use_alloc<_Tp2, polymorphic_allocator, _Args2...>(*this);
270	  index_sequence_for<_Args1...> __x_i;
271	  index_sequence_for<_Args2...> __y_i;
272
273	  ::new(__p) pair<_Tp1, _Tp2>(piecewise_construct,
274				      _S_construct_p(__x_tag, __x_i, __x),
275				      _S_construct_p(__y_tag, __y_i, __y));
276	}
277
278      template<typename _Tp1, typename _Tp2>
279	__attribute__((__nonnull__))
280	void
281	construct(pair<_Tp1, _Tp2>* __p)
282	{ this->construct(__p, piecewise_construct, tuple<>(), tuple<>()); }
283
284      template<typename _Tp1, typename _Tp2, typename _Up, typename _Vp>
285	__attribute__((__nonnull__))
286	void
287	construct(pair<_Tp1, _Tp2>* __p, _Up&& __x, _Vp&& __y)
288	{
289	  this->construct(__p, piecewise_construct,
290	      std::forward_as_tuple(std::forward<_Up>(__x)),
291	      std::forward_as_tuple(std::forward<_Vp>(__y)));
292	}
293
294      template <typename _Tp1, typename _Tp2, typename _Up, typename _Vp>
295	__attribute__((__nonnull__))
296	void
297	construct(pair<_Tp1, _Tp2>* __p, const std::pair<_Up, _Vp>& __pr)
298	{
299	  this->construct(__p, piecewise_construct,
300	      std::forward_as_tuple(__pr.first),
301	      std::forward_as_tuple(__pr.second));
302	}
303
304      template<typename _Tp1, typename _Tp2, typename _Up, typename _Vp>
305	__attribute__((__nonnull__))
306	void
307	construct(pair<_Tp1, _Tp2>* __p, pair<_Up, _Vp>&& __pr)
308	{
309	  this->construct(__p, piecewise_construct,
310	      std::forward_as_tuple(std::forward<_Up>(__pr.first)),
311	      std::forward_as_tuple(std::forward<_Vp>(__pr.second)));
312	}
313#else // make_obj_using_allocator
314      template<typename _Tp1, typename... _Args>
315	__attribute__((__nonnull__))
316	void
317	construct(_Tp1* __p, _Args&&... __args)
318	{
319	  std::uninitialized_construct_using_allocator(__p, *this,
320	      std::forward<_Args>(__args)...);
321	}
322#endif
323
324      template<typename _Up>
325	__attribute__((__nonnull__))
326	void
327	destroy(_Up* __p)
328	{ __p->~_Up(); }
329
330      polymorphic_allocator
331      select_on_container_copy_construction() const noexcept
332      { return polymorphic_allocator(); }
333
334      memory_resource*
335      resource() const noexcept
336      __attribute__((__returns_nonnull__))
337      { return _M_resource; }
338
339    private:
340      using __uses_alloc1_ = __uses_alloc1<polymorphic_allocator>;
341      using __uses_alloc2_ = __uses_alloc2<polymorphic_allocator>;
342
343#if ! __cpp_lib_make_obj_using_allocator
344      template<typename _Ind, typename... _Args>
345	static tuple<_Args&&...>
346	_S_construct_p(__uses_alloc0, _Ind, tuple<_Args...>& __t)
347	{ return std::move(__t); }
348
349      template<size_t... _Ind, typename... _Args>
350	static tuple<allocator_arg_t, polymorphic_allocator, _Args&&...>
351	_S_construct_p(__uses_alloc1_ __ua, index_sequence<_Ind...>,
352		       tuple<_Args...>& __t)
353	{
354	  return {
355	      allocator_arg, *__ua._M_a, std::get<_Ind>(std::move(__t))...
356	  };
357	}
358
359      template<size_t... _Ind, typename... _Args>
360	static tuple<_Args&&..., polymorphic_allocator>
361	_S_construct_p(__uses_alloc2_ __ua, index_sequence<_Ind...>,
362		       tuple<_Args...>& __t)
363	{ return { std::get<_Ind>(std::move(__t))..., *__ua._M_a }; }
364#endif
365
366      memory_resource* _M_resource;
367    };
368
369  template<typename _Tp1, typename _Tp2>
370    inline bool
371    operator==(const polymorphic_allocator<_Tp1>& __a,
372	       const polymorphic_allocator<_Tp2>& __b) noexcept
373    { return *__a.resource() == *__b.resource(); }
374
375#if __cpp_impl_three_way_comparison < 201907L
376  template<typename _Tp1, typename _Tp2>
377    inline bool
378    operator!=(const polymorphic_allocator<_Tp1>& __a,
379	       const polymorphic_allocator<_Tp2>& __b) noexcept
380    { return !(__a == __b); }
381#endif
382
383  /// Parameters for tuning a pool resource's behaviour.
384  struct pool_options
385  {
386    /** @brief Upper limit on number of blocks in a chunk.
387     *
388     * A lower value prevents allocating huge chunks that could remain mostly
389     * unused, but means pools will need to replenished more frequently.
390     */
391    size_t max_blocks_per_chunk = 0;
392
393    /* @brief Largest block size (in bytes) that should be served from pools.
394     *
395     * Larger allocations will be served directly by the upstream resource,
396     * not from one of the pools managed by the pool resource.
397     */
398    size_t largest_required_pool_block = 0;
399  };
400
401  // Common implementation details for un-/synchronized pool resources.
402  class __pool_resource
403  {
404    friend class synchronized_pool_resource;
405    friend class unsynchronized_pool_resource;
406
407    __pool_resource(const pool_options& __opts, memory_resource* __upstream);
408
409    ~__pool_resource();
410
411    __pool_resource(const __pool_resource&) = delete;
412    __pool_resource& operator=(const __pool_resource&) = delete;
413
414    // Allocate a large unpooled block.
415    void*
416    allocate(size_t __bytes, size_t __alignment);
417
418    // Deallocate a large unpooled block.
419    void
420    deallocate(void* __p, size_t __bytes, size_t __alignment);
421
422
423    // Deallocate unpooled memory.
424    void release() noexcept;
425
426    memory_resource* resource() const noexcept
427    { return _M_unpooled.get_allocator().resource(); }
428
429    struct _Pool;
430
431    _Pool* _M_alloc_pools();
432
433    const pool_options _M_opts;
434
435    struct _BigBlock;
436    // Collection of blocks too big for any pool, sorted by address.
437    // This also stores the only copy of the upstream memory resource pointer.
438    _GLIBCXX_STD_C::pmr::vector<_BigBlock> _M_unpooled;
439
440    const int _M_npools;
441  };
442
443#ifdef _GLIBCXX_HAS_GTHREADS
444  /// A thread-safe memory resource that manages pools of fixed-size blocks.
445  class synchronized_pool_resource : public memory_resource
446  {
447  public:
448    synchronized_pool_resource(const pool_options& __opts,
449				 memory_resource* __upstream)
450    __attribute__((__nonnull__));
451
452    synchronized_pool_resource()
453    : synchronized_pool_resource(pool_options(), get_default_resource())
454    { }
455
456    explicit
457    synchronized_pool_resource(memory_resource* __upstream)
458    __attribute__((__nonnull__))
459    : synchronized_pool_resource(pool_options(), __upstream)
460    { }
461
462    explicit
463    synchronized_pool_resource(const pool_options& __opts)
464    : synchronized_pool_resource(__opts, get_default_resource()) { }
465
466    synchronized_pool_resource(const synchronized_pool_resource&) = delete;
467
468    virtual ~synchronized_pool_resource();
469
470    synchronized_pool_resource&
471    operator=(const synchronized_pool_resource&) = delete;
472
473    void release();
474
475    memory_resource*
476    upstream_resource() const noexcept
477    __attribute__((__returns_nonnull__))
478    { return _M_impl.resource(); }
479
480    pool_options options() const noexcept { return _M_impl._M_opts; }
481
482  protected:
483    void*
484    do_allocate(size_t __bytes, size_t __alignment) override;
485
486    void
487    do_deallocate(void* __p, size_t __bytes, size_t __alignment) override;
488
489    bool
490    do_is_equal(const memory_resource& __other) const noexcept override
491    { return this == &__other; }
492
493  public:
494    // Thread-specific pools (only public for access by implementation details)
495    struct _TPools;
496
497  private:
498    _TPools* _M_alloc_tpools(lock_guard<shared_mutex>&);
499    _TPools* _M_alloc_shared_tpools(lock_guard<shared_mutex>&);
500    auto _M_thread_specific_pools() noexcept;
501
502    __pool_resource _M_impl;
503    __gthread_key_t _M_key;
504    // Linked list of thread-specific pools. All threads share _M_tpools[0].
505    _TPools* _M_tpools = nullptr;
506    mutable shared_mutex _M_mx;
507  };
508#endif
509
510  /// A non-thread-safe memory resource that manages pools of fixed-size blocks.
511  class unsynchronized_pool_resource : public memory_resource
512  {
513  public:
514    [[__gnu__::__nonnull__]]
515    unsynchronized_pool_resource(const pool_options& __opts,
516				 memory_resource* __upstream);
517
518    unsynchronized_pool_resource()
519    : unsynchronized_pool_resource(pool_options(), get_default_resource())
520    { }
521
522    [[__gnu__::__nonnull__]]
523    explicit
524    unsynchronized_pool_resource(memory_resource* __upstream)
525    : unsynchronized_pool_resource(pool_options(), __upstream)
526    { }
527
528    explicit
529    unsynchronized_pool_resource(const pool_options& __opts)
530    : unsynchronized_pool_resource(__opts, get_default_resource()) { }
531
532    unsynchronized_pool_resource(const unsynchronized_pool_resource&) = delete;
533
534    virtual ~unsynchronized_pool_resource();
535
536    unsynchronized_pool_resource&
537    operator=(const unsynchronized_pool_resource&) = delete;
538
539    void release();
540
541    [[__gnu__::__returns_nonnull__]]
542    memory_resource*
543    upstream_resource() const noexcept
544    { return _M_impl.resource(); }
545
546    pool_options options() const noexcept { return _M_impl._M_opts; }
547
548  protected:
549    void*
550    do_allocate(size_t __bytes, size_t __alignment) override;
551
552    void
553    do_deallocate(void* __p, size_t __bytes, size_t __alignment) override;
554
555    bool
556    do_is_equal(const memory_resource& __other) const noexcept override
557    { return this == &__other; }
558
559  private:
560    using _Pool = __pool_resource::_Pool;
561
562    auto _M_find_pool(size_t) noexcept;
563
564    __pool_resource _M_impl;
565    _Pool* _M_pools = nullptr;
566  };
567
568  class monotonic_buffer_resource : public memory_resource
569  {
570  public:
571    explicit
572    monotonic_buffer_resource(memory_resource* __upstream) noexcept
573    __attribute__((__nonnull__))
574    : _M_upstream(__upstream)
575    { _GLIBCXX_DEBUG_ASSERT(__upstream != nullptr); }
576
577    monotonic_buffer_resource(size_t __initial_size,
578			      memory_resource* __upstream) noexcept
579    __attribute__((__nonnull__))
580    : _M_next_bufsiz(__initial_size),
581      _M_upstream(__upstream)
582    {
583      _GLIBCXX_DEBUG_ASSERT(__upstream != nullptr);
584      _GLIBCXX_DEBUG_ASSERT(__initial_size > 0);
585    }
586
587    monotonic_buffer_resource(void* __buffer, size_t __buffer_size,
588			      memory_resource* __upstream) noexcept
589    __attribute__((__nonnull__(4)))
590    : _M_current_buf(__buffer), _M_avail(__buffer_size),
591      _M_next_bufsiz(_S_next_bufsize(__buffer_size)),
592      _M_upstream(__upstream),
593      _M_orig_buf(__buffer), _M_orig_size(__buffer_size)
594    {
595      _GLIBCXX_DEBUG_ASSERT(__upstream != nullptr);
596      _GLIBCXX_DEBUG_ASSERT(__buffer != nullptr || __buffer_size == 0);
597    }
598
599    monotonic_buffer_resource() noexcept
600    : monotonic_buffer_resource(get_default_resource())
601    { }
602
603    explicit
604    monotonic_buffer_resource(size_t __initial_size) noexcept
605    : monotonic_buffer_resource(__initial_size, get_default_resource())
606    { }
607
608    monotonic_buffer_resource(void* __buffer, size_t __buffer_size) noexcept
609    : monotonic_buffer_resource(__buffer, __buffer_size, get_default_resource())
610    { }
611
612    monotonic_buffer_resource(const monotonic_buffer_resource&) = delete;
613
614    virtual ~monotonic_buffer_resource(); // key function
615
616    monotonic_buffer_resource&
617    operator=(const monotonic_buffer_resource&) = delete;
618
619    void
620    release() noexcept
621    {
622      if (_M_head)
623	_M_release_buffers();
624
625      // reset to initial state at contruction:
626      if ((_M_current_buf = _M_orig_buf))
627	{
628	  _M_avail = _M_orig_size;
629	  _M_next_bufsiz = _S_next_bufsize(_M_orig_size);
630	}
631      else
632	{
633	  _M_avail = 0;
634	  _M_next_bufsiz = _M_orig_size;
635	}
636    }
637
638    memory_resource*
639    upstream_resource() const noexcept
640    __attribute__((__returns_nonnull__))
641    { return _M_upstream; }
642
643  protected:
644    void*
645    do_allocate(size_t __bytes, size_t __alignment) override
646    {
647      if (__builtin_expect(__bytes == 0, false))
648	__bytes = 1; // Ensures we don't return the same pointer twice.
649
650      void* __p = std::align(__alignment, __bytes, _M_current_buf, _M_avail);
651      if (__builtin_expect(__p == nullptr, false))
652	{
653	  _M_new_buffer(__bytes, __alignment);
654	  __p = _M_current_buf;
655	}
656      _M_current_buf = (char*)_M_current_buf + __bytes;
657      _M_avail -= __bytes;
658      return __p;
659    }
660
661    void
662    do_deallocate(void*, size_t, size_t) override
663    { }
664
665    bool
666    do_is_equal(const memory_resource& __other) const noexcept override
667    { return this == &__other; }
668
669  private:
670    // Update _M_current_buf and _M_avail to refer to a new buffer with
671    // at least the specified size and alignment, allocated from upstream.
672    void
673    _M_new_buffer(size_t __bytes, size_t __alignment);
674
675    // Deallocate all buffers obtained from upstream.
676    void
677    _M_release_buffers() noexcept;
678
679    static size_t
680    _S_next_bufsize(size_t __buffer_size) noexcept
681    {
682      if (__builtin_expect(__buffer_size == 0, false))
683	__buffer_size = 1;
684      return __buffer_size * _S_growth_factor;
685    }
686
687    static constexpr size_t _S_init_bufsize = 128 * sizeof(void*);
688    static constexpr float _S_growth_factor = 1.5;
689
690    void*	_M_current_buf = nullptr;
691    size_t	_M_avail = 0;
692    size_t	_M_next_bufsiz = _S_init_bufsize;
693
694    // Initial values set at construction and reused by release():
695    memory_resource* const	_M_upstream;
696    void* const			_M_orig_buf = nullptr;
697    size_t const		_M_orig_size = _M_next_bufsiz;
698
699    class _Chunk;
700    _Chunk* _M_head = nullptr;
701  };
702
703} // namespace pmr
704_GLIBCXX_END_NAMESPACE_VERSION
705} // namespace std
706
707#endif // C++17
708#endif // _GLIBCXX_MEMORY_RESOURCE
709