#include <gtest/gtest.h>
{
namespace pf17
{
{
struct MemoryBlock
{
void* aligned_memory{nullptr};
MemoryBlock* next{nullptr};
};
struct MemoryBlockDeleter
{
MemoryBlockDeleter(pmr::memory_resource* upstream)
: upstream_(upstream)
{
}
void operator()(MemoryBlock* cb)
{
CETL_DEBUG_ASSERT(
nullptr != upstream_,
"null memory_resource stored in MemoryBlockDeleter!");
MemoryBlock* n = cb;
while (n != nullptr)
{
MemoryBlock* nn = n->next;
const std::size_t memory_block_size_bytes = n->memory_block_size_bytes;
n->~MemoryBlock();
upstream_->deallocate(n, memory_block_size_bytes);
n = nn;
}
};
private:
pmr::memory_resource* upstream_;
};
using MemoryBlockPointer = std::unique_ptr<MemoryBlock, MemoryBlockDeleter>;
public:
OverAlignedMemoryResource(pmr::memory_resource* upstream = cetl::pf17::pmr::new_delete_resource())
: head_(nullptr, MemoryBlockDeleter(upstream))
, tail_(nullptr)
, upstream_(upstream)
{
}
virtual ~OverAlignedMemoryResource() = default;
OverAlignedMemoryResource(const OverAlignedMemoryResource&) = delete;
OverAlignedMemoryResource& operator=(const OverAlignedMemoryResource&) = delete;
OverAlignedMemoryResource(OverAlignedMemoryResource&& rhs) = delete;
OverAlignedMemoryResource& operator=(OverAlignedMemoryResource&& rhs) = delete;
protected:
void* do_allocate(std::size_t size_bytes, std::size_t alignment) override
{
CETL_DEBUG_ASSERT(alignment && !(alignment & (alignment - 1)),
"Alignment must be a power of 2.");
if (nullptr == upstream_)
{
#ifdefined(__cpp_exceptions)
throw std::bad_alloc();
#endif
return nullptr;
}
if (alignment <= alignof(std::max_align_t))
{
return upstream_->allocate(size_bytes, alignment);
}
const std::size_t control_block_size_bytes =
sizeof(MemoryBlock) + (sizeof(MemoryBlock) % alignof(std::max_align_t));
const std::size_t over_alignment = (alignment > alignof(std::max_align_t)) ? alignment : 0;
const std::size_t upstream_size = control_block_size_bytes + over_alignment + size_bytes;
byte* const max_aligned_memory =
static_cast<byte*>(upstream_->allocate(upstream_size, alignof(std::max_align_t)));
if (nullptr == max_aligned_memory)
{
return nullptr;
}
MemoryBlockPointer cb(new (max_aligned_memory) MemoryBlock{nullptr, size_bytes, upstream_size, nullptr},
MemoryBlockDeleter(upstream_));
CETL_DEBUG_ASSERT((
reinterpret_cast<std::uintptr_t
>(max_aligned_memory) %
alignof(std::max_align_t)) == 0,
"The upstream allocator must provide alignof(std::max_align_t) aligned memory!?");
void* aligned_memory = &static_cast<byte*>(max_aligned_memory)[control_block_size_bytes];
std::size_t max_aligned_memory_size = upstream_size - control_block_size_bytes;
aligned_memory =
std::align(alignment, size_bytes, aligned_memory, max_aligned_memory_size);
if (nullptr == aligned_memory || max_aligned_memory_size < size_bytes)
{
#ifdefined(__cpp_exceptions)
throw std::bad_alloc();
#endif
return nullptr;
}
CETL_DEBUG_ASSERT((
reinterpret_cast<std::uintptr_t
>(aligned_memory) %
static_cast<std::uintptr_t
>(alignment)) ==
0,
"Internal alignment math was incorrect and did not result in a properly aligned memory block "
"block.");
cb->aligned_memory = aligned_memory;
if (nullptr == head_.get())
{
head_.swap(cb);
tail_ = head_.get();
}
else if (nullptr == tail_)
{
head_->next = cb.release();
tail_ = head_->next;
}
else
{
tail_->next = cb.release();
tail_ = tail_->next;
}
return aligned_memory;
}
void do_deallocate(void* p, std::size_t size_bytes, std::size_t alignment) override
{
if (alignment <= alignof(std::max_align_t))
{
upstream_->deallocate(p, size_bytes, alignment);
return;
}
MemoryBlock* previous;
MemoryBlock* cb =
find(p, previous);
CETL_DEBUG_ASSERT((p && cb) || (!p && !cb),
"Unknown pointer provided to do_deallocate.");
"Control Block size did not match size argument for do_deallocate.");
if (nullptr != cb)
{
if (nullptr == previous)
{
(void) head_.release();
head_.reset(cb->next);
}
else
{
previous->next = cb->next;
}
if (cb == tail_)
{
tail_ = cb->next;
}
const std::size_t memory_block_size_bytes = cb->memory_block_size_bytes;
cb->~MemoryBlock();
upstream_->deallocate(cb, memory_block_size_bytes);
}
}
bool do_is_equal(const memory_resource& rhs) const noexcept override
{
return (&rhs == this);
}
private:
MemoryBlock*
find(
void* p, MemoryBlock*& out_previous)
{
out_previous = nullptr;
MemoryBlock* cb = head_.get();
while (cb != nullptr)
{
if (cb->aligned_memory == p)
{
break;
}
out_previous = cb;
cb = cb->next;
}
return cb;
}
MemoryBlockPointer head_;
MemoryBlock* tail_;
memory_resource* upstream_;
};
class FakeDmaTransfer
{
public:
FakeDmaTransfer(byte* buffer, std::size_t buffer_size)
: buffer_{buffer}
, buffer_size_{buffer_size}
, make_believe_progress_{0}
{
}
bool is_complete() const
{
const std::size_t run_until =
std::min(buffer_size_, make_believe_progress_ + (buffer_size_ / 12));
for (; make_believe_progress_ < run_until; ++make_believe_progress_)
{
buffer_[make_believe_progress_] =
static_cast<byte>(make_believe_progress_ %
sizeof(
byte));
}
return (make_believe_progress_ >= buffer_size_);
}
private:
byte* buffer_;
std::size_t buffer_size_;
mutable std::size_t make_believe_progress_;
};
template <typename T>
std::unique_ptr<T, cetl::pmr::MemoryResourceDeleter<cetl::pf17::pmr::memory_resource>> allocate_buffer(
std::size_t buffer_size,
std::size_t buffer_alignment)
{
return std::unique_ptr<
T,
cetl::pmr::MemoryResourceDeleter<
cetl::pf17::pmr::memory_resource>>{static_cast<T*>(allocator.allocate(buffer_size, buffer_alignment)),
cetl::pmr::MemoryResourceDeleter<
cetl::pf17::pmr::memory_resource>{&allocator,
buffer_size,
buffer_alignment}};
}
}
}
TEST(example_03_memory_resource, main)
{
cetl::pf17::OverAlignedMemoryResource over_aligned_new_delete_resource{};
auto buffer = cetl::pf17::allocate_buffer<cetl::pf17::byte>(over_aligned_new_delete_resource, 0x100000, 128);
cetl::pf17::FakeDmaTransfer transfer(buffer.get(), 0x100000);
while (!transfer.is_complete())
{
}
auto string_buffer =
cetl::pf17::allocate_buffer<char>(*cetl::pf17::pmr::new_delete_resource(), sizeof(char) * 12, alignof(char));
strncpy(string_buffer.get(),
"hello world", 12);
}
Defines a type compatible with C++17 std::byte.
#define CETL_DEBUG_ASSERT(c, m)
When CETL_ENABLE_DEBUG_ASSERT is defined and not 0 then this is redirected to assert as included from...
Definition cetl.hpp:135
Interface to a class that manages memory resources.
Definition memory_resource.hpp:38
Extensions and utilities for types found in the standard memory header to better integrate with pmr t...
memory_resource * set_new_delete_resource(memory_resource *r) noexcept
Replace the memory_resource returned from cetl::pf17::pmr::new_delete_resource().
Definition memory_resource.hpp:120
byte
A non-character type that implements the concept of a byte defined by the C++17 specification.
Definition byte.hpp:26
This namespace contains types specific to CETL and nested namespaces that contain types adhering to t...
Definition _helper_enable_copy_move.hpp:10
Extends the cetl::pf17::pmr namespace with system memory resources.