Zakero's C++ Header Libraries
A collection of reusable C++ libraries
Public Types | Public Member Functions | Static Public Member Functions | List of all members
zakero::MemoryPool Class Reference

A pool of memory. More...

Public Types

enum  Alignment : uint8_t
 The Byte-Alignment of the MemoryPool. More...
 
using AddressMap = std::map< uint8_t *, uint8_t * >
 A mapping of old addresses to new addresses.
 
using LambdaSize = std::function< void(size_t)>
 A lambda that receives a size_t argument.
 
using LambdaAddressMap = std::function< void(const MemoryPool::AddressMap &)>
 A lambda that receives a MemoryPool::AddressMap.
 

Public Member Functions

 MemoryPool (const std::string &) noexcept
 Constructor. More...
 
 ~MemoryPool () noexcept
 Destructor. More...
 
std::error_condition init (const size_t, const bool=false, const MemoryPool::Alignment=MemoryPool::Alignment::Bits_64) noexcept
 Initialize the MemoryPool. More...
 
int fd () const noexcept
 The backing file descriptor. More...
 
size_t size () const noexcept
 The size of the memory pool. More...
 
void sizeOnChange (MemoryPool::LambdaSize) noexcept
 Set the Size Event callback. More...
 
off_t alloc (const size_t) noexcept
 Allocate memory from the pool. More...
 
off_t alloc (const size_t, std::error_condition &) noexcept
 Allocate memory from the pool. More...
 
off_t alloc (size_t, uint8_t) noexcept
 Allocate memory from the pool. More...
 
off_t alloc (size_t, uint8_t, std::error_condition &) noexcept
 Allocate memory from the pool. More...
 
off_t alloc (size_t, uint32_t) noexcept
 Allocate memory from the pool. More...
 
off_t alloc (size_t, uint32_t, std::error_condition &) noexcept
 Allocate memory from the pool. More...
 
void free (off_t &) noexcept
 Free allocated memory. More...
 
off_t realloc (off_t, size_t) noexcept
 Change the size of allocated memory. More...
 
off_t realloc (off_t, size_t, std::error_condition &) noexcept
 Change the size of allocated memory. More...
 
uint8_t * addressOf (off_t) const noexcept
 Convert an offset into a pointer. More...
 
void onRemap (MemoryPool::LambdaAddressMap) noexcept
 Set the Remap Event callback. More...
 
std::string dump (size_t, size_t) const noexcept
 Output the internal state. More...
 

Static Public Member Functions

static uint8_t * remap (const MemoryPool::AddressMap &, uint8_t *) noexcept
 Get the new memory address. More...
 

Detailed Description

Refer to Zakero_MemoryPool.h to learn how to include this library.

This object will create a region of memory and provide an interface to allocate from that memory.

Member Enumeration Documentation

◆ Alignment

enum zakero::MemoryPool::Alignment : uint8_t
strong

When allocating memory from the MemoryPool, this enum determines which byte-boundary will be used.

Constructor & Destructor Documentation

◆ MemoryPool()

zakero::MemoryPool::MemoryPool ( const std::string &  name)
noexcept

Create a new instance of the MemoryPool. The provide file name is not the name of a file on the file system. The file name will only exist in RAM with an optional backing store in swap in available.

Example
zakero::MemoryPool memory_pool("The name of the MemoryPool");
Note
If ZAKERO_MEMORYPOOL_PROFILER is defined, then ZAKERO_PROFILER_INIT_METADATA will be called to setup the profiler.
Parameters
nameThe file name

◆ ~MemoryPool()

zakero::MemoryPool::~MemoryPool ( )
noexcept

Release all allocated resources.

Member Function Documentation

◆ init()

std::error_condition zakero::MemoryPool::init ( const size_t  size,
const bool  expandable = false,
const MemoryPool::Alignment  alignment = MemoryPool::Alignment::Bits_64 
)
noexcept

The MemoryPool must be initialized before it can be used. At a minimum, the size of the MemoryPool must be specified in bytes.

The maximum allowable size is MemoryPool::Size_Max which represents the largest offset value supported by the MemoryPool. Your hardware configuration and/or operating system may lower this limit.

Setting the expandable flag to true will allow the MemoryPool to grow to a larger size. If an allocation request is made that is larger that the largest available contiguous space available, then the Memory Pool will expand just enough to accommodate the request.

The Byte Boundary of all the allocations for this MemoryPool object is specified by the alignment.

Note
The size of the MemoryPool will never shrink.
Example
zakero::MemoryPool rgba_textures("Active Texture Cache");
#define KILOBYETS(size_) ((size_) * 1024)
#define MEGABYTES(size_) (KILOBYTES(size_) * 1024)
rgba_textures.init(MEGABYTES(32)
, false // Restricted environment, no expanding
, zakero::MemoryPool::Alignment::Bits_32 // RGBA == 32 bits
);
Returns
An error condition. If there was no error, then the value of the error condition will be 0.
Parameters
sizeThe initial size in bytes
expandableAllow the MemoryPool to expand
alignmentThe Byte Alignment

◆ fd()

int zakero::MemoryPool::fd ( ) const
noexcept

If something needs to be able to map the same region of memory as this MemoryPool, then this method will provide the file descriptor to do it.

The entire MemoryPool will be accessible from the file descriptor.

Example
// Get read access to the shared memory
uint8_t* mem_reader = (uint8_t*)mmap(nullptr
, memory_pool.size()
, PROT_READ
, MAP_SHARED | MAP_NORESERVE
, memory_pool.fd()
);
See also
MemoryPool::size()
Returns
The file descriptor.

◆ size()

size_t zakero::MemoryPool::size ( ) const
noexcept

The current size of the MemoryPool, in bytes, will be returned.

Returns
The size of the memory pool.

◆ sizeOnChange()

void zakero::MemoryPool::sizeOnChange ( MemoryPool::LambdaSize  lambda)
noexcept

If the MemoryPool was configured to be able to dynamically expand as needed (see init()), then the provided lambda will be called when the memory pool changes size. Using the sizeOnChange() method before calling init() will not cause the lambda to be executed when init() is called.

The lambda will receive the new size, in bytes, of the memory pool.

Example
zakero::MemoryPool memory_pool("Size Matters");
memory_pool.init(1, true);
memory_pool.sizeOnChange([](size_t new_size)
{
std::cout << "Size: " << size_t << "\n";
});
off_t uno = memory_pool.alloc(256);
off_t dos = memory_pool.alloc(512);
Note
The MemoryPool will be in a "locked state" so any call from the lambda to a non-const MemoryPool method will block indefinitely.
Parameters
lambdaThe callback

◆ alloc() [1/6]

off_t zakero::MemoryPool::alloc ( const size_t  size)
noexcept

The requested size (in bytes) will be allocated from the memory pool. If the memory could not be allocated, then -1 will be returned.

Example
zakero::MemoryPool memory_pool("foo");
memory_pool.init(1024);
off_t data_offset = memory_pool.alloc(128);
Note
The contents of the memory is undefined.
Returns
The offset of the block of memory.
Parameters
sizeThe size in bytes

◆ alloc() [2/6]

off_t zakero::MemoryPool::alloc ( const size_t  size,
std::error_condition &  error 
)
noexcept

The requested size (in bytes) will be allocated from the memory pool. If the memory could not be allocated, then -1 will be returned and the reason will be stored in error.

Example
zakero::MemoryPool memory_pool("foo");
memory_pool.init(1024);
std::error_condition error;
off_t data_offset = memory_pool.alloc(512, error);
if(error.value() != 0)
{
std::cerr << "Error: " << error.message() << "\n";
}
Note
The contents of the memory is undefined.
Returns
The offset of the block of memory.
Parameters
sizeThe size in bytes
errorThe error

◆ alloc() [3/6]

off_t zakero::MemoryPool::alloc ( size_t  size,
uint8_t  value 
)
noexcept

The requested size (in bytes) will be allocated from the memory pool. If the memory could not be allocated, then -1 will be returned.

The every byte of the allocated memory will be set to value.

Example
zakero::MemoryPool memory_pool("foo");
memory_pool.init(1024);
off_t data_offset = memory_pool.alloc(512, uint8_t(0xa5));
Returns
The offset of the block of memory.
Parameters
sizeThe size in bytes
valueThe fill value

◆ alloc() [4/6]

off_t zakero::MemoryPool::alloc ( size_t  size,
uint8_t  value,
std::error_condition &  error 
)
noexcept

The requested size (in bytes) will be allocated from the memory pool. If the memory could not be allocated, then -1 will be returned and the reason will be stored in error.

The every byte of the allocated memory will be set to value.

Example
zakero::MemoryPool memory_pool("foo");
memory_pool.init(1024);
std::error_condition error;
off_t data_offset = memory_pool.alloc(512, uint8_t(0xa5), error);
if(error.value() != 0)
{
std::cerr << "Error: " << error.message() << "\n";
}
Returns
The offset of the block of memory.
Parameters
sizeThe size in bytes
valueThe fill value
errorThe error

◆ alloc() [5/6]

off_t zakero::MemoryPool::alloc ( size_t  size,
uint32_t  value 
)
noexcept

The requested size (in bytes) will be allocated from the memory pool. If the memory could not be allocated, then -1 will be returned.

Every 32-bit in the allocated memory will be set to value. Any bytes that leftover will be undefined. For example, 10 byte allocation with a value of 0xaaaa5555. The last 2 bytes will be undefined. Memory Contents: aaaa5555aaaa5555??

Example
zakero::MemoryPool memory_pool("foo");
memory_pool.init(1024);
off_t data_offset = memory_pool.alloc(512, uint32_t(0xaaaa5555));
Returns
The offset of the block of memory.
Parameters
sizeThe size in bytes
valueThe fill value

◆ alloc() [6/6]

off_t zakero::MemoryPool::alloc ( size_t  size,
uint32_t  value,
std::error_condition &  error 
)
noexcept

The requested size (in bytes) will be allocated from the memory pool. If the memory could not be allocated, then -1 will be returned and the reason will be stored in error.

Every 32-bit in the allocated memory will be set to value. Any bytes that leftover will be undefined. For example, 10 byte allocation with a value of 0xaaaa5555. The last 2 bytes will be undefined. Memory Contents: aaaa5555aaaa5555??

Example
zakero::MemoryPool memory_pool("foo");
memory_pool.init(1024);
std::error_condition error;
off_t data_offset = memory_pool.alloc(512, uint32_t(0xaaaa5555),
error);
if(error.value() != 0)
{
std::cerr << "Error: " << error.message() << "\n";
}
Returns
The offset of the block of memory.
Parameters
sizeThe size in bytes
valueThe fill value
errorThe error

◆ free()

void zakero::MemoryPool::free ( off_t &  offset)
noexcept

The allocated memory at the provided offset will be free'ed. The offset will be set to -1.

If the offset is not valid, its value will not be changed.

Example
zakero::MemoryPool memory_pool("A Memory Pool");
memory_pool.init(128);
off_t offset = memory_pool.alloc(64);
// Do stuff
memory_pool.free(offset);
Parameters
offsetThe memory to free

◆ realloc() [1/2]

off_t zakero::MemoryPool::realloc ( off_t  offset,
size_t  size 
)
noexcept

This method is similar to std::realloc(), in that it will resize the allocated memory at the given offset. If the resize was successful, the new offset will be returned.

The contents of the memory will be preserved.

The return value will be -1 if the allocated memory could not be resized.

Example
zakero::MemoryPool memory_pool("Resizing in the Pool");
memory_pool.init(128);
off_t offset = memory_pool.alloc(64);
// Do stuff
// Oops, need more space
offset = memory_pool.resize(offset, 96);
Returns
The offset of the resized memory location.
Parameters
offsetThe memory to resize
sizeThe size in bytes

◆ realloc() [2/2]

off_t zakero::MemoryPool::realloc ( off_t  offset,
size_t  size,
std::error_condition &  error 
)
noexcept

This method is similar to std::realloc(), in that it will resize the allocated memory at the given offset. If the resize was successful, the new offset will be returned.

The contents of the memory will be preserved.

The return value will be -1 if the allocated memory could not be resized and the reason will be stored in error.

Example
zakero::MemoryPool memory_pool("Resizing in the Pool");
memory_pool.init(128);
off_t offset = memory_pool.alloc(64);
// Do stuff
// Oops, need more space
std::error_condition error;
auto new_offset = memory_pool.resize(offset, 96, error);
if(error(bool) == true)
{
std::cerr << "Error: " << error.message() << "\n";
}
else
{
offset = new_offset;
}
Returns
The offset of the resized memory location.
Parameters
offsetThe memory to resize
sizeThe size in bytes
errorThe error

◆ addressOf()

uint8_t * zakero::MemoryPool::addressOf ( off_t  offset) const
noexcept

The provided offset will be converted into an address that can be de-referenced as a normal C-Style pointer. If the offset is not valid, then nullptr will be returned.

Example
off_t offset = memory_pool.alloc(256);
uint8_t* ptr = memory_pool.addressOf(offset);
Note
If the MemoryPool expands and is relocated, the returned pointers will no longer be valid.
See also
onRemap()
Returns
An address
Parameters
offsetThe offset

◆ onRemap()

void zakero::MemoryPool::onRemap ( MemoryPool::LambdaAddressMap  lambda)
noexcept

There are times when the MemoryPool will move allocated data. When this happens, the provided lambda will be called so that the caller will have an opportunity to update their pointers.

The lambda will receive a map of addresses where the key is the old address and the value is the new address.

If the MemoryPool was configured to be not expandable (see init()), then the MemoryPool will never have a need to move it's region of memory. Therefore, the lambda will never be called and the pointers will never become invalid (unless the memory is freed).

Example
uint8_t* secret = nullptr;
memory_pool.onRemap([&](const zakero::MemoryPool::AddressMap& map)
{
secret = zakero::MemoryPool::remap(map, secret);
});
off_t secret_offset = memory_pool.alloc(512);
secret = memory_pool.addressOf(secret_offset);
Note
The MemoryPool will be in a "locked state" so any call from the lambda to a non-const MemoryPool method will block indefinitely.
See also
remap()
Parameters
lambdaThe callback

◆ remap()

uint8_t * zakero::MemoryPool::remap ( const MemoryPool::AddressMap addr_map,
uint8_t *  address 
)
staticnoexcept

Lookup the provided address in the addr_map and return the new address. If the address was not in the addr_map, the value of the address will be returned.

Note
This is a convenience method whose only purpose is to improve the quality-of-life of the MemoryPool API.
See also
onRemap()
Returns
A memory address.
Parameters
addr_mapA map of old/new addresses
addressAn old memory address

◆ dump()

std::string zakero::MemoryPool::dump ( size_t  bytes_per_character,
size_t  characters_per_line 
) const
noexcept

The internal state of the MemoryPool will be converted into a JSON formatted string. The JSON string will contain the following:

  • The name of the anonymous file
  • The size of the memory pool
  • A list of segments and information about each segment
  • A layout of memory usage

The content of the layout is controlled by the bytes_per_character and characters_per_line parameters.

Example
zakero::MemoryPool memory_pool("Dump It");
memory_pool(512);
off_t o1 = memory_pool.alloc(64);
off_t o2 = memory_pool.alloc(64);
off_t o3 = memory_pool.alloc(128);
off_t o4 = memory_pool.alloc(256);
memory_pool.free(o2);
memory_pool.dump(1, 128);
Returns
The JSON formatted string.
Parameters
bytes_per_characterUsed to determine how many allocated bytes are represented by each character in the layout.
characters_per_lineThe length of each layout line.

The documentation for this class was generated from the following file:
zakero::MemoryPool::remap
static uint8_t * remap(const MemoryPool::AddressMap &, uint8_t *) noexcept
Get the new memory address.
Definition: Zakero_MemoryPool.h:1364
zakero::MemoryPool::AddressMap
std::map< uint8_t *, uint8_t * > AddressMap
A mapping of old addresses to new addresses.
Definition: Zakero_MemoryPool.h:260
zakero::MemoryPool
A pool of memory.
Definition: Zakero_MemoryPool.h:241