Zakero's C++ Header Libraries
A collection of reusable C++ libraries
Zakero_MemoryPool.h
Go to the documentation of this file.
1 /******************************************************************************
2  * Copyright 2020 Andrew Moore
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7  */
8 
9 #ifndef zakero_MemoryPool_h
10 #define zakero_MemoryPool_h
11 
217 /******************************************************************************
218  * Includes
219  */
220 
221 #include <cstring>
222 #include <functional>
223 #include <map>
224 #include <mutex>
225 #include <system_error>
226 
227 // POSIX
228 #include <sys/mman.h>
229 #include <unistd.h>
230 
231 
232 /******************************************************************************
233  * Macros
234  */
235 
236 // {{{ Macros
237 
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." ) \
265 
266 // }}}
267 
268 namespace zakero
269 {
270  // {{{ Declaration
271 
273  {
274  public:
275 #define X(name_, val_, mesg_) \
276  static constexpr int name_ = val_;
277  ZAKERO_MEMORYPOOL__ERROR_DATA
278 #undef X
279  static constexpr size_t Size_Max = (size_t)std::numeric_limits<off_t>::max();
280 
281  enum class Alignment : uint8_t
282  { Bits_8 = 0
283  , Bits_16 = 1
284  , Bits_32 = 3
285  , Bits_64 = 7
286  , Byte_1 = Bits_8
287  , Byte_2 = Bits_16
288  , Byte_4 = Bits_32
289  , Byte_8 = Bits_64
290  };
291 
292  struct Segment
293  {
294  off_t offset;
295  off_t size;
296  bool in_use;
297  };
298 
299  using AddressMap = std::map<uint8_t*, uint8_t*>;
300 
301  using LambdaSize = std::function<void(size_t)>;
302  using LambdaAddressMap = std::function<void(const MemoryPool::AddressMap&)>;
303 
304  using VectorSegment = std::vector<Segment>;
305 
306  MemoryPool(const std::string&) noexcept;
307  ~MemoryPool() noexcept;
308 
309  std::error_code init(const size_t, const bool = false, const MemoryPool::Alignment = MemoryPool::Alignment::Bits_64) noexcept;
310  [[nodiscard]] int fd() const noexcept;
311  [[nodiscard]] size_t size() const noexcept;
312  void sizeOnChange(MemoryPool::LambdaSize) noexcept;
313 
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;
323 
324  [[nodiscard]] uint8_t* addressOf(off_t) const noexcept;
325  void onRemap(MemoryPool::LambdaAddressMap) noexcept;
326  [[nodiscard]] static uint8_t* remap(const MemoryPool::AddressMap&, uint8_t*) noexcept;
327 
328  [[nodiscard]] std::string dump(size_t, size_t) const noexcept;
329  [[nodiscard]] VectorSegment segmentList() const noexcept;
330 
331  private:
332  uint8_t* memory;
333  std::string name;
334  mutable std::mutex mutex;
335  MemoryPool::VectorSegment segment;
336  MemoryPool::LambdaSize size_on_change;
337  MemoryPool::LambdaAddressMap on_remap;
338  size_t pool_size;
339  int file_descriptor;
340  MemoryPool::Alignment alignment;
341  bool pool_can_expand;
342 
343  // -------------------------------------------------- //
344 
345  [[nodiscard]] bool expandToFit(size_t, size_t&) noexcept;
346 
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;
354 
355  // -------------------------------------------------- //
356 
357  MemoryPool(const MemoryPool&) = delete;
358  MemoryPool& operator=(const MemoryPool&) = delete;
359  };
360 
361  // }}}
362  // {{{ Convenience
363 
364  std::string to_string(const MemoryPool::VectorSegment&) noexcept;
365 
366  // }}}
367 }
368 
369 // {{{ Implementation
370 
371 #ifdef ZAKERO_MEMORYPOOL_IMPLEMENTATION
372 
373 // {{{ Macros
374 // {{{ Macros : Doxygen
375 
376 #ifdef ZAKERO__DOXYGEN_DEFINE_DOCS
377 
378 // Only used for generating Doxygen documentation
379 
391 #define ZAKERO_MEMORYPOOL_IMPLEMENTATION
392 
393 #endif
394 
395 // }}}
396 
406 #define ZAKERO_MEMORYPOOL__ERROR(err_) std::error_code(err_, MemoryPoolErrorCategory);
407 
408 // }}}
409 
410 namespace zakero
411 {
412 // {{{ Anonymous Namespace
413 
414 namespace
415 {
425  class MemoryPoolErrorCategory_
426  : public std::error_category
427  {
428  public:
429  constexpr MemoryPoolErrorCategory_() noexcept
430  {
431  }
432 
433  const char* name() const noexcept override
434  {
435  return "zakero.MemoryPool";
436  }
437 
438  std::string message(int condition) const override
439  {
440  switch(condition)
441  {
442 #define X(name_, val_, mesg_) \
443  case val_: return mesg_;
444  ZAKERO_MEMORYPOOL__ERROR_DATA
445 #undef X
446  }
447 
448  return "Unknown error condition";
449  }
450  } MemoryPoolErrorCategory;
451 
456  zakero::MemoryPool::LambdaSize LambdaSize_DoNothing = [](size_t) noexcept {};
457  zakero::MemoryPool::LambdaAddressMap LambdaAddressMap_DoNothing = [](const zakero::MemoryPool::AddressMap&) noexcept {};
470  inline off_t calculateActualSize(const off_t size
471  , const zakero::MemoryPool::Alignment alignment
472  )
473  {
474  const off_t mod = static_cast<off_t>(alignment);
475  const off_t step = static_cast<off_t>(alignment) + 1;
476 
477  return ((size + mod) / step) * step;
478  }
479 }
480 
481 // }}}
482 // {{{ Documentation
483 
554 // }}}
555 // {{{ MemoryPool
556 
569  MemoryPool::MemoryPool(const std::string& name
570  ) noexcept
571  : memory(nullptr)
572  , name(name)
573  , mutex()
574  , segment()
575  , size_on_change(LambdaSize_DoNothing)
576  , on_remap(LambdaAddressMap_DoNothing)
577  , pool_size(0)
578  , file_descriptor(-1)
579  , alignment(zakero::MemoryPool::Alignment::Bits_64)
580  , pool_can_expand(false)
581  {
582  }
583 
584 
591  {
592  on_remap = LambdaAddressMap_DoNothing;
593  segment.clear();
594  name.clear();
595 
596  if(memory != nullptr)
597  {
598  #ifdef ZAKERO_MEMORYPOOL_ZERO_ON_FREE
599  memset(memory, '\0', pool_size);
600  #endif
601 
602  munmap(memory, pool_size);
603  memory = nullptr;
604  }
605 
606  close(file_descriptor);
607  file_descriptor = -1;
608 
609  pool_size = 0;
610  }
611 
612 
648  std::error_code MemoryPool::init(const size_t size
649  , const bool expandable
650  , const MemoryPool::Alignment alignment
651  ) noexcept
652  {
653  //ZAKERO_PROFILER_DURATION("MemoryPool", "init");
654 
655  if(this->file_descriptor != -1)
656  {
657  return ZAKERO_MEMORYPOOL__ERROR(Error_Already_Initialized);
658  }
659 
660  if(size <= 0)
661  {
662  return ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Small);
663  }
664 
665  if(size > MemoryPool::Size_Max)
666  {
667  return ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Large);
668  }
669 
670  size_t pool_size = calculateActualSize(size, alignment);
671 
672  #ifdef __linux__
673  int fd = memfd_create(name.c_str(), 0);
674  #else
675  #error Need more code...
676  #endif
677 
678  if(fd == -1)
679  {
680  return ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Create_File);
681  }
682 
683  if(ftruncate(fd, pool_size) == -1)
684  {
685  return ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Resize_File);
686  }
687 
688  uint8_t* memory = (uint8_t*)mmap(nullptr
689  , pool_size
690  , PROT_READ | PROT_WRITE
691  , MAP_SHARED | MAP_NORESERVE
692  , fd
693  , 0
694  );
695 
696  if(memory == MAP_FAILED)
697  {
698  close(fd);
699 
700  return ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Map_File);
701  }
702 
703  this->segment.push_back(
704  { .offset = 0
705  , .size = (off_t)pool_size
706  , .in_use = false
707  });
708 
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;
714 
715  return ZAKERO_MEMORYPOOL__ERROR(Error_None);
716  }
717 
718 
742  int MemoryPool::fd() const noexcept
743  {
744  return file_descriptor;
745  }
746 
747 
755  size_t MemoryPool::size() const noexcept
756  {
757  return pool_size;
758  }
759 
760 
791  ) noexcept
792  {
793  if(lambda == nullptr)
794  {
795  size_on_change = LambdaSize_DoNothing;
796  }
797  else
798  {
799  size_on_change = lambda;
800  }
801  }
802 
803 
822  off_t MemoryPool::alloc(const size_t size
823  ) noexcept
824  {
825  std::error_code error;
826 
827  return alloc(size, error);
828  }
829 
830 
855  off_t MemoryPool::alloc(const size_t size
856  , std::error_code& error
857  ) noexcept
858  {
859  //ZAKERO_PROFILER_DURATION("MemoryPool", "alloc");
860 
861  // Validate Input
862 
863  if(size <= 0)
864  {
865  error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Small);
866  return -1;
867  }
868 
869  if(size > MemoryPool::Size_Max)
870  {
871  error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Large);
872  return -1;
873  }
874 
875  const size_t segment_size = calculateActualSize(size, alignment);
876 
877  // Allocate from the pool
878 
879  std::lock_guard<std::mutex> lock(mutex);
880 
881  size_t index = 0;
882 
883  if(segmentFindBestFit(segment_size, index) == false)
884  {
885  if(pool_can_expand == false)
886  {
887  error = ZAKERO_MEMORYPOOL__ERROR(Error_Out_Of_Memory);
888  return -1;
889  }
890 
891  if(expandToFit(segment_size, index) == false)
892  {
893  error = ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Resize_File);
894  return -1;
895  }
896  }
897 
898  error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
899 
900  segment[index].in_use = true;
901 
902  if((size_t)segment[index].size == segment_size)
903  {
904  return segment[index].offset;
905  }
906 
907  segmentSplit(index, segment_size);
908 
909  return segment[index].offset;
910  }
911 
912 
931  off_t MemoryPool::alloc(size_t size
932  , uint8_t value
933  ) noexcept
934  {
935  std::error_code error;
936 
937  return alloc(size, value, error);
938  }
939 
940 
965  off_t MemoryPool::alloc(size_t size
966  , uint8_t value
967  , std::error_code& error
968  ) noexcept
969  {
970  off_t offset = alloc(size, error);
971 
972  if(offset < 0)
973  {
974  return offset;
975  }
976 
977  uint8_t* ptr = addressOf(offset);
978 
979  memset(ptr, value, size);
980 
981  return offset;
982  }
983 
984 
1006  off_t MemoryPool::alloc(size_t size
1007  , uint32_t value
1008  ) noexcept
1009  {
1010  std::error_code error;
1011 
1012  return alloc(size, value, error);
1013  }
1014 
1015 
1044  off_t MemoryPool::alloc(size_t size
1045  , const uint32_t value
1046  , std::error_code& error
1047  ) noexcept
1048  {
1049  off_t offset = alloc(size, error);
1050 
1051  if(offset < 0)
1052  {
1053  return offset;
1054  }
1055 
1056  size_t count = size / 4;
1057  uint32_t* p = (uint32_t*)addressOf(offset);
1058 
1059  for(uint32_t i = 0; i < count; i++)
1060  {
1061  p[i] = value;
1062  }
1063 
1064  return offset;
1065  }
1066 
1067 
1088  void MemoryPool::free(off_t& offset
1089  ) noexcept
1090  {
1091  //ZAKERO_PROFILER_DURATION("MemoryPool", "free");
1092 
1093  std::lock_guard<std::mutex> lock(mutex);
1094 
1095  size_t index = 0;
1096 
1097  if(segmentFindInUse(offset, index) == false)
1098  {
1099  return;
1100  }
1101 
1102  #ifdef ZAKERO_MEMORYPOOL_ZERO_ON_FREE
1103  memset(memory + segment[index].offset
1104  , '\0'
1105  , segment[index].size
1106  );
1107  #endif
1108 
1109  segment[index].in_use = false;
1110 
1111  segmentMergeNext(index);
1112  segmentMergePrev(index);
1113 
1114  offset = -1;
1115  }
1116 
1117 
1145  off_t MemoryPool::realloc(off_t offset
1146  , size_t size
1147  ) noexcept
1148  {
1149  std::error_code error;
1150 
1151  return realloc(offset, size, error);
1152  }
1153 
1154 
1191  off_t MemoryPool::realloc(off_t offset
1192  , size_t size
1193  , std::error_code& error
1194  ) noexcept
1195  {
1196  //ZAKERO_PROFILER_DURATION("MemoryPool", "realloc");
1197 
1198  // Validate Input
1199 
1200  if(size <= 0)
1201  {
1202  error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Small);
1203  return -1;
1204  }
1205 
1206  if(size > MemoryPool::Size_Max)
1207  {
1208  error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Large);
1209  return -1;
1210  }
1211 
1212  const size_t segment_size = calculateActualSize(size, alignment);
1213 
1214  std::lock_guard<std::mutex> lock(mutex);
1215 
1216  size_t index_src = 0;
1217 
1218  if(segmentFindInUse(offset, index_src) == false)
1219  {
1220  error = ZAKERO_MEMORYPOOL__ERROR(Error_Invalid_Offset);
1221  return -1;
1222  }
1223 
1224  Segment& segment_src = segment[index_src];
1225 
1226  // Same size, nothing to do
1227  if(segment_size == (size_t)segment_src.size)
1228  {
1229  error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1230  return offset;
1231  }
1232 
1233  // New size is smaller, shrink segment
1234  if(segment_size < (size_t)segment_src.size)
1235  {
1236  segmentSplit(index_src, segment_size);
1237 
1238  error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1239  return offset;
1240  }
1241 
1242  // Larger, try to expand into the next segment
1243  if(segmentExpand(index_src, segment_size) == true)
1244  {
1245  error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1246  return offset;
1247  }
1248 
1249  // Larger, find a new location
1250  size_t index_dst = 0;
1251 
1252  if(segmentFindBestFit(segment_size, index_dst) == false)
1253  {
1254  if(pool_can_expand == false)
1255  {
1256  error = ZAKERO_MEMORYPOOL__ERROR(Error_Out_Of_Memory);
1257  return -1;
1258  }
1259 
1260  if(expandToFit(segment_size, index_dst) == false)
1261  {
1262  error = ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Resize_File);
1263  return -1;
1264  }
1265  }
1266 
1267  index_dst = segmentMove(index_src, segment_size, index_dst);
1268 
1269  offset = segment[index_dst].offset;
1270 
1271  error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1272  return offset;
1273  }
1274 
1275 
1295  uint8_t* MemoryPool::addressOf(off_t offset
1296  ) const noexcept
1297  {
1298  //ZAKERO_PROFILER_DURATION("MemoryPool", "addressOf");
1299 
1300  size_t index = 0;
1301 
1302  if(segmentFindInUse(offset, index) == false)
1303  {
1304  return nullptr;
1305  }
1306 
1307  return memory + offset;
1308  }
1309 
1310 
1344  ) noexcept
1345  {
1346  if(lambda == nullptr)
1347  {
1348  on_remap = LambdaAddressMap_DoNothing;
1349  }
1350  else
1351  {
1352  on_remap = lambda;
1353  }
1354  }
1355 
1356 
1371  uint8_t* MemoryPool::remap(const MemoryPool::AddressMap& addr_map
1372  , uint8_t* address
1373  ) noexcept
1374  {
1375  if(addr_map.contains(address))
1376  {
1377  return addr_map.at(address);
1378  }
1379 
1380  return address;
1381  }
1382 
1383 
1413  std::string MemoryPool::dump(size_t bytes_per_character
1414  , size_t characters_per_line
1415  ) const noexcept
1416  {
1417  std::lock_guard<std::mutex> lock(mutex);
1418 
1419  std::string str = "{ \"name\": \"" + name + "\"\n"
1420  + ", \"pool_size\": " + std::to_string(pool_size) + "\n"
1421  ;
1422 
1423  size_t count = 0;
1424 
1425  str += ", \"Segment\":\n";
1426  std::string delim = " [ ";
1427  for(const auto& seg : segment)
1428  {
1429  str += delim;
1430  str += "{ \"offset\": " + std::to_string(seg.offset)
1431  + ", \"size\": " + std::to_string(seg.size)
1432  + ", \"in_use\": " + std::to_string(seg.in_use)
1433  + " }\n";
1434 
1435  count++;
1436  delim = " , ";
1437  }
1438  str += " ]\n";
1439 
1440  count = 0;
1441 
1442  int character = 'A';
1443 
1444  str += ", \"Layout\":\n [ \"";
1445  for(const auto& seg : segment)
1446  {
1447  int ch = '.';
1448  if(seg.in_use)
1449  {
1450  ch = character;
1451  }
1452 
1453  size_t segment_count = seg.size / bytes_per_character;
1454 
1455  while(segment_count > 0)
1456  {
1457  if((count + segment_count) >= characters_per_line)
1458  {
1459  size_t t = characters_per_line - count;
1460 
1461  str += std::string(t, ch) + "\"\n , \"";
1462 
1463  count = 0;
1464  segment_count -= t;
1465  }
1466  else
1467  {
1468  str += std::string(segment_count, ch);
1469 
1470  count += segment_count;
1471  segment_count = 0;
1472  }
1473  }
1474 
1475  if(ch != '.')
1476  {
1477  if(character == 'Z')
1478  {
1479  character = 'A';
1480  }
1481  else
1482  {
1483  character++;
1484  }
1485  }
1486  }
1487 
1488  str += "\"\n ]\n}";
1489 
1490  return str;
1491  }
1492 
1493 
1500  ) const noexcept
1501  {
1502  std::lock_guard<std::mutex> lock(mutex);
1503 
1504  return segment;
1505  }
1506 
1507 
1514  bool MemoryPool::expandToFit(size_t size_increase
1515  , size_t& index
1516  ) noexcept
1517  {
1518  //ZAKERO_PROFILER_DURATION("MemoryPool", "expandToFit");
1519 
1520  if(pool_can_expand == false)
1521  {
1522  return false;
1523  }
1524 
1525  if(segment.back().in_use == false)
1526  {
1527  size_increase -= segment.back().size;
1528  }
1529 
1530  if(pool_size + size_increase > MemoryPool::Size_Max)
1531  {
1532  return false;
1533  }
1534 
1535  uint8_t* old_memory = memory;
1536  const size_t old_size = pool_size;
1537  const size_t new_size = pool_size + size_increase;
1538 
1539  if(ftruncate(file_descriptor, new_size) == -1)
1540  {
1541  return false;
1542  }
1543 
1544  void* new_memory = mremap(old_memory
1545  , old_size
1546  , new_size
1547  , MREMAP_MAYMOVE
1548  );
1549 
1550  if(new_memory == MAP_FAILED)
1551  {
1552  return false;
1553  }
1554 
1555  pool_size = new_size;
1556 
1557  index = segment.size();
1558  if(index > 0
1559  && segment[index - 1].in_use == false
1560  )
1561  {
1562  index--;
1563  segment[index].size += (off_t)size_increase;
1564  }
1565  else
1566  {
1567  segment.push_back(
1568  { .offset = (off_t)old_size
1569  , .size = (off_t)size_increase
1570  , .in_use = false
1571  });
1572  }
1573 
1574  size_on_change(pool_size);
1575 
1576  if(new_memory != old_memory)
1577  {
1578  memory = (uint8_t*)new_memory;
1579 
1580  MemoryPool::AddressMap addr_map;
1581 
1582  for(const auto& seg : segment)
1583  {
1584  if(seg.in_use)
1585  {
1586  uint8_t* old_addr = old_memory + seg.offset;
1587  uint8_t* new_addr = memory + seg.offset;
1588 
1589  addr_map[old_addr] = new_addr;
1590  }
1591  }
1592 
1593  on_remap(addr_map);
1594  }
1595 
1596  return true;
1597  }
1598 
1599 
1610  bool MemoryPool::segmentExpand(const size_t index
1611  , const size_t size
1612  ) noexcept
1613  {
1614  //ZAKERO_PROFILER_DURATION("MemoryPool::Segment", "Expand");
1615 
1616  const size_t index_next = index + 1;
1617  if(index_next >= segment.size())
1618  {
1619  return false;
1620  }
1621 
1622  Segment& segment_next = segment[index_next];
1623 
1624  if(segment_next.in_use == true)
1625  {
1626  return false;
1627  }
1628 
1629  Segment& segment_this = segment[index];
1630 
1631  if(size > size_t(segment_this.size + segment_next.size))
1632  {
1633  return false;
1634  }
1635 
1636  const size_t size_next = size - segment_this.size;
1637 
1638  segment_this.size = size;
1639 
1640  if(size_next == 0)
1641  {
1642  segment.erase(std::begin(segment) + index_next);
1643  }
1644  else
1645  {
1646  segment_next.offset += size_next;
1647  segment_next.size -= size_next;
1648  }
1649 
1650  return true;
1651  }
1652 
1653 
1662  bool MemoryPool::segmentFindBestFit(const size_t size
1663  , size_t& index
1664  ) noexcept
1665  {
1666  //ZAKERO_PROFILER_DURATION("MemoryPool::Segment", "FindBestFit");
1667 
1668  for(size_t i = 0; i < segment.size(); i++)
1669  {
1670  const Segment& seg = segment[i];
1671 
1672  if(seg.in_use == false && (size_t)seg.size >= size)
1673  {
1674  index = i;
1675 
1676  return true;
1677  }
1678  }
1679 
1680  return false;
1681  }
1682 
1683 
1691  bool MemoryPool::segmentFindInUse(const off_t offset
1692  , size_t& index
1693  ) const noexcept
1694  {
1695  //ZAKERO_PROFILER_DURATION("MemoryPool::Segment", "FindInUse");
1696 
1697  for(size_t i = 0; i < segment.size(); i++)
1698  {
1699  const Segment& seg = segment[i];
1700 
1701  if(seg.offset > offset)
1702  {
1703  return false;
1704  }
1705 
1706  if(seg.offset == offset && seg.in_use == true)
1707  {
1708  index = i;
1709 
1710  return true;
1711  }
1712  }
1713 
1714  return false;
1715  }
1716 
1717 
1728  void MemoryPool::segmentMergeNext(const size_t index
1729  ) noexcept
1730  {
1731  //ZAKERO_PROFILER_DURATION("MemoryPool::Segment", "MergeNext");
1732 
1733  size_t index_next = index + 1;
1734 
1735  if(index_next >= segment.size())
1736  {
1737  return;
1738  }
1739 
1740  Segment& segment_next = segment[index_next];
1741 
1742  if(segment_next.in_use == true)
1743  {
1744  return;
1745  }
1746 
1747  segment[index].size += segment[index_next].size;
1748 
1749  segment.erase(std::begin(segment) + index_next);
1750  }
1751 
1752 
1767  void MemoryPool::segmentMergePrev(const size_t index
1768  ) noexcept
1769  {
1770  //ZAKERO_PROFILER_DURATION("MemoryPool::Segment", "MergePrev");
1771 
1772  if(index == 0)
1773  {
1774  return;
1775  }
1776 
1777  const size_t index_prev = index - 1;
1778 
1779  Segment& segment_prev = segment[index_prev];
1780 
1781  if(segment_prev.in_use == true)
1782  {
1783  return;
1784  }
1785 
1786  segment_prev.size += segment[index].size;
1787 
1788  segment.erase(std::begin(segment) + index);
1789  }
1790 
1791 
1809  size_t MemoryPool::segmentMove(const size_t src_index
1810  , const size_t dst_size
1811  , size_t& dst_index
1812  ) noexcept
1813  {
1814  //ZAKERO_PROFILER_DURATION("MemoryPool::Segment", "Move");
1815 
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;
1819 
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;
1823 
1824  dst_segment.in_use = true;
1825 
1826  memcpy(dst_addr, src_addr, src_size);
1827 
1828  #ifdef ZAKERO_MEMORYPOOL_ZERO_ON_FREE
1829  memset(src_addr, '\0', src_size);
1830  #endif
1831  src_segment.in_use = false;
1832 
1833  if(src_index > dst_index)
1834  {
1835  segmentMergeNext(src_index);
1836  segmentMergePrev(src_index);
1837 
1838  segmentSplit(dst_index, dst_size);
1839  }
1840  else
1841  {
1842  segmentSplit(dst_index, dst_size);
1843 
1844  segmentMergeNext(src_index);
1845  segmentMergePrev(src_index);
1846 
1847  segmentFindInUse(dst_offset, dst_index);
1848  }
1849 
1850  return dst_index;
1851  }
1852 
1853 
1872  void MemoryPool::segmentSplit(size_t index
1873  , size_t size
1874  ) noexcept
1875  {
1876  //ZAKERO_PROFILER_DURATION("MemoryPool::Segment", "Split");
1877 
1878  Segment& this_segment = segment[index];
1879 
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;
1883 
1884  this_segment.size = size;
1885 
1886  if(this_segment.in_use)
1887  {
1888  uint8_t* addr = memory + offset_next;
1889 
1890  memset(addr, '\0', size_next);
1891  }
1892 
1893  if(index_next >= segment.size())
1894  {
1895  segment.push_back(
1896  { .offset = offset_next
1897  , .size = size_next
1898  , .in_use = false
1899  });
1900  }
1901  else
1902  {
1903  Segment& segment_next = segment[index_next];
1904 
1905  if(segment_next.in_use)
1906  {
1907  segment.insert(std::begin(segment) + index_next,
1908  { .offset = offset_next
1909  , .size = size_next
1910  , .in_use = false
1911  });
1912  }
1913  else // Not in use
1914  {
1915  segment_next.offset = offset_next;
1916  segment_next.size += size_next;
1917  }
1918  }
1919  }
1920 
1921 // }}}
1922 // {{{ Convenience
1923 
1929  std::string to_string(const MemoryPool::VectorSegment& segment
1930  ) noexcept
1931  {
1932  std::string str = "";
1933 
1934  std::string delim = "[ ";
1935  for(const auto& seg : segment)
1936  {
1937  str += delim;
1938  str += "{ \"offset\": " + std::to_string(seg.offset)
1939  + ", \"size\": " + std::to_string(seg.size)
1940  + ", \"in_use\": " + std::to_string(seg.in_use)
1941  + " }\n";
1942 
1943  delim = ", ";
1944  }
1945  str += "]\n";
1946 
1947  return str;
1948  }
1949 
1950 // }}}
1951 
1952 }
1953 
1954 #endif // ZAKERO_MEMORYPOOL_IMPLEMENTATION
1955 
1956 // }}}
1957 
1958 #endif // zakero_MemoryPool_h
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