9 #ifndef zakero_MemoryPool_h
10 #define zakero_MemoryPool_h
213 #include <functional>
216 #include <system_error>
219 #include <sys/mman.h>
246 #define ZAKERO_MEMORYPOOL__ERROR_DATA \
247 X(Error_None , 0 , "No Error" ) \
248 X(Error_Already_Initialized , 1 , "The Memory Pool has already been initialized." ) \
249 X(Error_Size_Too_Small , 2 , "Invalid Size: Must be greater than 0." ) \
250 X(Error_Size_Too_Large , 3 , "Invalid Size: Must be less than zakero::MemoryPool::Size_Max" ) \
251 X(Error_Failed_To_Create_File , 4 , "Unable to create file." ) \
252 X(Error_Failed_To_Resize_File , 5 , "Unable to resize file." ) \
253 X(Error_Failed_To_Map_File , 6 , "Unable to memory map the file." ) \
254 X(Error_Out_Of_Memory , 7 , "Not enough contiguous memory." ) \
255 X(Error_Invalid_Offset , 8 , "The offset is not valid." ) \
266 #define X(name_, val_, mesg_) \
267 static constexpr int name_ = val_;
268 ZAKERO_MEMORYPOOL__ERROR_DATA
270 static constexpr
size_t Size_Max = (size_t)std::numeric_limits<off_t>::max();
292 [[nodiscard]]
int fd() const noexcept;
293 [[nodiscard]]
size_t size() const noexcept;
296 [[nodiscard]] off_t
alloc(const
size_t) noexcept;
297 [[nodiscard]] off_t
alloc(const
size_t, std::error_code&) noexcept;
298 [[nodiscard]] off_t
alloc(
size_t, uint8_t) noexcept;
299 [[nodiscard]] off_t
alloc(
size_t, uint8_t, std::error_code&) noexcept;
300 [[nodiscard]] off_t
alloc(
size_t, uint32_t) noexcept;
301 [[nodiscard]] off_t
alloc(
size_t, uint32_t, std::error_code&) noexcept;
302 void free(off_t&) noexcept;
303 [[nodiscard]] off_t
realloc(off_t,
size_t) noexcept;
304 [[nodiscard]] off_t
realloc(off_t,
size_t, std::error_code&) noexcept;
306 [[nodiscard]] uint8_t*
addressOf(off_t) const noexcept;
310 [[nodiscard]] std::
string dump(
size_t,
size_t) const noexcept;
320 using VectorSegment = std::vector<Segment>;
326 mutable std::mutex mutex;
327 MemoryPool::VectorSegment segment;
333 bool pool_can_expand;
337 [[nodiscard]]
bool expandToFit(
size_t,
size_t&) noexcept;
339 [[nodiscard]]
bool segmentExpand(
const size_t,
const size_t) noexcept;
340 [[nodiscard]]
bool segmentFindBestFit(
const size_t,
size_t&) noexcept;
341 bool segmentFindInUse(
const off_t,
size_t&)
const noexcept;
342 void segmentMergeNext(
const size_t) noexcept;
343 void segmentMergePrev(
const size_t) noexcept;
344 [[nodiscard]]
size_t segmentMove(
const size_t,
const size_t,
size_t&) noexcept;
345 void segmentSplit(
size_t,
size_t) noexcept;
358 #ifdef ZAKERO_MEMORYPOOL_IMPLEMENTATION
363 #ifdef ZAKERO__DOXYGEN_DEFINE_DOCS
378 #define ZAKERO_MEMORYPOOL_IMPLEMENTATION
393 #define ZAKERO_MEMORYPOOL__ERROR(err_) std::error_code(err_, MemoryPoolErrorCategory);
412 class MemoryPoolErrorCategory_
413 :
public std::error_category
416 constexpr MemoryPoolErrorCategory_() noexcept
420 const char* name()
const noexcept
override
422 return "zakero.MemoryPool";
425 std::string message(
int condition)
const override
429 #define X(name_, val_, mesg_) \
430 case val_: return mesg_;
431 ZAKERO_MEMORYPOOL__ERROR_DATA
435 return "Unknown error condition";
437 } MemoryPoolErrorCategory;
457 inline off_t calculateActualSize(
const off_t size
461 const off_t mod =
static_cast<off_t
>(alignment);
462 const off_t step =
static_cast<off_t
>(alignment) + 1;
464 return ((size + mod) / step) * step;
561 , size_on_change(LambdaSize_DoNothing)
562 , on_remap(LambdaAddressMap_DoNothing)
564 , file_descriptor(-1)
565 , alignment(zakero::MemoryPool::Alignment::Bits_64)
566 , pool_can_expand(
false)
578 on_remap = LambdaAddressMap_DoNothing;
582 if(memory !=
nullptr)
584 #ifdef ZAKERO_MEMORYPOOL_ZERO_ON_FREE
585 memset(memory,
'\0', pool_size);
588 munmap(memory, pool_size);
592 close(file_descriptor);
593 file_descriptor = -1;
634 ,
const bool expandable
640 if(this->file_descriptor != -1)
642 return ZAKERO_MEMORYPOOL__ERROR(Error_Already_Initialized);
647 return ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Small);
650 if(size > MemoryPool::Size_Max)
652 return ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Large);
655 size_t pool_size = calculateActualSize(size, alignment);
658 int fd = memfd_create(name.c_str(), 0);
660 #error Need more code...
665 return ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Create_File);
668 if(ftruncate(fd, pool_size) == -1)
670 return ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Resize_File);
673 uint8_t* memory = (uint8_t*)mmap(
nullptr
675 , PROT_READ | PROT_WRITE
676 , MAP_SHARED | MAP_NORESERVE
681 if(memory == MAP_FAILED)
685 return ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Map_File);
688 this->segment.push_back(
690 , .size = (off_t)pool_size
694 this->pool_can_expand = expandable;
695 this->alignment = alignment;
696 this->pool_size = pool_size;
697 this->memory = memory;
698 this->file_descriptor = fd;
700 return ZAKERO_MEMORYPOOL__ERROR(Error_None);
729 return file_descriptor;
778 if(lambda ==
nullptr)
780 size_on_change = LambdaSize_DoNothing;
784 size_on_change = lambda;
810 std::error_code error;
812 return alloc(size, error);
841 , std::error_code& error
850 error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Small);
854 if(size > MemoryPool::Size_Max)
856 error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Large);
860 const size_t segment_size = calculateActualSize(size, alignment);
864 std::lock_guard<std::mutex> lock(mutex);
868 if(segmentFindBestFit(segment_size, index) ==
false)
870 if(pool_can_expand ==
false)
872 error = ZAKERO_MEMORYPOOL__ERROR(Error_Out_Of_Memory);
876 if(expandToFit(segment_size, index) ==
false)
878 error = ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Resize_File);
883 error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
885 segment[index].in_use =
true;
887 if((
size_t)segment[index].size == segment_size)
889 return segment[index].offset;
892 segmentSplit(index, segment_size);
894 return segment[index].offset;
920 std::error_code error;
922 return alloc(size, value, error);
952 , std::error_code& error
955 off_t offset = alloc(size, error);
962 uint8_t* ptr = addressOf(offset);
964 memset(ptr, value, size);
995 std::error_code error;
997 return alloc(size, value, error);
1030 ,
const uint32_t value
1031 , std::error_code& error
1034 off_t offset = alloc(size, error);
1041 size_t count = size / 4;
1042 uint32_t* p = (uint32_t*)addressOf(offset);
1044 for(uint32_t i = 0; i < count; i++)
1078 std::lock_guard<std::mutex> lock(mutex);
1082 if(segmentFindInUse(offset, index) ==
false)
1087 #ifdef ZAKERO_MEMORYPOOL_ZERO_ON_FREE
1088 memset(memory + segment[index].offset
1090 , segment[index].size
1094 segment[index].in_use =
false;
1096 segmentMergeNext(index);
1097 segmentMergePrev(index);
1134 std::error_code error;
1136 return realloc(offset, size, error);
1178 , std::error_code& error
1187 error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Small);
1191 if(size > MemoryPool::Size_Max)
1193 error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Large);
1197 const size_t segment_size = calculateActualSize(size, alignment);
1199 std::lock_guard<std::mutex> lock(mutex);
1201 size_t index_src = 0;
1203 if(segmentFindInUse(offset, index_src) ==
false)
1205 error = ZAKERO_MEMORYPOOL__ERROR(Error_Invalid_Offset);
1209 Segment& segment_src = segment[index_src];
1212 if(segment_size == (
size_t)segment_src.size)
1214 error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1219 if(segment_size < (
size_t)segment_src.size)
1221 segmentSplit(index_src, segment_size);
1223 error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1228 if(segmentExpand(index_src, segment_size) ==
true)
1230 error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1235 size_t index_dst = 0;
1237 if(segmentFindBestFit(segment_size, index_dst) ==
false)
1239 if(pool_can_expand ==
false)
1241 error = ZAKERO_MEMORYPOOL__ERROR(Error_Out_Of_Memory);
1245 if(expandToFit(segment_size, index_dst) ==
false)
1247 error = ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Resize_File);
1252 index_dst = segmentMove(index_src, segment_size, index_dst);
1254 offset = segment[index_dst].offset;
1256 error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1287 if(segmentFindInUse(offset, index) ==
false)
1292 return memory + offset;
1331 if(lambda ==
nullptr)
1333 on_remap = LambdaAddressMap_DoNothing;
1360 if(addr_map.contains(address))
1362 return addr_map.at(address);
1399 ,
size_t characters_per_line
1402 std::lock_guard<std::mutex> lock(mutex);
1404 std::string str =
"{ \"name\": \"" + name +
"\"\n"
1405 +
", \"pool_size\": " + std::to_string(pool_size) +
"\n"
1410 str +=
", \"Segment\":\n";
1411 std::string delim =
" [ ";
1412 for(
const auto& seg : segment)
1415 str +=
"{ \"offset\": " + std::to_string(seg.offset)
1416 +
", \"size\": " + std::to_string(seg.size)
1417 +
", \"in_use\": " + std::to_string(seg.in_use)
1427 int character =
'A';
1429 str +=
", \"Layout\":\n [ \"";
1430 for(
const auto& seg : segment)
1438 size_t segment_count = seg.size / bytes_per_character;
1440 while(segment_count > 0)
1442 if((count + segment_count) >= characters_per_line)
1444 size_t t = characters_per_line - count;
1446 str += std::string(t, ch) +
"\"\n , \"";
1453 str += std::string(segment_count, ch);
1455 count += segment_count;
1462 if(character ==
'Z')
1485 bool MemoryPool::expandToFit(
size_t size_increase
1491 if(pool_can_expand ==
false)
1496 if(segment.back().in_use ==
false)
1498 size_increase -= segment.back().size;
1501 if(pool_size + size_increase > MemoryPool::Size_Max)
1506 uint8_t* old_memory = memory;
1507 const size_t old_size = pool_size;
1508 const size_t new_size = pool_size + size_increase;
1510 if(ftruncate(file_descriptor, new_size) == -1)
1515 void* new_memory = mremap(old_memory
1521 if(new_memory == MAP_FAILED)
1526 pool_size = new_size;
1528 index = segment.size();
1530 && segment[index - 1].in_use ==
false
1534 segment[index].size += (off_t)size_increase;
1539 { .offset = (off_t)old_size
1540 , .size = (off_t)size_increase
1545 size_on_change(pool_size);
1547 if(new_memory != old_memory)
1549 memory = (uint8_t*)new_memory;
1553 for(
const auto& seg : segment)
1557 uint8_t* old_addr = old_memory + seg.offset;
1558 uint8_t* new_addr = memory + seg.offset;
1560 addr_map[old_addr] = new_addr;
1581 bool MemoryPool::segmentExpand(
const size_t index
1587 const size_t index_next = index + 1;
1588 if(index_next >= segment.size())
1593 Segment& segment_next = segment[index_next];
1595 if(segment_next.in_use ==
true)
1600 Segment& segment_this = segment[index];
1602 if(size >
size_t(segment_this.size + segment_next.size))
1607 const size_t size_next = size - segment_this.size;
1609 segment_this.size = size;
1613 segment.erase(std::begin(segment) + index_next);
1617 segment_next.offset += size_next;
1618 segment_next.size -= size_next;
1633 bool MemoryPool::segmentFindBestFit(
const size_t size
1639 for(
size_t i = 0; i < segment.size(); i++)
1641 const Segment& seg = segment[i];
1643 if(seg.in_use ==
false && (
size_t)seg.size >= size)
1662 bool MemoryPool::segmentFindInUse(
const off_t offset
1668 for(
size_t i = 0; i < segment.size(); i++)
1670 const Segment& seg = segment[i];
1672 if(seg.offset > offset)
1677 if(seg.offset == offset && seg.in_use ==
true)
1699 void MemoryPool::segmentMergeNext(
const size_t index
1704 size_t index_next = index + 1;
1706 if(index_next >= segment.size())
1711 Segment& segment_next = segment[index_next];
1713 if(segment_next.in_use ==
true)
1718 segment[index].size += segment[index_next].size;
1720 segment.erase(std::begin(segment) + index_next);
1738 void MemoryPool::segmentMergePrev(
const size_t index
1748 const size_t index_prev = index - 1;
1750 Segment& segment_prev = segment[index_prev];
1752 if(segment_prev.in_use ==
true)
1757 segment_prev.size += segment[index].size;
1759 segment.erase(std::begin(segment) + index);
1780 size_t MemoryPool::segmentMove(
const size_t src_index
1781 ,
const size_t dst_size
1787 Segment& src_segment = segment[src_index];
1788 const uint8_t* src_addr = memory + src_segment.offset;
1789 const size_t src_size = src_segment.size;
1791 Segment& dst_segment = segment[dst_index];
1792 uint8_t* dst_addr = memory + dst_segment.offset;
1793 const size_t dst_offset = dst_segment.offset;
1795 dst_segment.in_use =
true;
1797 memcpy(dst_addr, src_addr, src_size);
1799 #ifdef ZAKERO_MEMORYPOOL_ZERO_ON_FREE
1800 memset(src_addr,
'\0', src_size);
1802 src_segment.in_use =
false;
1804 if(src_index > dst_index)
1806 segmentMergeNext(src_index);
1807 segmentMergePrev(src_index);
1809 segmentSplit(dst_index, dst_size);
1813 segmentSplit(dst_index, dst_size);
1815 segmentMergeNext(src_index);
1816 segmentMergePrev(src_index);
1818 segmentFindInUse(dst_offset, dst_index);
1843 void MemoryPool::segmentSplit(
size_t index
1849 Segment& this_segment = segment[index];
1851 const size_t index_next = index + 1;
1852 const off_t offset_next = this_segment.offset + size;
1853 const off_t size_next = this_segment.size - size;
1855 this_segment.size = size;
1857 if(this_segment.in_use)
1859 uint8_t* addr = memory + offset_next;
1861 memset(addr,
'\0', size_next);
1864 if(index_next >= segment.size())
1867 { .offset = offset_next
1874 Segment& segment_next = segment[index_next];
1876 if(segment_next.in_use)
1878 segment.insert(std::begin(segment) + index_next,
1879 { .offset = offset_next
1886 segment_next.offset = offset_next;
1887 segment_next.size += size_next;
1897 #endif // zakero_MemoryPool_h