Zakero's C++ Header Libraries
A collection of reusable C++ libraries
Zakero_MemoryPool.h
Go to the documentation of this file.
1 /******************************************************************************
2  * Copyright 2010-2019 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  using AddressMap = std::map<uint8_t*, uint8_t*>;
284 
285  using LambdaSize = std::function<void(size_t)>;
286  using LambdaAddressMap = std::function<void(const MemoryPool::AddressMap&)>;
287 
288  MemoryPool(const std::string&) noexcept;
289  ~MemoryPool() noexcept;
290 
291  std::error_code init(const size_t, const bool = false, const MemoryPool::Alignment = MemoryPool::Alignment::Bits_64) noexcept;
292  [[nodiscard]] int fd() const noexcept;
293  [[nodiscard]] size_t size() const noexcept;
294  void sizeOnChange(MemoryPool::LambdaSize) noexcept;
295 
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;
305 
306  [[nodiscard]] uint8_t* addressOf(off_t) const noexcept;
307  void onRemap(MemoryPool::LambdaAddressMap) noexcept;
308  [[nodiscard]] static uint8_t* remap(const MemoryPool::AddressMap&, uint8_t*) noexcept;
309 
310  [[nodiscard]] std::string dump(size_t, size_t) const noexcept;
311 
312  private:
313  struct Segment
314  {
315  off_t offset;
316  off_t size;
317  bool in_use;
318  };
319 
320  using VectorSegment = std::vector<Segment>;
321 
322  // -------------------------------------------------- //
323 
324  uint8_t* memory;
325  std::string name;
326  mutable std::mutex mutex;
327  MemoryPool::VectorSegment segment;
328  MemoryPool::LambdaSize size_on_change;
330  size_t pool_size;
331  int file_descriptor;
332  MemoryPool::Alignment alignment;
333  bool pool_can_expand;
334 
335  // -------------------------------------------------- //
336 
337  [[nodiscard]] bool expandToFit(size_t, size_t&) noexcept;
338 
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;
346 
347  // -------------------------------------------------- //
348 
349  MemoryPool(const MemoryPool&) = delete;
350  MemoryPool& operator=(const MemoryPool&) = delete;
351  };
352 
353  // }}}
354 }
355 
356 // {{{ Implementation
357 
358 #ifdef ZAKERO_MEMORYPOOL_IMPLEMENTATION
359 
360 // {{{ Macros
361 // {{{ Macros : Doxygen
362 
363 #ifdef ZAKERO__DOXYGEN_DEFINE_DOCS
364 
365 // Only used for generating Doxygen documentation
366 
378 #define ZAKERO_MEMORYPOOL_IMPLEMENTATION
379 
380 #endif
381 
382 // }}}
383 
393 #define ZAKERO_MEMORYPOOL__ERROR(err_) std::error_code(err_, MemoryPoolErrorCategory);
394 
395 // }}}
396 
397 namespace zakero
398 {
399 // {{{ Anonymous Namespace
400 
401 namespace
402 {
412  class MemoryPoolErrorCategory_
413  : public std::error_category
414  {
415  public:
416  constexpr MemoryPoolErrorCategory_() noexcept
417  {
418  }
419 
420  const char* name() const noexcept override
421  {
422  return "zakero.MemoryPool";
423  }
424 
425  std::string message(int condition) const override
426  {
427  switch(condition)
428  {
429 #define X(name_, val_, mesg_) \
430  case val_: return mesg_;
431  ZAKERO_MEMORYPOOL__ERROR_DATA
432 #undef X
433  }
434 
435  return "Unknown error condition";
436  }
437  } MemoryPoolErrorCategory;
438 
443  zakero::MemoryPool::LambdaSize LambdaSize_DoNothing = [](size_t) noexcept {};
444  zakero::MemoryPool::LambdaAddressMap LambdaAddressMap_DoNothing = [](const zakero::MemoryPool::AddressMap&) noexcept {};
457  inline off_t calculateActualSize(const off_t size
458  , const zakero::MemoryPool::Alignment alignment
459  )
460  {
461  const off_t mod = static_cast<off_t>(alignment);
462  const off_t step = static_cast<off_t>(alignment) + 1;
463 
464  return ((size + mod) / step) * step;
465  }
466 }
467 
468 // }}}
469 // {{{ Documentation
470 
541 // }}}
542 
555  MemoryPool::MemoryPool(const std::string& name
556  ) noexcept
557  : memory(nullptr)
558  , name(name)
559  , mutex()
560  , segment()
561  , size_on_change(LambdaSize_DoNothing)
562  , on_remap(LambdaAddressMap_DoNothing)
563  , pool_size(0)
564  , file_descriptor(-1)
565  , alignment(zakero::MemoryPool::Alignment::Bits_64)
566  , pool_can_expand(false)
567  {
568  }
569 
570 
577  {
578  on_remap = LambdaAddressMap_DoNothing;
579  segment.clear();
580  name.clear();
581 
582  if(memory != nullptr)
583  {
584  #ifdef ZAKERO_MEMORYPOOL_ZERO_ON_FREE
585  memset(memory, '\0', pool_size);
586  #endif
587 
588  munmap(memory, pool_size);
589  memory = nullptr;
590  }
591 
592  close(file_descriptor);
593  file_descriptor = -1;
594 
595  pool_size = 0;
596  }
597 
598 
633  std::error_code MemoryPool::init(const size_t size
634  , const bool expandable
635  , const MemoryPool::Alignment alignment
636  ) noexcept
637  {
638  //ZAKERO_PROFILER_DURATION("MemoryPool", "init");
639 
640  if(this->file_descriptor != -1)
641  {
642  return ZAKERO_MEMORYPOOL__ERROR(Error_Already_Initialized);
643  }
644 
645  if(size <= 0)
646  {
647  return ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Small);
648  }
649 
650  if(size > MemoryPool::Size_Max)
651  {
652  return ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Large);
653  }
654 
655  size_t pool_size = calculateActualSize(size, alignment);
656 
657  #ifdef __linux__
658  int fd = memfd_create(name.c_str(), 0);
659  #else
660  #error Need more code...
661  #endif
662 
663  if(fd == -1)
664  {
665  return ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Create_File);
666  }
667 
668  if(ftruncate(fd, pool_size) == -1)
669  {
670  return ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Resize_File);
671  }
672 
673  uint8_t* memory = (uint8_t*)mmap(nullptr
674  , pool_size
675  , PROT_READ | PROT_WRITE
676  , MAP_SHARED | MAP_NORESERVE
677  , fd
678  , 0
679  );
680 
681  if(memory == MAP_FAILED)
682  {
683  close(fd);
684 
685  return ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Map_File);
686  }
687 
688  this->segment.push_back(
689  { .offset = 0
690  , .size = (off_t)pool_size
691  , .in_use = false
692  });
693 
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;
699 
700  return ZAKERO_MEMORYPOOL__ERROR(Error_None);
701  }
702 
703 
727  int MemoryPool::fd() const noexcept
728  {
729  return file_descriptor;
730  }
731 
732 
740  size_t MemoryPool::size() const noexcept
741  {
742  return pool_size;
743  }
744 
745 
776  ) noexcept
777  {
778  if(lambda == nullptr)
779  {
780  size_on_change = LambdaSize_DoNothing;
781  }
782  else
783  {
784  size_on_change = lambda;
785  }
786  }
787 
788 
807  off_t MemoryPool::alloc(const size_t size
808  ) noexcept
809  {
810  std::error_code error;
811 
812  return alloc(size, error);
813  }
814 
815 
840  off_t MemoryPool::alloc(const size_t size
841  , std::error_code& error
842  ) noexcept
843  {
844  //ZAKERO_PROFILER_DURATION("MemoryPool", "alloc");
845 
846  // Validate Input
847 
848  if(size <= 0)
849  {
850  error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Small);
851  return -1;
852  }
853 
854  if(size > MemoryPool::Size_Max)
855  {
856  error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Large);
857  return -1;
858  }
859 
860  const size_t segment_size = calculateActualSize(size, alignment);
861 
862  // Allocate from the pool
863 
864  std::lock_guard<std::mutex> lock(mutex);
865 
866  size_t index = 0;
867 
868  if(segmentFindBestFit(segment_size, index) == false)
869  {
870  if(pool_can_expand == false)
871  {
872  error = ZAKERO_MEMORYPOOL__ERROR(Error_Out_Of_Memory);
873  return -1;
874  }
875 
876  if(expandToFit(segment_size, index) == false)
877  {
878  error = ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Resize_File);
879  return -1;
880  }
881  }
882 
883  error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
884 
885  segment[index].in_use = true;
886 
887  if((size_t)segment[index].size == segment_size)
888  {
889  return segment[index].offset;
890  }
891 
892  segmentSplit(index, segment_size);
893 
894  return segment[index].offset;
895  }
896 
897 
916  off_t MemoryPool::alloc(size_t size
917  , uint8_t value
918  ) noexcept
919  {
920  std::error_code error;
921 
922  return alloc(size, value, error);
923  }
924 
925 
950  off_t MemoryPool::alloc(size_t size
951  , uint8_t value
952  , std::error_code& error
953  ) noexcept
954  {
955  off_t offset = alloc(size, error);
956 
957  if(offset < 0)
958  {
959  return offset;
960  }
961 
962  uint8_t* ptr = addressOf(offset);
963 
964  memset(ptr, value, size);
965 
966  return offset;
967  }
968 
969 
991  off_t MemoryPool::alloc(size_t size
992  , uint32_t value
993  ) noexcept
994  {
995  std::error_code error;
996 
997  return alloc(size, value, error);
998  }
999 
1000 
1029  off_t MemoryPool::alloc(size_t size
1030  , const uint32_t value
1031  , std::error_code& error
1032  ) noexcept
1033  {
1034  off_t offset = alloc(size, error);
1035 
1036  if(offset < 0)
1037  {
1038  return offset;
1039  }
1040 
1041  size_t count = size / 4;
1042  uint32_t* p = (uint32_t*)addressOf(offset);
1043 
1044  for(uint32_t i = 0; i < count; i++)
1045  {
1046  p[i] = value;
1047  }
1048 
1049  return offset;
1050  }
1051 
1052 
1073  void MemoryPool::free(off_t& offset
1074  ) noexcept
1075  {
1076  //ZAKERO_PROFILER_DURATION("MemoryPool", "free");
1077 
1078  std::lock_guard<std::mutex> lock(mutex);
1079 
1080  size_t index = 0;
1081 
1082  if(segmentFindInUse(offset, index) == false)
1083  {
1084  return;
1085  }
1086 
1087  #ifdef ZAKERO_MEMORYPOOL_ZERO_ON_FREE
1088  memset(memory + segment[index].offset
1089  , '\0'
1090  , segment[index].size
1091  );
1092  #endif
1093 
1094  segment[index].in_use = false;
1095 
1096  segmentMergeNext(index);
1097  segmentMergePrev(index);
1098 
1099  offset = -1;
1100  }
1101 
1102 
1130  off_t MemoryPool::realloc(off_t offset
1131  , size_t size
1132  ) noexcept
1133  {
1134  std::error_code error;
1135 
1136  return realloc(offset, size, error);
1137  }
1138 
1139 
1176  off_t MemoryPool::realloc(off_t offset
1177  , size_t size
1178  , std::error_code& error
1179  ) noexcept
1180  {
1181  //ZAKERO_PROFILER_DURATION("MemoryPool", "realloc");
1182 
1183  // Validate Input
1184 
1185  if(size <= 0)
1186  {
1187  error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Small);
1188  return -1;
1189  }
1190 
1191  if(size > MemoryPool::Size_Max)
1192  {
1193  error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Large);
1194  return -1;
1195  }
1196 
1197  const size_t segment_size = calculateActualSize(size, alignment);
1198 
1199  std::lock_guard<std::mutex> lock(mutex);
1200 
1201  size_t index_src = 0;
1202 
1203  if(segmentFindInUse(offset, index_src) == false)
1204  {
1205  error = ZAKERO_MEMORYPOOL__ERROR(Error_Invalid_Offset);
1206  return -1;
1207  }
1208 
1209  Segment& segment_src = segment[index_src];
1210 
1211  // Same size, nothing to do
1212  if(segment_size == (size_t)segment_src.size)
1213  {
1214  error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1215  return offset;
1216  }
1217 
1218  // New size is smaller, shrink segment
1219  if(segment_size < (size_t)segment_src.size)
1220  {
1221  segmentSplit(index_src, segment_size);
1222 
1223  error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1224  return offset;
1225  }
1226 
1227  // Larger, try to expand into the next segment
1228  if(segmentExpand(index_src, segment_size) == true)
1229  {
1230  error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1231  return offset;
1232  }
1233 
1234  // Larger, find a new location
1235  size_t index_dst = 0;
1236 
1237  if(segmentFindBestFit(segment_size, index_dst) == false)
1238  {
1239  if(pool_can_expand == false)
1240  {
1241  error = ZAKERO_MEMORYPOOL__ERROR(Error_Out_Of_Memory);
1242  return -1;
1243  }
1244 
1245  if(expandToFit(segment_size, index_dst) == false)
1246  {
1247  error = ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Resize_File);
1248  return -1;
1249  }
1250  }
1251 
1252  index_dst = segmentMove(index_src, segment_size, index_dst);
1253 
1254  offset = segment[index_dst].offset;
1255 
1256  error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1257  return offset;
1258  }
1259 
1260 
1280  uint8_t* MemoryPool::addressOf(off_t offset
1281  ) const noexcept
1282  {
1283  //ZAKERO_PROFILER_DURATION("MemoryPool", "addressOf");
1284 
1285  size_t index = 0;
1286 
1287  if(segmentFindInUse(offset, index) == false)
1288  {
1289  return nullptr;
1290  }
1291 
1292  return memory + offset;
1293  }
1294 
1295 
1329  ) noexcept
1330  {
1331  if(lambda == nullptr)
1332  {
1333  on_remap = LambdaAddressMap_DoNothing;
1334  }
1335  else
1336  {
1337  on_remap = lambda;
1338  }
1339  }
1340 
1341 
1356  uint8_t* MemoryPool::remap(const MemoryPool::AddressMap& addr_map
1357  , uint8_t* address
1358  ) noexcept
1359  {
1360  if(addr_map.contains(address))
1361  {
1362  return addr_map.at(address);
1363  }
1364 
1365  return address;
1366  }
1367 
1368 
1398  std::string MemoryPool::dump(size_t bytes_per_character
1399  , size_t characters_per_line
1400  ) const noexcept
1401  {
1402  std::lock_guard<std::mutex> lock(mutex);
1403 
1404  std::string str = "{ \"name\": \"" + name + "\"\n"
1405  + ", \"pool_size\": " + std::to_string(pool_size) + "\n"
1406  ;
1407 
1408  size_t count = 0;
1409 
1410  str += ", \"Segment\":\n";
1411  std::string delim = " [ ";
1412  for(const auto& seg : segment)
1413  {
1414  str += delim;
1415  str += "{ \"offset\": " + std::to_string(seg.offset)
1416  + ", \"size\": " + std::to_string(seg.size)
1417  + ", \"in_use\": " + std::to_string(seg.in_use)
1418  + " }\n";
1419 
1420  count++;
1421  delim = " , ";
1422  }
1423  str += " ]\n";
1424 
1425  count = 0;
1426 
1427  int character = 'A';
1428 
1429  str += ", \"Layout\":\n [ \"";
1430  for(const auto& seg : segment)
1431  {
1432  int ch = '.';
1433  if(seg.in_use)
1434  {
1435  ch = character;
1436  }
1437 
1438  size_t segment_count = seg.size / bytes_per_character;
1439 
1440  while(segment_count > 0)
1441  {
1442  if((count + segment_count) >= characters_per_line)
1443  {
1444  size_t t = characters_per_line - count;
1445 
1446  str += std::string(t, ch) + "\"\n , \"";
1447 
1448  count = 0;
1449  segment_count -= t;
1450  }
1451  else
1452  {
1453  str += std::string(segment_count, ch);
1454 
1455  count += segment_count;
1456  segment_count = 0;
1457  }
1458  }
1459 
1460  if(ch != '.')
1461  {
1462  if(character == 'Z')
1463  {
1464  character = 'A';
1465  }
1466  else
1467  {
1468  character++;
1469  }
1470  }
1471  }
1472 
1473  str += "\"\n ]\n}";
1474 
1475  return str;
1476  }
1477 
1478 
1485  bool MemoryPool::expandToFit(size_t size_increase
1486  , size_t& index
1487  ) noexcept
1488  {
1489  //ZAKERO_PROFILER_DURATION("MemoryPool", "expandToFit");
1490 
1491  if(pool_can_expand == false)
1492  {
1493  return false;
1494  }
1495 
1496  if(segment.back().in_use == false)
1497  {
1498  size_increase -= segment.back().size;
1499  }
1500 
1501  if(pool_size + size_increase > MemoryPool::Size_Max)
1502  {
1503  return false;
1504  }
1505 
1506  uint8_t* old_memory = memory;
1507  const size_t old_size = pool_size;
1508  const size_t new_size = pool_size + size_increase;
1509 
1510  if(ftruncate(file_descriptor, new_size) == -1)
1511  {
1512  return false;
1513  }
1514 
1515  void* new_memory = mremap(old_memory
1516  , old_size
1517  , new_size
1518  , MREMAP_MAYMOVE
1519  );
1520 
1521  if(new_memory == MAP_FAILED)
1522  {
1523  return false;
1524  }
1525 
1526  pool_size = new_size;
1527 
1528  index = segment.size();
1529  if(index > 0
1530  && segment[index - 1].in_use == false
1531  )
1532  {
1533  index--;
1534  segment[index].size += (off_t)size_increase;
1535  }
1536  else
1537  {
1538  segment.push_back(
1539  { .offset = (off_t)old_size
1540  , .size = (off_t)size_increase
1541  , .in_use = false
1542  });
1543  }
1544 
1545  size_on_change(pool_size);
1546 
1547  if(new_memory != old_memory)
1548  {
1549  memory = (uint8_t*)new_memory;
1550 
1551  MemoryPool::AddressMap addr_map;
1552 
1553  for(const auto& seg : segment)
1554  {
1555  if(seg.in_use)
1556  {
1557  uint8_t* old_addr = old_memory + seg.offset;
1558  uint8_t* new_addr = memory + seg.offset;
1559 
1560  addr_map[old_addr] = new_addr;
1561  }
1562  }
1563 
1564  on_remap(addr_map);
1565  }
1566 
1567  return true;
1568  }
1569 
1570 
1581  bool MemoryPool::segmentExpand(const size_t index
1582  , const size_t size
1583  ) noexcept
1584  {
1585  //ZAKERO_PROFILER_DURATION("MemoryPool::Segment", "Expand");
1586 
1587  const size_t index_next = index + 1;
1588  if(index_next >= segment.size())
1589  {
1590  return false;
1591  }
1592 
1593  Segment& segment_next = segment[index_next];
1594 
1595  if(segment_next.in_use == true)
1596  {
1597  return false;
1598  }
1599 
1600  Segment& segment_this = segment[index];
1601 
1602  if(size > size_t(segment_this.size + segment_next.size))
1603  {
1604  return false;
1605  }
1606 
1607  const size_t size_next = size - segment_this.size;
1608 
1609  segment_this.size = size;
1610 
1611  if(size_next == 0)
1612  {
1613  segment.erase(std::begin(segment) + index_next);
1614  }
1615  else
1616  {
1617  segment_next.offset += size_next;
1618  segment_next.size -= size_next;
1619  }
1620 
1621  return true;
1622  }
1623 
1624 
1633  bool MemoryPool::segmentFindBestFit(const size_t size
1634  , size_t& index
1635  ) noexcept
1636  {
1637  //ZAKERO_PROFILER_DURATION("MemoryPool::Segment", "FindBestFit");
1638 
1639  for(size_t i = 0; i < segment.size(); i++)
1640  {
1641  const Segment& seg = segment[i];
1642 
1643  if(seg.in_use == false && (size_t)seg.size >= size)
1644  {
1645  index = i;
1646 
1647  return true;
1648  }
1649  }
1650 
1651  return false;
1652  }
1653 
1654 
1662  bool MemoryPool::segmentFindInUse(const off_t offset
1663  , size_t& index
1664  ) const noexcept
1665  {
1666  //ZAKERO_PROFILER_DURATION("MemoryPool::Segment", "FindInUse");
1667 
1668  for(size_t i = 0; i < segment.size(); i++)
1669  {
1670  const Segment& seg = segment[i];
1671 
1672  if(seg.offset > offset)
1673  {
1674  return false;
1675  }
1676 
1677  if(seg.offset == offset && seg.in_use == true)
1678  {
1679  index = i;
1680 
1681  return true;
1682  }
1683  }
1684 
1685  return false;
1686  }
1687 
1688 
1699  void MemoryPool::segmentMergeNext(const size_t index
1700  ) noexcept
1701  {
1702  //ZAKERO_PROFILER_DURATION("MemoryPool::Segment", "MergeNext");
1703 
1704  size_t index_next = index + 1;
1705 
1706  if(index_next >= segment.size())
1707  {
1708  return;
1709  }
1710 
1711  Segment& segment_next = segment[index_next];
1712 
1713  if(segment_next.in_use == true)
1714  {
1715  return;
1716  }
1717 
1718  segment[index].size += segment[index_next].size;
1719 
1720  segment.erase(std::begin(segment) + index_next);
1721  }
1722 
1723 
1738  void MemoryPool::segmentMergePrev(const size_t index
1739  ) noexcept
1740  {
1741  //ZAKERO_PROFILER_DURATION("MemoryPool::Segment", "MergePrev");
1742 
1743  if(index == 0)
1744  {
1745  return;
1746  }
1747 
1748  const size_t index_prev = index - 1;
1749 
1750  Segment& segment_prev = segment[index_prev];
1751 
1752  if(segment_prev.in_use == true)
1753  {
1754  return;
1755  }
1756 
1757  segment_prev.size += segment[index].size;
1758 
1759  segment.erase(std::begin(segment) + index);
1760  }
1761 
1762 
1780  size_t MemoryPool::segmentMove(const size_t src_index
1781  , const size_t dst_size
1782  , size_t& dst_index
1783  ) noexcept
1784  {
1785  //ZAKERO_PROFILER_DURATION("MemoryPool::Segment", "Move");
1786 
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;
1790 
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;
1794 
1795  dst_segment.in_use = true;
1796 
1797  memcpy(dst_addr, src_addr, src_size);
1798 
1799  #ifdef ZAKERO_MEMORYPOOL_ZERO_ON_FREE
1800  memset(src_addr, '\0', src_size);
1801  #endif
1802  src_segment.in_use = false;
1803 
1804  if(src_index > dst_index)
1805  {
1806  segmentMergeNext(src_index);
1807  segmentMergePrev(src_index);
1808 
1809  segmentSplit(dst_index, dst_size);
1810  }
1811  else
1812  {
1813  segmentSplit(dst_index, dst_size);
1814 
1815  segmentMergeNext(src_index);
1816  segmentMergePrev(src_index);
1817 
1818  segmentFindInUse(dst_offset, dst_index);
1819  }
1820 
1821  return dst_index;
1822  }
1823 
1824 
1843  void MemoryPool::segmentSplit(size_t index
1844  , size_t size
1845  ) noexcept
1846  {
1847  //ZAKERO_PROFILER_DURATION("MemoryPool::Segment", "Split");
1848 
1849  Segment& this_segment = segment[index];
1850 
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;
1854 
1855  this_segment.size = size;
1856 
1857  if(this_segment.in_use)
1858  {
1859  uint8_t* addr = memory + offset_next;
1860 
1861  memset(addr, '\0', size_next);
1862  }
1863 
1864  if(index_next >= segment.size())
1865  {
1866  segment.push_back(
1867  { .offset = offset_next
1868  , .size = size_next
1869  , .in_use = false
1870  });
1871  }
1872  else
1873  {
1874  Segment& segment_next = segment[index_next];
1875 
1876  if(segment_next.in_use)
1877  {
1878  segment.insert(std::begin(segment) + index_next,
1879  { .offset = offset_next
1880  , .size = size_next
1881  , .in_use = false
1882  });
1883  }
1884  else // Not in use
1885  {
1886  segment_next.offset = offset_next;
1887  segment_next.size += size_next;
1888  }
1889  }
1890  }
1891 }
1892 
1893 #endif
1894 
1895 // }}}
1896 
1897 #endif // zakero_MemoryPool_h
zakero::MemoryPool::size
size_t size() const noexcept
The size of the memory pool.
Definition: Zakero_MemoryPool.h:740
zakero::MemoryPool
A pool of memory.
Definition: Zakero_MemoryPool.h:264
zakero::MemoryPool::realloc
off_t realloc(off_t, size_t) noexcept
Change the size of allocated memory.
Definition: Zakero_MemoryPool.h:1130
zakero::MemoryPool::sizeOnChange
void sizeOnChange(MemoryPool::LambdaSize) noexcept
Set the Size Event callback.
Definition: Zakero_MemoryPool.h:775
zakero::MemoryPool::MemoryPool
MemoryPool(const std::string &) noexcept
Constructor.
Definition: Zakero_MemoryPool.h:555
zakero::MemoryPool::fd
int fd() const noexcept
The backing file descriptor.
Definition: Zakero_MemoryPool.h:727
zakero::MemoryPool::alloc
off_t alloc(const size_t) noexcept
Allocate memory from the pool.
Definition: Zakero_MemoryPool.h:807
zakero::MemoryPool::remap
static uint8_t * remap(const MemoryPool::AddressMap &, uint8_t *) noexcept
Get the new memory address.
Definition: Zakero_MemoryPool.h:1356
zakero::MemoryPool::free
void free(off_t &) noexcept
Free allocated memory.
Definition: Zakero_MemoryPool.h:1073
zakero::MemoryPool::onRemap
void onRemap(MemoryPool::LambdaAddressMap) noexcept
Set the Remap Event callback.
Definition: Zakero_MemoryPool.h:1328
zakero::MemoryPool::addressOf
uint8_t * addressOf(off_t) const noexcept
Convert an offset into a pointer.
Definition: Zakero_MemoryPool.h:1280
zakero::MemoryPool::init
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:633
zakero::MemoryPool::~MemoryPool
~MemoryPool() noexcept
Destructor.
Definition: Zakero_MemoryPool.h:576
zakero::MemoryPool::LambdaAddressMap
std::function< void(const MemoryPool::AddressMap &)> LambdaAddressMap
A lambda that receives a MemoryPool::AddressMap.
Definition: Zakero_MemoryPool.h:286
zakero::MemoryPool::Alignment
Alignment
The Byte-Alignment of the MemoryPool.
Definition: Zakero_MemoryPool.h:273
zakero::MemoryPool::LambdaSize
std::function< void(size_t)> LambdaSize
A lambda that receives a size_t argument.
Definition: Zakero_MemoryPool.h:285
zakero::MemoryPool::AddressMap
std::map< uint8_t *, uint8_t * > AddressMap
A mapping of old addresses to new addresses.
Definition: Zakero_MemoryPool.h:283
zakero::MemoryPool::dump
std::string dump(size_t, size_t) const noexcept
Output the internal state.
Definition: Zakero_MemoryPool.h:1398