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;
353 #ifdef ZAKERO_MEMORYPOOL_IMPLEMENTATION
358 #ifdef ZAKERO__DOXYGEN_DEFINE_DOCS
373 #define ZAKERO_MEMORYPOOL_IMPLEMENTATION
388 #define ZAKERO_MEMORYPOOL__ERROR(err_) std::error_code(err_, MemoryPoolErrorCategory);
407 class MemoryPoolErrorCategory_
408 :
public std::error_category
411 constexpr MemoryPoolErrorCategory_() noexcept
415 const char* name()
const noexcept
override
417 return "zakero.MemoryPool";
420 std::string message(
int condition)
const override
424 #define X(name_, val_, mesg_) \
425 case val_: return mesg_;
426 ZAKERO_MEMORYPOOL__ERROR_DATA
430 return "Unknown error condition";
432 } MemoryPoolErrorCategory;
452 inline off_t calculateActualSize(
const off_t size
456 const off_t mod =
static_cast<off_t
>(alignment);
457 const off_t step =
static_cast<off_t
>(alignment) + 1;
459 return ((size + mod) / step) * step;
556 , size_on_change(LambdaSize_DoNothing)
557 , on_remap(LambdaAddressMap_DoNothing)
559 , file_descriptor(-1)
560 , alignment(zakero::MemoryPool::Alignment::Bits_64)
561 , pool_can_expand(
false)
573 on_remap = LambdaAddressMap_DoNothing;
577 if(memory !=
nullptr)
579 #ifdef ZAKERO_MEMORYPOOL_ZERO_ON_FREE
580 memset(memory,
'\0', pool_size);
583 munmap(memory, pool_size);
587 close(file_descriptor);
588 file_descriptor = -1;
629 ,
const bool expandable
635 if(this->file_descriptor != -1)
637 return ZAKERO_MEMORYPOOL__ERROR(Error_Already_Initialized);
642 return ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Small);
645 if(size > MemoryPool::Size_Max)
647 return ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Large);
650 size_t pool_size = calculateActualSize(size, alignment);
653 int fd = memfd_create(name.c_str(), 0);
655 #error Need more code...
660 return ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Create_File);
663 if(ftruncate(fd, pool_size) == -1)
665 return ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Resize_File);
668 uint8_t* memory = (uint8_t*)mmap(
nullptr
670 , PROT_READ | PROT_WRITE
671 , MAP_SHARED | MAP_NORESERVE
676 if(memory == MAP_FAILED)
680 return ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Map_File);
683 this->segment.push_back(
685 , .size = (off_t)pool_size
689 this->pool_can_expand = expandable;
690 this->alignment = alignment;
691 this->pool_size = pool_size;
692 this->memory = memory;
693 this->file_descriptor = fd;
695 return ZAKERO_MEMORYPOOL__ERROR(Error_None);
724 return file_descriptor;
773 if(lambda ==
nullptr)
775 size_on_change = LambdaSize_DoNothing;
779 size_on_change = lambda;
805 std::error_code error;
807 return alloc(size, error);
836 , std::error_code& error
845 error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Small);
849 if(size > MemoryPool::Size_Max)
851 error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Large);
855 const size_t segment_size = calculateActualSize(size, alignment);
859 std::lock_guard<std::mutex> lock(mutex);
863 if(segmentFindBestFit(segment_size, index) ==
false)
865 if(pool_can_expand ==
false)
867 error = ZAKERO_MEMORYPOOL__ERROR(Error_Out_Of_Memory);
871 if(expandToFit(segment_size, index) ==
false)
873 error = ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Resize_File);
878 error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
880 segment[index].in_use =
true;
882 if((
size_t)segment[index].size == segment_size)
884 return segment[index].offset;
887 segmentSplit(index, segment_size);
889 return segment[index].offset;
915 std::error_code error;
917 return alloc(size, value, error);
947 , std::error_code& error
950 off_t offset = alloc(size, error);
957 uint8_t* ptr = addressOf(offset);
959 memset(ptr, value, size);
990 std::error_code error;
992 return alloc(size, value, error);
1026 , std::error_code& error
1029 off_t offset = alloc(size);
1036 size_t count = size / 4;
1037 uint32_t* p = (uint32_t*)addressOf(offset);
1039 for(uint32_t i = 0; i < count; i++)
1073 std::lock_guard<std::mutex> lock(mutex);
1077 if(segmentFindInUse(offset, index) ==
false)
1082 #ifdef ZAKERO_MEMORYPOOL_ZERO_ON_FREE
1083 memset(memory + segment[index].offset
1085 , segment[index].size
1089 segment[index].in_use =
false;
1091 segmentMergeNext(index);
1092 segmentMergePrev(index);
1129 std::error_code error;
1131 return realloc(offset, size, error);
1173 , std::error_code& error
1182 error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Small);
1186 if(size > MemoryPool::Size_Max)
1188 error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Large);
1192 const size_t segment_size = calculateActualSize(size, alignment);
1194 std::lock_guard<std::mutex> lock(mutex);
1196 size_t index_src = 0;
1198 if(segmentFindInUse(offset, index_src) ==
false)
1200 error = ZAKERO_MEMORYPOOL__ERROR(Error_Invalid_Offset);
1204 Segment& segment_src = segment[index_src];
1207 if(segment_size == (
size_t)segment_src.size)
1209 error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1214 if(segment_size < (
size_t)segment_src.size)
1216 segmentSplit(index_src, segment_size);
1218 error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1223 if(segmentExpand(index_src, segment_size) ==
true)
1225 error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1230 size_t index_dst = 0;
1232 if(segmentFindBestFit(segment_size, index_dst) ==
false)
1234 if(pool_can_expand ==
false)
1236 error = ZAKERO_MEMORYPOOL__ERROR(Error_Out_Of_Memory);
1240 if(expandToFit(segment_size, index_dst) ==
false)
1242 error = ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Resize_File);
1247 index_dst = segmentMove(index_src, segment_size, index_dst);
1249 offset = segment[index_dst].offset;
1251 error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1282 if(segmentFindInUse(offset, index) ==
false)
1287 return memory + offset;
1326 if(lambda ==
nullptr)
1328 on_remap = LambdaAddressMap_DoNothing;
1355 if(addr_map.contains(address))
1357 return addr_map.at(address);
1394 ,
size_t characters_per_line
1397 std::lock_guard<std::mutex> lock(mutex);
1399 std::string str =
"{ \"name\": \"" + name +
"\"\n"
1400 +
", \"pool_size\": " + std::to_string(pool_size) +
"\n"
1405 str +=
", \"Segment\":\n";
1406 std::string delim =
" [ ";
1407 for(
const auto& seg : segment)
1410 str +=
"{ \"offset\": " + std::to_string(seg.offset)
1411 +
", \"size\": " + std::to_string(seg.size)
1412 +
", \"in_use\": " + std::to_string(seg.in_use)
1422 int character =
'A';
1424 str +=
", \"Layout\":\n [ \"";
1425 for(
const auto& seg : segment)
1433 size_t segment_count = seg.size / bytes_per_character;
1435 while(segment_count > 0)
1437 if((count + segment_count) >= characters_per_line)
1439 size_t t = characters_per_line - count;
1441 str += std::string(t, ch) +
"\"\n , \"";
1448 str += std::string(segment_count, ch);
1450 count += segment_count;
1457 if(character ==
'Z')
1480 bool MemoryPool::expandToFit(
size_t size_increase
1486 if(pool_can_expand ==
false)
1491 if(segment.back().in_use ==
false)
1493 size_increase -= segment.back().size;
1496 if(pool_size + size_increase > MemoryPool::Size_Max)
1501 uint8_t* old_memory = memory;
1502 const size_t old_size = pool_size;
1503 const size_t new_size = pool_size + size_increase;
1505 if(ftruncate(file_descriptor, new_size) == -1)
1510 void* new_memory = mremap(old_memory
1516 if(new_memory == MAP_FAILED)
1521 pool_size = new_size;
1523 index = segment.size();
1525 && segment[index - 1].in_use ==
false
1529 segment[index].size += (off_t)size_increase;
1534 { .offset = (off_t)old_size
1535 , .size = (off_t)size_increase
1540 size_on_change(pool_size);
1542 if(new_memory != old_memory)
1544 memory = (uint8_t*)new_memory;
1548 for(
const auto& seg : segment)
1552 uint8_t* old_addr = old_memory + seg.offset;
1553 uint8_t* new_addr = memory + seg.offset;
1555 addr_map[old_addr] = new_addr;
1576 bool MemoryPool::segmentExpand(
const size_t index
1582 const size_t index_next = index + 1;
1583 if(index_next >= segment.size())
1588 Segment& segment_next = segment[index_next];
1590 if(segment_next.in_use ==
true)
1595 Segment& segment_this = segment[index];
1597 if(size >
size_t(segment_this.size + segment_next.size))
1602 const size_t size_next = size - segment_this.size;
1604 segment_this.size = size;
1608 segment.erase(std::begin(segment) + index_next);
1612 segment_next.offset += size_next;
1613 segment_next.size -= size_next;
1628 bool MemoryPool::segmentFindBestFit(
const size_t size
1634 for(
size_t i = 0; i < segment.size(); i++)
1636 const Segment& seg = segment[i];
1638 if(seg.in_use ==
false && (
size_t)seg.size >= size)
1657 bool MemoryPool::segmentFindInUse(
const off_t offset
1663 for(
size_t i = 0; i < segment.size(); i++)
1665 const Segment& seg = segment[i];
1667 if(seg.offset > offset)
1672 if(seg.offset == offset && seg.in_use ==
true)
1694 void MemoryPool::segmentMergeNext(
const size_t index
1699 size_t index_next = index + 1;
1701 if(index_next >= segment.size())
1706 Segment& segment_next = segment[index_next];
1708 if(segment_next.in_use ==
true)
1713 segment[index].size += segment[index_next].size;
1715 segment.erase(std::begin(segment) + index_next);
1733 void MemoryPool::segmentMergePrev(
const size_t index
1743 const size_t index_prev = index - 1;
1745 Segment& segment_prev = segment[index_prev];
1747 if(segment_prev.in_use ==
true)
1752 segment_prev.size += segment[index].size;
1754 segment.erase(std::begin(segment) + index);
1775 size_t MemoryPool::segmentMove(
const size_t src_index
1776 ,
const size_t dst_size
1782 Segment& src_segment = segment[src_index];
1783 const uint8_t* src_addr = memory + src_segment.offset;
1784 const size_t src_size = src_segment.size;
1786 Segment& dst_segment = segment[dst_index];
1787 uint8_t* dst_addr = memory + dst_segment.offset;
1788 const size_t dst_offset = dst_segment.offset;
1790 dst_segment.in_use =
true;
1792 memcpy(dst_addr, src_addr, src_size);
1794 #ifdef ZAKERO_MEMORYPOOL_ZERO_ON_FREE
1795 memset(src_addr,
'\0', src_size);
1797 src_segment.in_use =
false;
1799 if(src_index > dst_index)
1801 segmentMergeNext(src_index);
1802 segmentMergePrev(src_index);
1804 segmentSplit(dst_index, dst_size);
1808 segmentSplit(dst_index, dst_size);
1810 segmentMergeNext(src_index);
1811 segmentMergePrev(src_index);
1813 segmentFindInUse(dst_offset, dst_index);
1838 void MemoryPool::segmentSplit(
size_t index
1844 Segment& this_segment = segment[index];
1846 const size_t index_next = index + 1;
1847 const off_t offset_next = this_segment.offset + size;
1848 const off_t size_next = this_segment.size - size;
1850 this_segment.size = size;
1852 if(this_segment.in_use)
1854 uint8_t* addr = memory + offset_next;
1856 memset(addr,
'\0', size_next);
1859 if(index_next >= segment.size())
1862 { .offset = offset_next
1869 Segment& segment_next = segment[index_next];
1871 if(segment_next.in_use)
1873 segment.insert(std::begin(segment) + index_next,
1874 { .offset = offset_next
1881 segment_next.offset = offset_next;
1882 segment_next.size += size_next;
1892 #endif // zakero_MemoryPool_h