192 #ifndef zakero_MemoryPool_h
193 #define zakero_MemoryPool_h
197 #include <functional>
200 #include <sys/mman.h>
201 #include <system_error>
224 #define ZAKERO_MEMORYPOOL__ERROR_CODES \
225 X(Error_None , 0 , "No Error" ) \
226 X(Error_Already_Initialized , 1 , "The Memory Pool has already been initialized." ) \
227 X(Error_Size_Too_Small , 2 , "Invalid Size: Must be greater than 0." ) \
228 X(Error_Size_Too_Large , 3 , "Invalid Size: Must be less than zakero::MemoryPool::Size_Max" ) \
229 X(Error_Failed_To_Create_File , 4 , "Unable to create file." ) \
230 X(Error_Failed_To_Resize_File , 5 , "Unable to resize file." ) \
231 X(Error_Failed_To_Map_File , 6 , "Unable to memory map the file." ) \
232 X(Error_Out_Of_Memory , 7 , "Not enough contiguous memory." ) \
233 X(Error_Invalid_Offset , 8 , "The offset is not valid." ) \
243 #define X(name_, val_, mesg_) \
244 static constexpr int name_ = val_;
245 ZAKERO_MEMORYPOOL__ERROR_CODES
247 static constexpr
size_t Size_Max = (size_t)std::numeric_limits<off_t>::max();
269 [[nodiscard]]
int fd() const noexcept;
270 [[nodiscard]]
size_t size() const noexcept;
273 [[nodiscard]] off_t
alloc(const
size_t) noexcept;
274 [[nodiscard]] off_t
alloc(const
size_t, std::error_condition&) noexcept;
275 [[nodiscard]] off_t
alloc(
size_t, uint8_t) noexcept;
276 [[nodiscard]] off_t
alloc(
size_t, uint8_t, std::error_condition&) noexcept;
277 [[nodiscard]] off_t
alloc(
size_t, uint32_t) noexcept;
278 [[nodiscard]] off_t
alloc(
size_t, uint32_t, std::error_condition&) noexcept;
279 void free(off_t&) noexcept;
280 [[nodiscard]] off_t
realloc(off_t,
size_t) noexcept;
281 [[nodiscard]] off_t
realloc(off_t,
size_t, std::error_condition&) noexcept;
283 [[nodiscard]] uint8_t*
addressOf(off_t) const noexcept;
287 [[nodiscard]] std::
string dump(
size_t,
size_t) const noexcept;
290 [[nodiscard]]
bool expandToFit(
size_t,
size_t&) noexcept;
292 [[nodiscard]]
bool segmentExpand(const
size_t, const
size_t) noexcept;
293 [[nodiscard]]
bool segmentFindBestFit(const
size_t,
size_t&) noexcept;
294 bool segmentFindInUse(const off_t,
size_t&) const noexcept;
295 void segmentMergeNext(const
size_t) noexcept;
296 void segmentMergePrev(const
size_t) noexcept;
297 [[nodiscard]]
size_t segmentMove(const
size_t, const
size_t,
size_t&) noexcept;
298 void segmentSplit(
size_t,
size_t) noexcept;
309 using VectorSegment = std::vector<Segment>;
313 mutable std::mutex mutex;
314 MemoryPool::VectorSegment segment;
320 bool pool_can_expand;
327 #if defined (ZAKERO_MEMORYPOOL_IMPLEMENTATION)
331 #if defined(ZAKERO_MEMORYPOOL_PROFILER)
332 # if !defined(ZAKERO_PROFILER_IMPLEMENTATION)
333 # define ZAKERO_PROFILER_IMPLEMENTATION
335 # define ZAKERO_PROFILER_ENABLE
351 #define ZAKERO_MEMORYPOOL__ERROR(err_) std::error_condition(err_, MemoryPoolErrorCategory);
360 #define ZAKERO_MACRO_HAS_VALUE(macro_define_) \
361 ~(~macro_define_ + 0) == 0 && ~(~macro_define_ + 1) == 1
365 #if defined(ZAKERO__DOXYGEN_DEFINE_DOCS)
380 #define ZAKERO_MEMORYPOOL_IMPLEMENTATION
399 #define ZAKERO_MEMORYPOOL_PROFILER
413 #define ZAKERO_MEMORYPOOL_PROFILER_FILE
420 #if defined(ZAKERO_MEMORYPOOL_PROFILER)
421 # if !defined(ZAKERO_MEMORYPOOL_PROFILER_FILE) || !ZAKERO_MACRO_HAS_VALUE(ZAKERO_MEMORYPOOL_PROFILER_FILE)
422 # define ZAKERO_MEMORYPOOL_PROFILER_FILE "./zakero_MemoryPool_profile.json"
424 # define ZAKERO_MEMORYPOOL__PROFILER_INIT_METADATA(output_, meta_data_) ZAKERO_PROFILER_INIT_METADATA(output_, meta_data_)
425 # define ZAKERO_MEMORYPOOL__PROFILER_DURATION(category_, name_) ZAKERO_PROFILER_DURATION(category_, name_)
426 # define ZAKERO_MEMORYPOOL__PROFILER_INSTANT(category_, name_) ZAKERO_PROFILER_INSTANT(category_, name_)
428 # define ZAKERO_MEMORYPOOL__PROFILER_INIT_METADATA(output_, meta_data_)
429 # define ZAKERO_MEMORYPOOL__PROFILER_DURATION(category_, name_)
430 # define ZAKERO_MEMORYPOOL__PROFILER_INSTANT(category_, name_)
438 class MemoryPoolErrorCategory_
439 :
public std::error_category
442 constexpr MemoryPoolErrorCategory_() noexcept
446 const char* name()
const noexcept
override
448 return "zakero.MemoryPool";
451 std::string message(
int condition)
const override
455 #define X(name_, val_, mesg_) \
456 case val_: return mesg_;
457 ZAKERO_MEMORYPOOL__ERROR_CODES
461 return "Unknown error condition";
463 } MemoryPoolErrorCategory;
476 inline off_t calculateActualSize(
const off_t size
480 const off_t mod =
static_cast<off_t
>(alignment);
481 const off_t step =
static_cast<off_t
>(alignment) + 1;
483 return ((size + mod) / step) * step;
560 , size_on_change(LambdaSize_DoNothing)
561 , on_remap(LambdaAddressMap_DoNothing)
563 , file_descriptor(-1)
564 , alignment(zakero::MemoryPool::Alignment::Bits_64)
565 , pool_can_expand(
false)
567 #if defined(ZAKERO_MEMORYPOOL_PROFILER)
568 zakero::Profiler::MetaData meta_data =
569 { {
"ZHL" ,
"Zakero_MemoryPool.h" }
570 , {
"version" ,
"0.8.0" }
586 on_remap = LambdaAddressMap_DoNothing;
590 if(memory !=
nullptr)
592 #if defined (ZAKERO_MEMORYPOOL_ZERO_ON_FREE)
593 memset(memory,
'\0', pool_size);
596 munmap(memory, pool_size);
600 close(file_descriptor);
601 file_descriptor = -1;
642 ,
const bool expandable
646 ZAKERO_MEMORYPOOL__PROFILER_DURATION(
"MemoryPool",
"init");
648 if(this->file_descriptor != -1)
650 return ZAKERO_MEMORYPOOL__ERROR(Error_Already_Initialized);
655 return ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Small);
658 if(size > MemoryPool::Size_Max)
660 return ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Large);
663 size_t pool_size = calculateActualSize(size, alignment);
665 #if defined (__linux__)
666 int fd = memfd_create(name.c_str(), 0);
668 #error Need more code...
673 return ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Create_File);
676 if(ftruncate(fd, pool_size) == -1)
678 return ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Resize_File);
681 uint8_t* memory = (uint8_t*)mmap(
nullptr
683 , PROT_READ | PROT_WRITE
684 , MAP_SHARED | MAP_NORESERVE
689 if(memory == MAP_FAILED)
693 return ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Map_File);
696 this->segment.push_back(
698 , .size = (off_t)pool_size
702 this->pool_can_expand = expandable;
703 this->alignment = alignment;
704 this->pool_size = pool_size;
705 this->memory = memory;
706 this->file_descriptor = fd;
708 return ZAKERO_MEMORYPOOL__ERROR(Error_None);
737 return file_descriptor;
786 if(lambda ==
nullptr)
788 size_on_change = LambdaSize_DoNothing;
792 size_on_change = lambda;
818 std::error_condition error;
820 return alloc(size, error);
849 , std::error_condition& error
852 ZAKERO_MEMORYPOOL__PROFILER_DURATION(
"MemoryPool",
"alloc");
858 error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Small);
862 if(size > MemoryPool::Size_Max)
864 error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Large);
868 const size_t segment_size = calculateActualSize(size, alignment);
872 std::lock_guard<std::mutex> lock(mutex);
876 if(segmentFindBestFit(segment_size, index) ==
false)
878 if(pool_can_expand ==
false)
880 error = ZAKERO_MEMORYPOOL__ERROR(Error_Out_Of_Memory);
884 if(expandToFit(segment_size, index) ==
false)
886 error = ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Resize_File);
891 error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
893 segment[index].in_use =
true;
895 if((
size_t)segment[index].size == segment_size)
897 return segment[index].offset;
900 segmentSplit(index, segment_size);
902 return segment[index].offset;
928 std::error_condition error;
930 return alloc(size, value, error);
960 , std::error_condition& error
963 off_t offset = alloc(size, error);
970 uint8_t* ptr = addressOf(offset);
972 memset(ptr, value, size);
1003 std::error_condition error;
1005 return alloc(size, value, error);
1039 , std::error_condition& error
1042 off_t offset = alloc(size);
1049 size_t count = size / 4;
1050 uint32_t* p = (uint32_t*)addressOf(offset);
1052 for(uint32_t i = 0; i < count; i++)
1084 ZAKERO_MEMORYPOOL__PROFILER_DURATION(
"MemoryPool",
"free");
1086 std::lock_guard<std::mutex> lock(mutex);
1090 if(segmentFindInUse(offset, index) ==
false)
1095 #if defined (ZAKERO_MEMORYPOOL_ZERO_ON_FREE)
1096 memset(memory + segment[index].offset
1098 , segment[index].size
1102 segment[index].in_use =
false;
1104 segmentMergeNext(index);
1105 segmentMergePrev(index);
1142 std::error_condition error;
1144 return realloc(offset, size, error);
1186 , std::error_condition& error
1189 ZAKERO_MEMORYPOOL__PROFILER_DURATION(
"MemoryPool",
"realloc");
1195 error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Small);
1199 if(size > MemoryPool::Size_Max)
1201 error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Large);
1205 const size_t segment_size = calculateActualSize(size, alignment);
1207 std::lock_guard<std::mutex> lock(mutex);
1209 size_t index_src = 0;
1211 if(segmentFindInUse(offset, index_src) ==
false)
1213 error = ZAKERO_MEMORYPOOL__ERROR(Error_Invalid_Offset);
1217 Segment& segment_src = segment[index_src];
1220 if(segment_size == (
size_t)segment_src.size)
1222 error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1227 if(segment_size < (
size_t)segment_src.size)
1229 segmentSplit(index_src, segment_size);
1231 error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1236 if(segmentExpand(index_src, segment_size) ==
true)
1238 error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1243 size_t index_dst = 0;
1245 if(segmentFindBestFit(segment_size, index_dst) ==
false)
1247 if(pool_can_expand ==
false)
1249 error = ZAKERO_MEMORYPOOL__ERROR(Error_Out_Of_Memory);
1253 if(expandToFit(segment_size, index_dst) ==
false)
1255 error = ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Resize_File);
1260 index_dst = segmentMove(index_src, segment_size, index_dst);
1262 offset = segment[index_dst].offset;
1264 error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1291 ZAKERO_MEMORYPOOL__PROFILER_DURATION(
"MemoryPool",
"addressOf");
1295 if(segmentFindInUse(offset, index) ==
false)
1300 return memory + offset;
1339 if(lambda ==
nullptr)
1341 on_remap = LambdaAddressMap_DoNothing;
1368 if(addr_map.contains(address))
1370 return addr_map.at(address);
1407 ,
size_t characters_per_line
1410 std::lock_guard<std::mutex> lock(mutex);
1412 std::string str =
"{ \"name\": \"" + name +
"\"\n"
1413 +
", \"pool_size\": " + std::to_string(pool_size) +
"\n"
1418 str +=
", \"Segment\":\n";
1419 std::string delim =
" [ ";
1420 for(
const auto& seg : segment)
1423 str +=
"{ \"offset\": " + std::to_string(seg.offset)
1424 +
", \"size\": " + std::to_string(seg.size)
1425 +
", \"in_use\": " + std::to_string(seg.in_use)
1435 int character =
'A';
1437 str +=
", \"Layout\":\n [ \"";
1438 for(
const auto& seg : segment)
1446 size_t segment_count = seg.size / bytes_per_character;
1448 while(segment_count > 0)
1450 if((count + segment_count) >= characters_per_line)
1452 size_t t = characters_per_line - count;
1454 str += std::string(t, ch) +
"\"\n , \"";
1461 str += std::string(segment_count, ch);
1463 count += segment_count;
1470 if(character ==
'Z')
1493 bool MemoryPool::expandToFit(
size_t size_increase
1497 ZAKERO_MEMORYPOOL__PROFILER_DURATION(
"MemoryPool",
"expandToFit");
1499 if(pool_can_expand ==
false)
1504 if(segment.back().in_use ==
false)
1506 size_increase -= segment.back().size;
1509 if(pool_size + size_increase > MemoryPool::Size_Max)
1514 uint8_t* old_memory = memory;
1515 const size_t old_size = pool_size;
1516 const size_t new_size = pool_size + size_increase;
1518 if(ftruncate(file_descriptor, new_size) == -1)
1523 void* new_memory = mremap(old_memory
1529 if(new_memory == MAP_FAILED)
1534 pool_size = new_size;
1536 index = segment.size();
1538 && segment[index - 1].in_use ==
false
1542 segment[index].size += (off_t)size_increase;
1547 { .offset = (off_t)old_size
1548 , .size = (off_t)size_increase
1553 size_on_change(pool_size);
1555 if(new_memory != old_memory)
1557 memory = (uint8_t*)new_memory;
1561 for(
const auto& seg : segment)
1565 uint8_t* old_addr = old_memory + seg.offset;
1566 uint8_t* new_addr = memory + seg.offset;
1568 addr_map[old_addr] = new_addr;
1589 bool MemoryPool::segmentExpand(
const size_t index
1593 ZAKERO_MEMORYPOOL__PROFILER_DURATION(
"MemoryPool::Segment",
"Expand");
1595 const size_t index_next = index + 1;
1596 if(index_next >= segment.size())
1601 Segment& segment_next = segment[index_next];
1603 if(segment_next.in_use ==
true)
1608 Segment& segment_this = segment[index];
1610 if(size >
size_t(segment_this.size + segment_next.size))
1615 const size_t size_next = size - segment_this.size;
1617 segment_this.size = size;
1621 segment.erase(std::begin(segment) + index_next);
1625 segment_next.offset += size_next;
1626 segment_next.size -= size_next;
1641 bool MemoryPool::segmentFindBestFit(
const size_t size
1645 ZAKERO_MEMORYPOOL__PROFILER_DURATION(
"MemoryPool::Segment",
"FindBestFit");
1647 for(
size_t i = 0; i < segment.size(); i++)
1649 if(segment[i].in_use ==
false
1650 && (
size_t)segment[i].size >= size
1670 bool MemoryPool::segmentFindInUse(
const off_t offset
1674 ZAKERO_MEMORYPOOL__PROFILER_DURATION(
"MemoryPool::Segment",
"FindInUse");
1676 for(
size_t i = 0; i < segment.size(); i++)
1678 if(segment[i].offset == offset
1679 && segment[i].in_use ==
true
1686 if(segment[i].offset > offset)
1706 void MemoryPool::segmentMergeNext(
const size_t index
1709 ZAKERO_MEMORYPOOL__PROFILER_DURATION(
"MemoryPool::Segment",
"MergeNext");
1711 size_t index_next = index + 1;
1713 if(index_next >= segment.size())
1718 Segment& segment_next = segment[index_next];
1720 if(segment_next.in_use ==
true)
1725 segment[index].size += segment[index_next].size;
1727 segment.erase(std::begin(segment) + index_next);
1745 void MemoryPool::segmentMergePrev(
const size_t index
1748 ZAKERO_MEMORYPOOL__PROFILER_DURATION(
"MemoryPool::Segment",
"MergePrev");
1755 const size_t index_prev = index - 1;
1757 Segment& segment_prev = segment[index_prev];
1759 if(segment_prev.in_use ==
true)
1764 segment_prev.size += segment[index].size;
1766 segment.erase(std::begin(segment) + index);
1787 size_t MemoryPool::segmentMove(
const size_t src_index
1788 ,
const size_t dst_size
1792 ZAKERO_MEMORYPOOL__PROFILER_DURATION(
"MemoryPool::Segment",
"Move");
1794 Segment& src_segment = segment[src_index];
1795 const uint8_t* src_addr = memory + src_segment.offset;
1796 const size_t src_size = src_segment.size;
1798 Segment& dst_segment = segment[dst_index];
1799 uint8_t* dst_addr = memory + dst_segment.offset;
1800 const size_t dst_offset = dst_segment.offset;
1802 dst_segment.in_use =
true;
1804 memcpy(dst_addr, src_addr, src_size);
1806 #if defined (ZAKERO_MEMORYPOOL_ZERO_ON_FREE)
1807 memset(src_addr,
'\0', src_size);
1809 src_segment.in_use =
false;
1811 if(src_index > dst_index)
1813 segmentMergeNext(src_index);
1814 segmentMergePrev(src_index);
1816 segmentSplit(dst_index, dst_size);
1820 segmentSplit(dst_index, dst_size);
1822 segmentMergeNext(src_index);
1823 segmentMergePrev(src_index);
1825 segmentFindInUse(dst_offset, dst_index);
1850 void MemoryPool::segmentSplit(
size_t index
1854 ZAKERO_MEMORYPOOL__PROFILER_DURATION(
"MemoryPool::Segment",
"Split");
1856 Segment& this_segment = segment[index];
1858 const size_t index_next = index + 1;
1859 const off_t offset_next = this_segment.offset + size;
1860 const off_t size_next = this_segment.size - size;
1862 this_segment.size = size;
1864 if(this_segment.in_use)
1866 uint8_t* addr = memory + offset_next;
1868 memset(addr,
'\0', size_next);
1871 if(index_next >= segment.size())
1874 { .offset = offset_next
1881 Segment& segment_next = segment[index_next];
1883 if(segment_next.in_use)
1885 segment.insert(std::begin(segment) + index_next,
1886 { .offset = offset_next
1893 segment_next.offset = offset_next;
1894 segment_next.size += size_next;
1904 #endif // zakero_MemoryPool_h