43 friend struct ::m::list_allocator;
45 using base_type = allocator<list_allocator_proxy>;
50 list_allocator *alloc_;
54 ~list_allocator_proxy() { }
59 list_allocator * get_allocator()
const {
return alloc_; }
126 M_insist(second.override_addr_ ==
nullptr);
128 M_insist(second.chunks_.get_allocator().get_allocator() == &second);
131 swap(first.min_allocation_size_, second.min_allocation_size_);
132 swap(first.min_allocation_alignment_, second.min_allocation_alignment_);
133 swap(first.strategy_, second.strategy_);
134 swap(first.chunks_, second.chunks_);
141 M_insist(second.override_addr_ ==
nullptr);
170 if (marked_for_deallocation)
186 if (size == 0)
return nullptr;
188 M_insist(alignment == 0 or
is_pow_2(alignment),
"alignment must be 0 or a power of 2");
199 if (usable_size < size or
is_unaligned(header, alignment))
231 if (ptr ==
nullptr)
return;
240 out <<
"allocation size = " << A.min_allocation_size_
241 <<
", alignment = " << A.min_allocation_alignment_
242 <<
", available chunks: ";
243 for (
auto it = A.chunks_.begin(); it != A.chunks_.end(); ++it) {
244 if (it != A.chunks_.begin())
250 out << ((
void*) header) <<
", " <<
extract_size(header) <<
"B]";
255 void dump(std::ostream &out)
const { out << *
this << std::endl; }
265 M_insist(alignment == 0 or
is_pow_2(alignment),
"alignment must be a power of 2");
266 return alignment == 0 ? false :
reinterpret_cast<std::uintptr_t
>(ptr) & (alignment -
size_type(1));
279 ceil_to_multiple_of_pow_2(
reinterpret_cast<std::uintptr_t
>(chunk) + size,
alignof(
size_type))
283 return *
reinterpret_cast<const size_type*
>(
284 ceil_to_multiple_of_pow_2(
reinterpret_cast<std::uintptr_t
>(chunk) + size,
alignof(
size_type))
291 reinterpret_cast<uint8_t*
>(value_addr) +
sizeof(*value_addr) -
sizeof(
header_type)
296 reinterpret_cast<const uint8_t*
>(value_addr) +
sizeof(*value_addr) -
sizeof(
header_type)
352 M_insist(alignment == 0 or
is_pow_2(alignment),
"alignment must be a power of 2");
359 const size_type effective_size = ceil_to_multiple_of_pow_2(
366 void *chunk =
aligned_mmap(effective_size, effective_alignment);
395 "cannot split chunk that does not fit two headers");
398 const size_type effective_size_first_chunk = ceil_to_multiple_of_pow_2(
414 reinterpret_cast<std::uintptr_t
>(header_first_chunk) + effective_size_first_chunk
416 const size_type effective_size_second_chunk = effective_size_original_chunk - effective_size_first_chunk;
417 M_insist(effective_size_second_chunk %
alignof(
header_type) == 0,
"effective size violates alignment");
418 M_insist(effective_size_second_chunk >=
sizeof(
header_type),
"effective size is too small");
419 link_chunk(std::next(it), second_chunk, effective_size_second_chunk);
421 "the second split chunk must never be marked for deallocation");
430 auto successor_it = std::next(it);
447 void *
const addr_end_first_chunk =
reinterpret_cast<uint8_t*
>(header_first_chunk) + size_first_chunk;
449 if (addr_end_first_chunk != header_second_chunk)
456 header_first_chunk->value_ += size_second_chunk;
460 "coalescing must not alter the deallocation mark");
463 return std::prev(next);
485 void *ptr = mmap(
nullptr, size + alignment, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0);
486 if (ptr == MAP_FAILED) {
487 const auto errsv = errno;
488 throw std::runtime_error(strerror(errsv));
491 const std::uintptr_t unaligned_ptr =
reinterpret_cast<std::uintptr_t
>(ptr);
492 const std::uintptr_t aligned_ptr = ceil_to_multiple_of_pow_2(unaligned_ptr, alignment);
493 M_insist(aligned_ptr >= unaligned_ptr);
494 M_insist(aligned_ptr < unaligned_ptr + alignment);
495 void *
const end_of_aligned_allocation =
reinterpret_cast<void*
>(aligned_ptr + size);
497 munmap(ptr, aligned_ptr - unaligned_ptr);
498 munmap(end_of_aligned_allocation, alignment - (aligned_ptr - unaligned_ptr));
500 return reinterpret_cast<void*
>(aligned_ptr);
503 void *ptr = mmap(
nullptr, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0);
504 if (ptr == MAP_FAILED) {
505 const auto errsv = errno;
506 throw std::runtime_error(strerror(errsv));
515inline void * list_allocator_proxy::allocate(size_type size, size_type alignment)
517 return alloc_->proxy_allocate(size, alignment);
520inline void list_allocator_proxy::deallocate(
void *ptr, size_type size)
522 return alloc_->proxy_deallocate(ptr, size);
M_EXPORT constexpr bool is_pow_2(T n)
std::size_t M_EXPORT get_pagesize()
Returns the page size of the system.
std::enable_if_t< not std::is_void_v< T >, T * > allocate()
Allocate space for a single entity of type T that is aligned according to Ts alignment requirement.
void deallocate(void *ptr, size_type size)
Deallocate the allocation at ptr of size size.
allocator_type & get_allocator() const noexcept
iterator erase(iterator pos)
iterator insert(const_iterator pos, const value_type &value)
the_iterator< false > iterator
Implements a list allocator.
chunk_list_type::iterator insert_chunk_sorted(void *chunk, size_type masked_size)
void * override_addr_
used to override the behavior of allocation requests by chunks_ by routing through a proxy allocator
list_allocator(list_allocator &&other)
static constexpr size_type DEALLOCATION_BIT_MASK
mask of the high bit of a size_type (the type of the header of a used chunk)
void * allocate(const size_type size, const size_type alignment=0)
static size_type preserve_deallocation(size_type n, const header_type *header)
static bool is_aligned(void *ptr, size_type alignment)
friend struct list_allocator_proxy
void dump(std::ostream &out) const
void deallocate(void *ptr, const size_type size)
void proxy_deallocate(void *ptr, size_type)
static size_type & size_of_used_chunk(void *chunk, const size_type size)
Returns a reference to the effective size field of a chunk in use.
M_LCOV_EXCL_START friend std::ostream & operator<<(std::ostream &out, const list_allocator &A)
void * aligned_mmap(size_type size, size_type alignment)
size_type num_chunks_available() const
static header_type * header_of_unused_chunk(size_type *value_addr)
Returns a pointer to the header of an unused chunk.
static size_type size_of_used_chunk(const void *chunk, const size_type size)
static constexpr size_type CHUNK_SPLIT_FACTOR
the factor by which a chunk must be larger than the requested allocation to be considered for splitt...
static bool is_unaligned(void *ptr, size_type alignment)
allocator< list_allocator > base_type
chunk_list_type::node_type header_type
the type of a list node, which is the "header" of an unused chunk
static bool is_marked_for_deallocation(const header_type *header)
void * proxy_allocate(size_type, size_type)
chunk_list_type::iterator coalesce(chunk_list_type::iterator it)
Attempt to coalesce the chunk pointed to by it and its successor.
void reclaim_chunk(void *chunk, size_type masked_size)
chunk_list_type::iterator split_unused_chunk(chunk_list_type::iterator it, size_type required_size)
Splits the given chunk into two chunks.
chunk_list_type::iterator add_fresh_chunk(const size_type size, const size_type alignment)
Allocates fresh memory that contains enough space to fit a chunk of size size, that is aligned accord...
size_type min_allocation_size_
the size of the next actual memory allocation
list_allocator & operator=(list_allocator other)
static size_type extract_size(const header_type *header)
size_type min_allocation_alignment_
the minimum alignment of an actual memory allocation
chunk_list_type::iterator link_chunk(chunk_list_type::iterator pos, void *chunk, size_type masked_size)
Adds the given chunk to the list of unused chunks by emplacing the list node within the chunk's memor...
list_allocator(const list_allocator &other)
Copy c'tor.
static const header_type * header_of_unused_chunk(const size_type *value_addr)
chunk_list_type chunks_
the list of unused (free) chunks
list_allocator(size_type initial_size=0, AllocationStrategy strategy=AllocationStrategy::Linear)
static size_type mark_for_deallocation(size_type n)
friend void swap(list_allocator &first, list_allocator &second)
AllocationStrategy strategy_
the allocation strategy; influences how min_allocation_size_ is updated with each actual allocation
chunk_list_type::iterator unlink_chunk(chunk_list_type::iterator it)
Removes the referenced chunk from the list of available chunks without deallocating its memory.