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 
208 /******************************************************************************
209  * Includes
210  */
211 
212 #include <cstring>
213 #include <functional>
214 #include <map>
215 #include <mutex>
216 #include <system_error>
217 
218 // POSIX
219 #include <sys/mman.h>
220 #include <unistd.h>
221 
222 
223 /******************************************************************************
224  * Macros
225  */
226 
227 // {{{ Macros
228 
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." ) \
256 
257 // }}}
258 
259 namespace zakero
260 {
261  // {{{ Declaration
262 
264  {
265  public:
266 #define X(name_, val_, mesg_) \
267  static constexpr int name_ = val_;
268  ZAKERO_MEMORYPOOL__ERROR_DATA
269 #undef X
270  static constexpr size_t Size_Max = (size_t)std::numeric_limits<off_t>::max();
271 
272  enum class Alignment : uint8_t
273  { Bits_8 = 0
274  , Bits_16 = 1
275  , Bits_32 = 3
276  , Bits_64 = 7
277  , Byte_1 = Bits_8
278  , Byte_2 = Bits_16
279  , Byte_4 = Bits_32
280  , Byte_8 = Bits_64
281  };
282 
283  struct Segment
284  {
285  off_t offset;
286  off_t size;
287  bool in_use;
288  };
289 
290  using AddressMap = std::map<uint8_t*, uint8_t*>;
291 
292  using LambdaSize = std::function<void(size_t)>;
293  using LambdaAddressMap = std::function<void(const MemoryPool::AddressMap&)>;
294 
295  using VectorSegment = std::vector<Segment>;
296 
297  MemoryPool(const std::string&) noexcept;
298  ~MemoryPool() noexcept;
299 
300  std::error_code init(const size_t, const bool = false, const MemoryPool::Alignment = MemoryPool::Alignment::Bits_64) noexcept;
301  [[nodiscard]] int fd() const noexcept;
302  [[nodiscard]] size_t size() const noexcept;
303  void sizeOnChange(MemoryPool::LambdaSize) noexcept;
304 
305  [[nodiscard]] off_t alloc(const size_t) noexcept;
306  [[nodiscard]] off_t alloc(const size_t, std::error_code&) noexcept;
307  [[nodiscard]] off_t alloc(size_t, uint8_t) noexcept;
308  [[nodiscard]] off_t alloc(size_t, uint8_t, std::error_code&) noexcept;
309  [[nodiscard]] off_t alloc(size_t, uint32_t) noexcept;
310  [[nodiscard]] off_t alloc(size_t, uint32_t, std::error_code&) noexcept;
311  void free(off_t&) noexcept;
312  [[nodiscard]] off_t realloc(off_t, size_t) noexcept;
313  [[nodiscard]] off_t realloc(off_t, size_t, std::error_code&) noexcept;
314 
315  [[nodiscard]] uint8_t* addressOf(off_t) const noexcept;
316  void onRemap(MemoryPool::LambdaAddressMap) noexcept;
317  [[nodiscard]] static uint8_t* remap(const MemoryPool::AddressMap&, uint8_t*) noexcept;
318 
319  [[nodiscard]] std::string dump(size_t, size_t) const noexcept;
320  [[nodiscard]] VectorSegment segmentList() const noexcept;
321 
322  private:
323  uint8_t* memory;
324  std::string name;
325  mutable std::mutex mutex;
326  MemoryPool::VectorSegment segment;
327  MemoryPool::LambdaSize size_on_change;
328  MemoryPool::LambdaAddressMap on_remap;
329  size_t pool_size;
330  int file_descriptor;
331  MemoryPool::Alignment alignment;
332  bool pool_can_expand;
333 
334  // -------------------------------------------------- //
335 
336  [[nodiscard]] bool expandToFit(size_t, size_t&) noexcept;
337 
338  [[nodiscard]] bool segmentExpand(const size_t, const size_t) noexcept;
339  [[nodiscard]] bool segmentFindBestFit(const size_t, size_t&) noexcept;
340  bool segmentFindInUse(const off_t, size_t&) const noexcept;
341  void segmentMergeNext(const size_t) noexcept;
342  void segmentMergePrev(const size_t) noexcept;
343  [[nodiscard]] size_t segmentMove(const size_t, const size_t, size_t&) noexcept;
344  void segmentSplit(size_t, size_t) noexcept;
345 
346  // -------------------------------------------------- //
347 
348  MemoryPool(const MemoryPool&) = delete;
349  MemoryPool& operator=(const MemoryPool&) = delete;
350  };
351 
352  // }}}
353  // {{{ Convenience
354 
355  std::string to_string(const MemoryPool::VectorSegment&) noexcept;
356 
357  // }}}
358 }
359 
360 // {{{ Implementation
361 
362 #ifdef ZAKERO_MEMORYPOOL_IMPLEMENTATION
363 
364 // {{{ Macros
365 // {{{ Macros : Doxygen
366 
367 #ifdef ZAKERO__DOXYGEN_DEFINE_DOCS
368 
369 // Only used for generating Doxygen documentation
370 
382 #define ZAKERO_MEMORYPOOL_IMPLEMENTATION
383 
384 #endif
385 
386 // }}}
387 
397 #define ZAKERO_MEMORYPOOL__ERROR(err_) std::error_code(err_, MemoryPoolErrorCategory);
398 
399 // }}}
400 
401 namespace zakero
402 {
403 // {{{ Anonymous Namespace
404 
405 namespace
406 {
416  class MemoryPoolErrorCategory_
417  : public std::error_category
418  {
419  public:
420  constexpr MemoryPoolErrorCategory_() noexcept
421  {
422  }
423 
424  const char* name() const noexcept override
425  {
426  return "zakero.MemoryPool";
427  }
428 
429  std::string message(int condition) const override
430  {
431  switch(condition)
432  {
433 #define X(name_, val_, mesg_) \
434  case val_: return mesg_;
435  ZAKERO_MEMORYPOOL__ERROR_DATA
436 #undef X
437  }
438 
439  return "Unknown error condition";
440  }
441  } MemoryPoolErrorCategory;
442 
447  zakero::MemoryPool::LambdaSize LambdaSize_DoNothing = [](size_t) noexcept {};
448  zakero::MemoryPool::LambdaAddressMap LambdaAddressMap_DoNothing = [](const zakero::MemoryPool::AddressMap&) noexcept {};
461  inline off_t calculateActualSize(const off_t size
462  , const zakero::MemoryPool::Alignment alignment
463  )
464  {
465  const off_t mod = static_cast<off_t>(alignment);
466  const off_t step = static_cast<off_t>(alignment) + 1;
467 
468  return ((size + mod) / step) * step;
469  }
470 }
471 
472 // }}}
473 // {{{ Documentation
474 
545 // }}}
546 // {{{ MemoryPool
547 
560  MemoryPool::MemoryPool(const std::string& name
561  ) noexcept
562  : memory(nullptr)
563  , name(name)
564  , mutex()
565  , segment()
566  , size_on_change(LambdaSize_DoNothing)
567  , on_remap(LambdaAddressMap_DoNothing)
568  , pool_size(0)
569  , file_descriptor(-1)
570  , alignment(zakero::MemoryPool::Alignment::Bits_64)
571  , pool_can_expand(false)
572  {
573  }
574 
575 
582  {
583  on_remap = LambdaAddressMap_DoNothing;
584  segment.clear();
585  name.clear();
586 
587  if(memory != nullptr)
588  {
589  #ifdef ZAKERO_MEMORYPOOL_ZERO_ON_FREE
590  memset(memory, '\0', pool_size);
591  #endif
592 
593  munmap(memory, pool_size);
594  memory = nullptr;
595  }
596 
597  close(file_descriptor);
598  file_descriptor = -1;
599 
600  pool_size = 0;
601  }
602 
603 
639  std::error_code MemoryPool::init(const size_t size
640  , const bool expandable
641  , const MemoryPool::Alignment alignment
642  ) noexcept
643  {
644  //ZAKERO_PROFILER_DURATION("MemoryPool", "init");
645 
646  if(this->file_descriptor != -1)
647  {
648  return ZAKERO_MEMORYPOOL__ERROR(Error_Already_Initialized);
649  }
650 
651  if(size <= 0)
652  {
653  return ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Small);
654  }
655 
656  if(size > MemoryPool::Size_Max)
657  {
658  return ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Large);
659  }
660 
661  size_t pool_size = calculateActualSize(size, alignment);
662 
663  #ifdef __linux__
664  int fd = memfd_create(name.c_str(), 0);
665  #else
666  #error Need more code...
667  #endif
668 
669  if(fd == -1)
670  {
671  return ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Create_File);
672  }
673 
674  if(ftruncate(fd, pool_size) == -1)
675  {
676  return ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Resize_File);
677  }
678 
679  uint8_t* memory = (uint8_t*)mmap(nullptr
680  , pool_size
681  , PROT_READ | PROT_WRITE
682  , MAP_SHARED | MAP_NORESERVE
683  , fd
684  , 0
685  );
686 
687  if(memory == MAP_FAILED)
688  {
689  close(fd);
690 
691  return ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Map_File);
692  }
693 
694  this->segment.push_back(
695  { .offset = 0
696  , .size = (off_t)pool_size
697  , .in_use = false
698  });
699 
700  this->pool_can_expand = expandable;
701  this->alignment = alignment;
702  this->pool_size = pool_size;
703  this->memory = memory;
704  this->file_descriptor = fd;
705 
706  return ZAKERO_MEMORYPOOL__ERROR(Error_None);
707  }
708 
709 
733  int MemoryPool::fd() const noexcept
734  {
735  return file_descriptor;
736  }
737 
738 
746  size_t MemoryPool::size() const noexcept
747  {
748  return pool_size;
749  }
750 
751 
782  ) noexcept
783  {
784  if(lambda == nullptr)
785  {
786  size_on_change = LambdaSize_DoNothing;
787  }
788  else
789  {
790  size_on_change = lambda;
791  }
792  }
793 
794 
813  off_t MemoryPool::alloc(const size_t size
814  ) noexcept
815  {
816  std::error_code error;
817 
818  return alloc(size, error);
819  }
820 
821 
846  off_t MemoryPool::alloc(const size_t size
847  , std::error_code& error
848  ) noexcept
849  {
850  //ZAKERO_PROFILER_DURATION("MemoryPool", "alloc");
851 
852  // Validate Input
853 
854  if(size <= 0)
855  {
856  error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Small);
857  return -1;
858  }
859 
860  if(size > MemoryPool::Size_Max)
861  {
862  error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Large);
863  return -1;
864  }
865 
866  const size_t segment_size = calculateActualSize(size, alignment);
867 
868  // Allocate from the pool
869 
870  std::lock_guard<std::mutex> lock(mutex);
871 
872  size_t index = 0;
873 
874  if(segmentFindBestFit(segment_size, index) == false)
875  {
876  if(pool_can_expand == false)
877  {
878  error = ZAKERO_MEMORYPOOL__ERROR(Error_Out_Of_Memory);
879  return -1;
880  }
881 
882  if(expandToFit(segment_size, index) == false)
883  {
884  error = ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Resize_File);
885  return -1;
886  }
887  }
888 
889  error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
890 
891  segment[index].in_use = true;
892 
893  if((size_t)segment[index].size == segment_size)
894  {
895  return segment[index].offset;
896  }
897 
898  segmentSplit(index, segment_size);
899 
900  return segment[index].offset;
901  }
902 
903 
922  off_t MemoryPool::alloc(size_t size
923  , uint8_t value
924  ) noexcept
925  {
926  std::error_code error;
927 
928  return alloc(size, value, error);
929  }
930 
931 
956  off_t MemoryPool::alloc(size_t size
957  , uint8_t value
958  , std::error_code& error
959  ) noexcept
960  {
961  off_t offset = alloc(size, error);
962 
963  if(offset < 0)
964  {
965  return offset;
966  }
967 
968  uint8_t* ptr = addressOf(offset);
969 
970  memset(ptr, value, size);
971 
972  return offset;
973  }
974 
975 
997  off_t MemoryPool::alloc(size_t size
998  , uint32_t value
999  ) noexcept
1000  {
1001  std::error_code error;
1002 
1003  return alloc(size, value, error);
1004  }
1005 
1006 
1035  off_t MemoryPool::alloc(size_t size
1036  , const uint32_t value
1037  , std::error_code& error
1038  ) noexcept
1039  {
1040  off_t offset = alloc(size, error);
1041 
1042  if(offset < 0)
1043  {
1044  return offset;
1045  }
1046 
1047  size_t count = size / 4;
1048  uint32_t* p = (uint32_t*)addressOf(offset);
1049 
1050  for(uint32_t i = 0; i < count; i++)
1051  {
1052  p[i] = value;
1053  }
1054 
1055  return offset;
1056  }
1057 
1058 
1079  void MemoryPool::free(off_t& offset
1080  ) noexcept
1081  {
1082  //ZAKERO_PROFILER_DURATION("MemoryPool", "free");
1083 
1084  std::lock_guard<std::mutex> lock(mutex);
1085 
1086  size_t index = 0;
1087 
1088  if(segmentFindInUse(offset, index) == false)
1089  {
1090  return;
1091  }
1092 
1093  #ifdef ZAKERO_MEMORYPOOL_ZERO_ON_FREE
1094  memset(memory + segment[index].offset
1095  , '\0'
1096  , segment[index].size
1097  );
1098  #endif
1099 
1100  segment[index].in_use = false;
1101 
1102  segmentMergeNext(index);
1103  segmentMergePrev(index);
1104 
1105  offset = -1;
1106  }
1107 
1108 
1136  off_t MemoryPool::realloc(off_t offset
1137  , size_t size
1138  ) noexcept
1139  {
1140  std::error_code error;
1141 
1142  return realloc(offset, size, error);
1143  }
1144 
1145 
1182  off_t MemoryPool::realloc(off_t offset
1183  , size_t size
1184  , std::error_code& error
1185  ) noexcept
1186  {
1187  //ZAKERO_PROFILER_DURATION("MemoryPool", "realloc");
1188 
1189  // Validate Input
1190 
1191  if(size <= 0)
1192  {
1193  error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Small);
1194  return -1;
1195  }
1196 
1197  if(size > MemoryPool::Size_Max)
1198  {
1199  error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Large);
1200  return -1;
1201  }
1202 
1203  const size_t segment_size = calculateActualSize(size, alignment);
1204 
1205  std::lock_guard<std::mutex> lock(mutex);
1206 
1207  size_t index_src = 0;
1208 
1209  if(segmentFindInUse(offset, index_src) == false)
1210  {
1211  error = ZAKERO_MEMORYPOOL__ERROR(Error_Invalid_Offset);
1212  return -1;
1213  }
1214 
1215  Segment& segment_src = segment[index_src];
1216 
1217  // Same size, nothing to do
1218  if(segment_size == (size_t)segment_src.size)
1219  {
1220  error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1221  return offset;
1222  }
1223 
1224  // New size is smaller, shrink segment
1225  if(segment_size < (size_t)segment_src.size)
1226  {
1227  segmentSplit(index_src, segment_size);
1228 
1229  error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1230  return offset;
1231  }
1232 
1233  // Larger, try to expand into the next segment
1234  if(segmentExpand(index_src, segment_size) == true)
1235  {
1236  error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1237  return offset;
1238  }
1239 
1240  // Larger, find a new location
1241  size_t index_dst = 0;
1242 
1243  if(segmentFindBestFit(segment_size, index_dst) == false)
1244  {
1245  if(pool_can_expand == false)
1246  {
1247  error = ZAKERO_MEMORYPOOL__ERROR(Error_Out_Of_Memory);
1248  return -1;
1249  }
1250 
1251  if(expandToFit(segment_size, index_dst) == false)
1252  {
1253  error = ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Resize_File);
1254  return -1;
1255  }
1256  }
1257 
1258  index_dst = segmentMove(index_src, segment_size, index_dst);
1259 
1260  offset = segment[index_dst].offset;
1261 
1262  error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1263  return offset;
1264  }
1265 
1266 
1286  uint8_t* MemoryPool::addressOf(off_t offset
1287  ) const noexcept
1288  {
1289  //ZAKERO_PROFILER_DURATION("MemoryPool", "addressOf");
1290 
1291  size_t index = 0;
1292 
1293  if(segmentFindInUse(offset, index) == false)
1294  {
1295  return nullptr;
1296  }
1297 
1298  return memory + offset;
1299  }
1300 
1301 
1335  ) noexcept
1336  {
1337  if(lambda == nullptr)
1338  {
1339  on_remap = LambdaAddressMap_DoNothing;
1340  }
1341  else
1342  {
1343  on_remap = lambda;
1344  }
1345  }
1346 
1347 
1362  uint8_t* MemoryPool::remap(const MemoryPool::AddressMap& addr_map
1363  , uint8_t* address
1364  ) noexcept
1365  {
1366  if(addr_map.contains(address))
1367  {
1368  return addr_map.at(address);
1369  }
1370 
1371  return address;
1372  }
1373 
1374 
1404  std::string MemoryPool::dump(size_t bytes_per_character
1405  , size_t characters_per_line
1406  ) const noexcept
1407  {
1408  std::lock_guard<std::mutex> lock(mutex);
1409 
1410  std::string str = "{ \"name\": \"" + name + "\"\n"
1411  + ", \"pool_size\": " + std::to_string(pool_size) + "\n"
1412  ;
1413 
1414  size_t count = 0;
1415 
1416  str += ", \"Segment\":\n";
1417  std::string delim = " [ ";
1418  for(const auto& seg : segment)
1419  {
1420  str += delim;
1421  str += "{ \"offset\": " + std::to_string(seg.offset)
1422  + ", \"size\": " + std::to_string(seg.size)
1423  + ", \"in_use\": " + std::to_string(seg.in_use)
1424  + " }\n";
1425 
1426  count++;
1427  delim = " , ";
1428  }
1429  str += " ]\n";
1430 
1431  count = 0;
1432 
1433  int character = 'A';
1434 
1435  str += ", \"Layout\":\n [ \"";
1436  for(const auto& seg : segment)
1437  {
1438  int ch = '.';
1439  if(seg.in_use)
1440  {
1441  ch = character;
1442  }
1443 
1444  size_t segment_count = seg.size / bytes_per_character;
1445 
1446  while(segment_count > 0)
1447  {
1448  if((count + segment_count) >= characters_per_line)
1449  {
1450  size_t t = characters_per_line - count;
1451 
1452  str += std::string(t, ch) + "\"\n , \"";
1453 
1454  count = 0;
1455  segment_count -= t;
1456  }
1457  else
1458  {
1459  str += std::string(segment_count, ch);
1460 
1461  count += segment_count;
1462  segment_count = 0;
1463  }
1464  }
1465 
1466  if(ch != '.')
1467  {
1468  if(character == 'Z')
1469  {
1470  character = 'A';
1471  }
1472  else
1473  {
1474  character++;
1475  }
1476  }
1477  }
1478 
1479  str += "\"\n ]\n}";
1480 
1481  return str;
1482  }
1483 
1484 
1491  ) const noexcept
1492  {
1493  std::lock_guard<std::mutex> lock(mutex);
1494 
1495  return segment;
1496  }
1497 
1498 
1505  bool MemoryPool::expandToFit(size_t size_increase
1506  , size_t& index
1507  ) noexcept
1508  {
1509  //ZAKERO_PROFILER_DURATION("MemoryPool", "expandToFit");
1510 
1511  if(pool_can_expand == false)
1512  {
1513  return false;
1514  }
1515 
1516  if(segment.back().in_use == false)
1517  {
1518  size_increase -= segment.back().size;
1519  }
1520 
1521  if(pool_size + size_increase > MemoryPool::Size_Max)
1522  {
1523  return false;
1524  }
1525 
1526  uint8_t* old_memory = memory;
1527  const size_t old_size = pool_size;
1528  const size_t new_size = pool_size + size_increase;
1529 
1530  if(ftruncate(file_descriptor, new_size) == -1)
1531  {
1532  return false;
1533  }
1534 
1535  void* new_memory = mremap(old_memory
1536  , old_size
1537  , new_size
1538  , MREMAP_MAYMOVE
1539  );
1540 
1541  if(new_memory == MAP_FAILED)
1542  {
1543  return false;
1544  }
1545 
1546  pool_size = new_size;
1547 
1548  index = segment.size();
1549  if(index > 0
1550  && segment[index - 1].in_use == false
1551  )
1552  {
1553  index--;
1554  segment[index].size += (off_t)size_increase;
1555  }
1556  else
1557  {
1558  segment.push_back(
1559  { .offset = (off_t)old_size
1560  , .size = (off_t)size_increase
1561  , .in_use = false
1562  });
1563  }
1564 
1565  size_on_change(pool_size);
1566 
1567  if(new_memory != old_memory)
1568  {
1569  memory = (uint8_t*)new_memory;
1570 
1571  MemoryPool::AddressMap addr_map;
1572 
1573  for(const auto& seg : segment)
1574  {
1575  if(seg.in_use)
1576  {
1577  uint8_t* old_addr = old_memory + seg.offset;
1578  uint8_t* new_addr = memory + seg.offset;
1579 
1580  addr_map[old_addr] = new_addr;
1581  }
1582  }
1583 
1584  on_remap(addr_map);
1585  }
1586 
1587  return true;
1588  }
1589 
1590 
1601  bool MemoryPool::segmentExpand(const size_t index
1602  , const size_t size
1603  ) noexcept
1604  {
1605  //ZAKERO_PROFILER_DURATION("MemoryPool::Segment", "Expand");
1606 
1607  const size_t index_next = index + 1;
1608  if(index_next >= segment.size())
1609  {
1610  return false;
1611  }
1612 
1613  Segment& segment_next = segment[index_next];
1614 
1615  if(segment_next.in_use == true)
1616  {
1617  return false;
1618  }
1619 
1620  Segment& segment_this = segment[index];
1621 
1622  if(size > size_t(segment_this.size + segment_next.size))
1623  {
1624  return false;
1625  }
1626 
1627  const size_t size_next = size - segment_this.size;
1628 
1629  segment_this.size = size;
1630 
1631  if(size_next == 0)
1632  {
1633  segment.erase(std::begin(segment) + index_next);
1634  }
1635  else
1636  {
1637  segment_next.offset += size_next;
1638  segment_next.size -= size_next;
1639  }
1640 
1641  return true;
1642  }
1643 
1644 
1653  bool MemoryPool::segmentFindBestFit(const size_t size
1654  , size_t& index
1655  ) noexcept
1656  {
1657  //ZAKERO_PROFILER_DURATION("MemoryPool::Segment", "FindBestFit");
1658 
1659  for(size_t i = 0; i < segment.size(); i++)
1660  {
1661  const Segment& seg = segment[i];
1662 
1663  if(seg.in_use == false && (size_t)seg.size >= size)
1664  {
1665  index = i;
1666 
1667  return true;
1668  }
1669  }
1670 
1671  return false;
1672  }
1673 
1674 
1682  bool MemoryPool::segmentFindInUse(const off_t offset
1683  , size_t& index
1684  ) const noexcept
1685  {
1686  //ZAKERO_PROFILER_DURATION("MemoryPool::Segment", "FindInUse");
1687 
1688  for(size_t i = 0; i < segment.size(); i++)
1689  {
1690  const Segment& seg = segment[i];
1691 
1692  if(seg.offset > offset)
1693  {
1694  return false;
1695  }
1696 
1697  if(seg.offset == offset && seg.in_use == true)
1698  {
1699  index = i;
1700 
1701  return true;
1702  }
1703  }
1704 
1705  return false;
1706  }
1707 
1708 
1719  void MemoryPool::segmentMergeNext(const size_t index
1720  ) noexcept
1721  {
1722  //ZAKERO_PROFILER_DURATION("MemoryPool::Segment", "MergeNext");
1723 
1724  size_t index_next = index + 1;
1725 
1726  if(index_next >= segment.size())
1727  {
1728  return;
1729  }
1730 
1731  Segment& segment_next = segment[index_next];
1732 
1733  if(segment_next.in_use == true)
1734  {
1735  return;
1736  }
1737 
1738  segment[index].size += segment[index_next].size;
1739 
1740  segment.erase(std::begin(segment) + index_next);
1741  }
1742 
1743 
1758  void MemoryPool::segmentMergePrev(const size_t index
1759  ) noexcept
1760  {
1761  //ZAKERO_PROFILER_DURATION("MemoryPool::Segment", "MergePrev");
1762 
1763  if(index == 0)
1764  {
1765  return;
1766  }
1767 
1768  const size_t index_prev = index - 1;
1769 
1770  Segment& segment_prev = segment[index_prev];
1771 
1772  if(segment_prev.in_use == true)
1773  {
1774  return;
1775  }
1776 
1777  segment_prev.size += segment[index].size;
1778 
1779  segment.erase(std::begin(segment) + index);
1780  }
1781 
1782 
1800  size_t MemoryPool::segmentMove(const size_t src_index
1801  , const size_t dst_size
1802  , size_t& dst_index
1803  ) noexcept
1804  {
1805  //ZAKERO_PROFILER_DURATION("MemoryPool::Segment", "Move");
1806 
1807  Segment& src_segment = segment[src_index];
1808  const uint8_t* src_addr = memory + src_segment.offset;
1809  const size_t src_size = src_segment.size;
1810 
1811  Segment& dst_segment = segment[dst_index];
1812  uint8_t* dst_addr = memory + dst_segment.offset;
1813  const size_t dst_offset = dst_segment.offset;
1814 
1815  dst_segment.in_use = true;
1816 
1817  memcpy(dst_addr, src_addr, src_size);
1818 
1819  #ifdef ZAKERO_MEMORYPOOL_ZERO_ON_FREE
1820  memset(src_addr, '\0', src_size);
1821  #endif
1822  src_segment.in_use = false;
1823 
1824  if(src_index > dst_index)
1825  {
1826  segmentMergeNext(src_index);
1827  segmentMergePrev(src_index);
1828 
1829  segmentSplit(dst_index, dst_size);
1830  }
1831  else
1832  {
1833  segmentSplit(dst_index, dst_size);
1834 
1835  segmentMergeNext(src_index);
1836  segmentMergePrev(src_index);
1837 
1838  segmentFindInUse(dst_offset, dst_index);
1839  }
1840 
1841  return dst_index;
1842  }
1843 
1844 
1863  void MemoryPool::segmentSplit(size_t index
1864  , size_t size
1865  ) noexcept
1866  {
1867  //ZAKERO_PROFILER_DURATION("MemoryPool::Segment", "Split");
1868 
1869  Segment& this_segment = segment[index];
1870 
1871  const size_t index_next = index + 1;
1872  const off_t offset_next = this_segment.offset + size;
1873  const off_t size_next = this_segment.size - size;
1874 
1875  this_segment.size = size;
1876 
1877  if(this_segment.in_use)
1878  {
1879  uint8_t* addr = memory + offset_next;
1880 
1881  memset(addr, '\0', size_next);
1882  }
1883 
1884  if(index_next >= segment.size())
1885  {
1886  segment.push_back(
1887  { .offset = offset_next
1888  , .size = size_next
1889  , .in_use = false
1890  });
1891  }
1892  else
1893  {
1894  Segment& segment_next = segment[index_next];
1895 
1896  if(segment_next.in_use)
1897  {
1898  segment.insert(std::begin(segment) + index_next,
1899  { .offset = offset_next
1900  , .size = size_next
1901  , .in_use = false
1902  });
1903  }
1904  else // Not in use
1905  {
1906  segment_next.offset = offset_next;
1907  segment_next.size += size_next;
1908  }
1909  }
1910  }
1911 
1912 // }}}
1913 // {{{ Convenience
1914 
1920  std::string to_string(const MemoryPool::VectorSegment& segment
1921  ) noexcept
1922  {
1923  std::string str = "";
1924 
1925  std::string delim = "[ ";
1926  for(const auto& seg : segment)
1927  {
1928  str += delim;
1929  str += "{ \"offset\": " + std::to_string(seg.offset)
1930  + ", \"size\": " + std::to_string(seg.size)
1931  + ", \"in_use\": " + std::to_string(seg.in_use)
1932  + " }\n";
1933 
1934  delim = ", ";
1935  }
1936  str += "]\n";
1937 
1938  return str;
1939  }
1940 
1941 // }}}
1942 
1943 }
1944 
1945 #endif // ZAKERO_MEMORYPOOL_IMPLEMENTATION
1946 
1947 // }}}
1948 
1949 #endif // zakero_MemoryPool_h
std::string to_string(const bool value) noexcept
Convert a bool into a string.
Definition: Zakero_Base.h:534
A pool of memory.
Definition: Zakero_MemoryPool.h:264
std::function< void(size_t)> LambdaSize
A lambda that receives a size_t argument.
Definition: Zakero_MemoryPool.h:292
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:639
std::string dump(size_t, size_t) const noexcept
Output the internal state.
Definition: Zakero_MemoryPool.h:1404
off_t size
The size of the allocated memory chunk.
Definition: Zakero_MemoryPool.h:286
std::vector< Segment > VectorSegment
A convenience data type.
Definition: Zakero_MemoryPool.h:295
void free(off_t &) noexcept
Free allocated memory.
Definition: Zakero_MemoryPool.h:1079
~MemoryPool() noexcept
Destructor.
Definition: Zakero_MemoryPool.h:581
std::map< uint8_t *, uint8_t * > AddressMap
A mapping of old addresses to new addresses.
Definition: Zakero_MemoryPool.h:290
bool in_use
A flag used to determine if the memory chunk is in use.
Definition: Zakero_MemoryPool.h:287
Alignment
The Byte-Alignment of the MemoryPool.
Definition: Zakero_MemoryPool.h:273
off_t realloc(off_t, size_t) noexcept
Change the size of allocated memory.
Definition: Zakero_MemoryPool.h:1136
size_t size() const noexcept
The size of the memory pool.
Definition: Zakero_MemoryPool.h:746
int fd() const noexcept
The backing file descriptor.
Definition: Zakero_MemoryPool.h:733
uint8_t * addressOf(off_t) const noexcept
Convert an offset into a pointer.
Definition: Zakero_MemoryPool.h:1286
off_t alloc(const size_t) noexcept
Allocate memory from the pool.
Definition: Zakero_MemoryPool.h:813
off_t offset
The offset into the allocated memory pool.
Definition: Zakero_MemoryPool.h:285
void sizeOnChange(MemoryPool::LambdaSize) noexcept
Set the Size Event callback.
Definition: Zakero_MemoryPool.h:781
static uint8_t * remap(const MemoryPool::AddressMap &, uint8_t *) noexcept
Get the new memory address.
Definition: Zakero_MemoryPool.h:1362
void onRemap(MemoryPool::LambdaAddressMap) noexcept
Set the Remap Event callback.
Definition: Zakero_MemoryPool.h:1334
MemoryPool(const std::string &) noexcept
Constructor.
Definition: Zakero_MemoryPool.h:560
std::function< void(const MemoryPool::AddressMap &)> LambdaAddressMap
A lambda that receives a MemoryPool::AddressMap.
Definition: Zakero_MemoryPool.h:293
VectorSegment segmentList() const noexcept
Information about the current internal state.
Definition: Zakero_MemoryPool.h:1490
Data that defines a segment.
Definition: Zakero_MemoryPool.h:284