9 #ifndef zakero_MemoryPool_h
10 #define zakero_MemoryPool_h
222 #include <functional>
225 #include <system_error>
228 #include <sys/mman.h>
255 #define ZAKERO_MEMORYPOOL__ERROR_DATA \
256 X(Error_None , 0 , "No Error" ) \
257 X(Error_Already_Initialized , 1 , "The Memory Pool has already been initialized." ) \
258 X(Error_Size_Too_Small , 2 , "Invalid Size: Must be greater than 0." ) \
259 X(Error_Size_Too_Large , 3 , "Invalid Size: Must be less than zakero::MemoryPool::Size_Max" ) \
260 X(Error_Failed_To_Create_File , 4 , "Unable to create file." ) \
261 X(Error_Failed_To_Resize_File , 5 , "Unable to resize file." ) \
262 X(Error_Failed_To_Map_File , 6 , "Unable to memory map the file." ) \
263 X(Error_Out_Of_Memory , 7 , "Not enough contiguous memory." ) \
264 X(Error_Invalid_Offset , 8 , "The offset is not valid." ) \
275 #define X(name_, val_, mesg_) \
276 static constexpr int name_ = val_;
277 ZAKERO_MEMORYPOOL__ERROR_DATA
279 static constexpr
size_t Size_Max = (size_t)std::numeric_limits<off_t>::max();
310 [[nodiscard]]
int fd() const noexcept;
311 [[nodiscard]]
size_t size() const noexcept;
314 [[nodiscard]] off_t
alloc(const
size_t) noexcept;
315 [[nodiscard]] off_t
alloc(const
size_t, std::error_code&) noexcept;
316 [[nodiscard]] off_t
alloc(
size_t, uint8_t) noexcept;
317 [[nodiscard]] off_t
alloc(
size_t, uint8_t, std::error_code&) noexcept;
318 [[nodiscard]] off_t
alloc(
size_t, uint32_t) noexcept;
319 [[nodiscard]] off_t
alloc(
size_t, uint32_t, std::error_code&) noexcept;
320 void free(off_t&) noexcept;
321 [[nodiscard]] off_t
realloc(off_t,
size_t) noexcept;
322 [[nodiscard]] off_t
realloc(off_t,
size_t, std::error_code&) noexcept;
324 [[nodiscard]] uint8_t*
addressOf(off_t) const noexcept;
328 [[nodiscard]] std::
string dump(
size_t,
size_t) const noexcept;
334 mutable std::mutex mutex;
341 bool pool_can_expand;
345 [[nodiscard]]
bool expandToFit(
size_t,
size_t&) noexcept;
347 [[nodiscard]]
bool segmentExpand(const
size_t, const
size_t) noexcept;
348 [[nodiscard]]
bool segmentFindBestFit(const
size_t,
size_t&) noexcept;
349 bool segmentFindInUse(const off_t,
size_t&) const noexcept;
350 void segmentMergeNext(const
size_t) noexcept;
351 void segmentMergePrev(const
size_t) noexcept;
352 [[nodiscard]]
size_t segmentMove(const
size_t, const
size_t,
size_t&) noexcept;
353 void segmentSplit(
size_t,
size_t) noexcept;
371 #ifdef ZAKERO_MEMORYPOOL_IMPLEMENTATION
376 #ifdef ZAKERO__DOXYGEN_DEFINE_DOCS
391 #define ZAKERO_MEMORYPOOL_IMPLEMENTATION
406 #define ZAKERO_MEMORYPOOL__ERROR(err_) std::error_code(err_, MemoryPoolErrorCategory);
425 class MemoryPoolErrorCategory_
426 :
public std::error_category
429 constexpr MemoryPoolErrorCategory_() noexcept
433 const char* name()
const noexcept
override
435 return "zakero.MemoryPool";
438 std::string message(
int condition)
const override
442 #define X(name_, val_, mesg_) \
443 case val_: return mesg_;
444 ZAKERO_MEMORYPOOL__ERROR_DATA
448 return "Unknown error condition";
450 } MemoryPoolErrorCategory;
470 inline off_t calculateActualSize(
const off_t
size
474 const off_t mod =
static_cast<off_t
>(alignment);
475 const off_t step =
static_cast<off_t
>(alignment) + 1;
477 return ((
size + mod) / step) * step;
575 , size_on_change(LambdaSize_DoNothing)
576 , on_remap(LambdaAddressMap_DoNothing)
578 , file_descriptor(-1)
579 , alignment(zakero::MemoryPool::Alignment::Bits_64)
580 , pool_can_expand(
false)
592 on_remap = LambdaAddressMap_DoNothing;
596 if(memory !=
nullptr)
598 #ifdef ZAKERO_MEMORYPOOL_ZERO_ON_FREE
599 memset(memory,
'\0', pool_size);
602 munmap(memory, pool_size);
606 close(file_descriptor);
607 file_descriptor = -1;
649 ,
const bool expandable
655 if(this->file_descriptor != -1)
657 return ZAKERO_MEMORYPOOL__ERROR(Error_Already_Initialized);
662 return ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Small);
665 if(
size > MemoryPool::Size_Max)
667 return ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Large);
670 size_t pool_size = calculateActualSize(
size, alignment);
673 int fd = memfd_create(name.c_str(), 0);
675 #error Need more code...
680 return ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Create_File);
683 if(ftruncate(
fd, pool_size) == -1)
685 return ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Resize_File);
688 uint8_t* memory = (uint8_t*)mmap(
nullptr
690 , PROT_READ | PROT_WRITE
691 , MAP_SHARED | MAP_NORESERVE
696 if(memory == MAP_FAILED)
700 return ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Map_File);
703 this->segment.push_back(
705 , .size = (off_t)pool_size
709 this->pool_can_expand = expandable;
710 this->alignment = alignment;
711 this->pool_size = pool_size;
712 this->memory = memory;
713 this->file_descriptor =
fd;
715 return ZAKERO_MEMORYPOOL__ERROR(Error_None);
744 return file_descriptor;
793 if(lambda ==
nullptr)
795 size_on_change = LambdaSize_DoNothing;
799 size_on_change = lambda;
825 std::error_code error;
856 , std::error_code& error
865 error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Small);
869 if(
size > MemoryPool::Size_Max)
871 error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Large);
875 const size_t segment_size = calculateActualSize(
size, alignment);
879 std::lock_guard<std::mutex> lock(mutex);
883 if(segmentFindBestFit(segment_size, index) ==
false)
885 if(pool_can_expand ==
false)
887 error = ZAKERO_MEMORYPOOL__ERROR(Error_Out_Of_Memory);
891 if(expandToFit(segment_size, index) ==
false)
893 error = ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Resize_File);
898 error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
900 segment[index].in_use =
true;
902 if((
size_t)segment[index].
size == segment_size)
904 return segment[index].offset;
907 segmentSplit(index, segment_size);
909 return segment[index].offset;
935 std::error_code error;
967 , std::error_code& error
979 memset(ptr, value,
size);
1010 std::error_code error;
1045 ,
const uint32_t value
1046 , std::error_code& error
1056 size_t count =
size / 4;
1057 uint32_t* p = (uint32_t*)
addressOf(offset);
1059 for(uint32_t i = 0; i < count; i++)
1093 std::lock_guard<std::mutex> lock(mutex);
1097 if(segmentFindInUse(offset, index) ==
false)
1102 #ifdef ZAKERO_MEMORYPOOL_ZERO_ON_FREE
1103 memset(memory + segment[index].offset
1105 , segment[index].
size
1109 segment[index].in_use =
false;
1111 segmentMergeNext(index);
1112 segmentMergePrev(index);
1149 std::error_code error;
1193 , std::error_code& error
1202 error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Small);
1206 if(
size > MemoryPool::Size_Max)
1208 error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Large);
1212 const size_t segment_size = calculateActualSize(
size, alignment);
1214 std::lock_guard<std::mutex> lock(mutex);
1216 size_t index_src = 0;
1218 if(segmentFindInUse(offset, index_src) ==
false)
1220 error = ZAKERO_MEMORYPOOL__ERROR(Error_Invalid_Offset);
1224 Segment& segment_src = segment[index_src];
1227 if(segment_size == (
size_t)segment_src.
size)
1229 error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1234 if(segment_size < (
size_t)segment_src.
size)
1236 segmentSplit(index_src, segment_size);
1238 error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1243 if(segmentExpand(index_src, segment_size) ==
true)
1245 error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1250 size_t index_dst = 0;
1252 if(segmentFindBestFit(segment_size, index_dst) ==
false)
1254 if(pool_can_expand ==
false)
1256 error = ZAKERO_MEMORYPOOL__ERROR(Error_Out_Of_Memory);
1260 if(expandToFit(segment_size, index_dst) ==
false)
1262 error = ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Resize_File);
1267 index_dst = segmentMove(index_src, segment_size, index_dst);
1269 offset = segment[index_dst].offset;
1271 error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1302 if(segmentFindInUse(offset, index) ==
false)
1307 return memory + offset;
1346 if(lambda ==
nullptr)
1348 on_remap = LambdaAddressMap_DoNothing;
1375 if(addr_map.contains(address))
1377 return addr_map.at(address);
1414 ,
size_t characters_per_line
1417 std::lock_guard<std::mutex> lock(mutex);
1419 std::string str =
"{ \"name\": \"" + name +
"\"\n"
1420 +
", \"pool_size\": " + std::to_string(pool_size) +
"\n"
1425 str +=
", \"Segment\":\n";
1426 std::string delim =
" [ ";
1427 for(
const auto& seg : segment)
1430 str +=
"{ \"offset\": " + std::to_string(seg.offset)
1431 +
", \"size\": " + std::to_string(seg.size)
1432 +
", \"in_use\": " + std::to_string(seg.in_use)
1442 int character =
'A';
1444 str +=
", \"Layout\":\n [ \"";
1445 for(
const auto& seg : segment)
1453 size_t segment_count = seg.size / bytes_per_character;
1455 while(segment_count > 0)
1457 if((count + segment_count) >= characters_per_line)
1459 size_t t = characters_per_line - count;
1461 str += std::string(t, ch) +
"\"\n , \"";
1468 str += std::string(segment_count, ch);
1470 count += segment_count;
1477 if(character ==
'Z')
1502 std::lock_guard<std::mutex> lock(mutex);
1514 bool MemoryPool::expandToFit(
size_t size_increase
1520 if(pool_can_expand ==
false)
1525 if(segment.back().in_use ==
false)
1527 size_increase -= segment.back().size;
1530 if(pool_size + size_increase > MemoryPool::Size_Max)
1535 uint8_t* old_memory = memory;
1536 const size_t old_size = pool_size;
1537 const size_t new_size = pool_size + size_increase;
1539 if(ftruncate(file_descriptor, new_size) == -1)
1544 void* new_memory = mremap(old_memory
1550 if(new_memory == MAP_FAILED)
1555 pool_size = new_size;
1557 index = segment.size();
1559 && segment[index - 1].in_use ==
false
1563 segment[index].size += (off_t)size_increase;
1568 { .offset = (off_t)old_size
1569 , .
size = (off_t)size_increase
1574 size_on_change(pool_size);
1576 if(new_memory != old_memory)
1578 memory = (uint8_t*)new_memory;
1582 for(
const auto& seg : segment)
1586 uint8_t* old_addr = old_memory + seg.offset;
1587 uint8_t* new_addr = memory + seg.offset;
1589 addr_map[old_addr] = new_addr;
1610 bool MemoryPool::segmentExpand(
const size_t index
1616 const size_t index_next = index + 1;
1617 if(index_next >= segment.size())
1622 Segment& segment_next = segment[index_next];
1624 if(segment_next.in_use ==
true)
1629 Segment& segment_this = segment[index];
1631 if(
size >
size_t(segment_this.size + segment_next.size))
1636 const size_t size_next =
size - segment_this.size;
1638 segment_this.size =
size;
1642 segment.erase(std::begin(segment) + index_next);
1646 segment_next.offset += size_next;
1647 segment_next.size -= size_next;
1662 bool MemoryPool::segmentFindBestFit(
const size_t size
1668 for(
size_t i = 0; i < segment.size(); i++)
1670 const Segment& seg = segment[i];
1672 if(seg.in_use ==
false && (
size_t)seg.size >=
size)
1691 bool MemoryPool::segmentFindInUse(
const off_t offset
1697 for(
size_t i = 0; i < segment.size(); i++)
1699 const Segment& seg = segment[i];
1701 if(seg.offset > offset)
1706 if(seg.offset == offset && seg.in_use ==
true)
1728 void MemoryPool::segmentMergeNext(
const size_t index
1733 size_t index_next = index + 1;
1735 if(index_next >= segment.size())
1740 Segment& segment_next = segment[index_next];
1742 if(segment_next.in_use ==
true)
1747 segment[index].size += segment[index_next].size;
1749 segment.erase(std::begin(segment) + index_next);
1767 void MemoryPool::segmentMergePrev(
const size_t index
1777 const size_t index_prev = index - 1;
1779 Segment& segment_prev = segment[index_prev];
1781 if(segment_prev.in_use ==
true)
1786 segment_prev.size += segment[index].size;
1788 segment.erase(std::begin(segment) + index);
1809 size_t MemoryPool::segmentMove(
const size_t src_index
1810 ,
const size_t dst_size
1816 Segment& src_segment = segment[src_index];
1817 const uint8_t* src_addr = memory + src_segment.offset;
1818 const size_t src_size = src_segment.size;
1820 Segment& dst_segment = segment[dst_index];
1821 uint8_t* dst_addr = memory + dst_segment.offset;
1822 const size_t dst_offset = dst_segment.offset;
1824 dst_segment.in_use =
true;
1826 memcpy(dst_addr, src_addr, src_size);
1828 #ifdef ZAKERO_MEMORYPOOL_ZERO_ON_FREE
1829 memset(src_addr,
'\0', src_size);
1831 src_segment.in_use =
false;
1833 if(src_index > dst_index)
1835 segmentMergeNext(src_index);
1836 segmentMergePrev(src_index);
1838 segmentSplit(dst_index, dst_size);
1842 segmentSplit(dst_index, dst_size);
1844 segmentMergeNext(src_index);
1845 segmentMergePrev(src_index);
1847 segmentFindInUse(dst_offset, dst_index);
1872 void MemoryPool::segmentSplit(
size_t index
1878 Segment& this_segment = segment[index];
1880 const size_t index_next = index + 1;
1881 const off_t offset_next = this_segment.offset +
size;
1882 const off_t size_next = this_segment.size -
size;
1884 this_segment.size =
size;
1886 if(this_segment.in_use)
1888 uint8_t* addr = memory + offset_next;
1890 memset(addr,
'\0', size_next);
1893 if(index_next >= segment.size())
1896 { .offset = offset_next
1903 Segment& segment_next = segment[index_next];
1905 if(segment_next.in_use)
1907 segment.insert(std::begin(segment) + index_next,
1908 { .offset = offset_next
1915 segment_next.offset = offset_next;
1916 segment_next.size += size_next;
1932 std::string str =
"";
1934 std::string delim =
"[ ";
1935 for(
const auto& seg : segment)
1938 str +=
"{ \"offset\": " + std::to_string(seg.offset)
1939 +
", \"size\": " + std::to_string(seg.size)
1940 +
", \"in_use\": " + std::to_string(seg.in_use)
std::string to_string(const bool value) noexcept
Convert a bool into a string.
Definition: Zakero_Base.h:561
A pool of memory.
Definition: Zakero_MemoryPool.h:273
std::function< void(size_t)> LambdaSize
A lambda that receives a size_t argument.
Definition: Zakero_MemoryPool.h:301
std::error_code init(const size_t, const bool=false, const MemoryPool::Alignment=MemoryPool::Alignment::Bits_64) noexcept
Initialize the MemoryPool.
Definition: Zakero_MemoryPool.h:648
std::string dump(size_t, size_t) const noexcept
Output the internal state.
Definition: Zakero_MemoryPool.h:1413
off_t size
The size of the allocated memory chunk.
Definition: Zakero_MemoryPool.h:295
std::vector< Segment > VectorSegment
A convenience data type.
Definition: Zakero_MemoryPool.h:304
void free(off_t &) noexcept
Free allocated memory.
Definition: Zakero_MemoryPool.h:1088
~MemoryPool() noexcept
Destructor.
Definition: Zakero_MemoryPool.h:590
std::map< uint8_t *, uint8_t * > AddressMap
A mapping of old addresses to new addresses.
Definition: Zakero_MemoryPool.h:299
bool in_use
A flag used to determine if the memory chunk is in use.
Definition: Zakero_MemoryPool.h:296
Alignment
The Byte-Alignment of the MemoryPool.
Definition: Zakero_MemoryPool.h:282
off_t realloc(off_t, size_t) noexcept
Change the size of allocated memory.
Definition: Zakero_MemoryPool.h:1145
size_t size() const noexcept
The size of the memory pool.
Definition: Zakero_MemoryPool.h:755
int fd() const noexcept
The backing file descriptor.
Definition: Zakero_MemoryPool.h:742
uint8_t * addressOf(off_t) const noexcept
Convert an offset into a pointer.
Definition: Zakero_MemoryPool.h:1295
off_t alloc(const size_t) noexcept
Allocate memory from the pool.
Definition: Zakero_MemoryPool.h:822
off_t offset
The offset into the allocated memory pool.
Definition: Zakero_MemoryPool.h:294
void sizeOnChange(MemoryPool::LambdaSize) noexcept
Set the Size Event callback.
Definition: Zakero_MemoryPool.h:790
static uint8_t * remap(const MemoryPool::AddressMap &, uint8_t *) noexcept
Get the new memory address.
Definition: Zakero_MemoryPool.h:1371
void onRemap(MemoryPool::LambdaAddressMap) noexcept
Set the Remap Event callback.
Definition: Zakero_MemoryPool.h:1343
MemoryPool(const std::string &) noexcept
Constructor.
Definition: Zakero_MemoryPool.h:569
std::function< void(const MemoryPool::AddressMap &)> LambdaAddressMap
A lambda that receives a MemoryPool::AddressMap.
Definition: Zakero_MemoryPool.h:302
VectorSegment segmentList() const noexcept
Information about the current internal state.
Definition: Zakero_MemoryPool.h:1499
Data that defines a segment.
Definition: Zakero_MemoryPool.h:293