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 }
350 
351 // {{{ Implementation
352 
353 #ifdef ZAKERO_MEMORYPOOL_IMPLEMENTATION
354 
355 // {{{ Macros
356 // {{{ Macros : Doxygen
357 
358 #ifdef ZAKERO__DOXYGEN_DEFINE_DOCS
359 
360 // Only used for generating Doxygen documentation
361 
373 #define ZAKERO_MEMORYPOOL_IMPLEMENTATION
374 
375 #endif
376 
377 // }}}
378 
388 #define ZAKERO_MEMORYPOOL__ERROR(err_) std::error_code(err_, MemoryPoolErrorCategory);
389 
390 // }}}
391 
392 namespace zakero
393 {
394 // {{{ Anonymous Namespace
395 
396 namespace
397 {
407  class MemoryPoolErrorCategory_
408  : public std::error_category
409  {
410  public:
411  constexpr MemoryPoolErrorCategory_() noexcept
412  {
413  }
414 
415  const char* name() const noexcept override
416  {
417  return "zakero.MemoryPool";
418  }
419 
420  std::string message(int condition) const override
421  {
422  switch(condition)
423  {
424 #define X(name_, val_, mesg_) \
425  case val_: return mesg_;
426  ZAKERO_MEMORYPOOL__ERROR_DATA
427 #undef X
428  }
429 
430  return "Unknown error condition";
431  }
432  } MemoryPoolErrorCategory;
433 
438  zakero::MemoryPool::LambdaSize LambdaSize_DoNothing = [](size_t){};
439  zakero::MemoryPool::LambdaAddressMap LambdaAddressMap_DoNothing = [](const zakero::MemoryPool::AddressMap&){};
452  inline off_t calculateActualSize(const off_t size
453  , const zakero::MemoryPool::Alignment alignment
454  )
455  {
456  const off_t mod = static_cast<off_t>(alignment);
457  const off_t step = static_cast<off_t>(alignment) + 1;
458 
459  return ((size + mod) / step) * step;
460  }
461 }
462 
463 // }}}
464 // {{{ Documentation
465 
536 // }}}
537 
550  MemoryPool::MemoryPool(const std::string& name
551  ) noexcept
552  : memory(nullptr)
553  , name(name)
554  , mutex()
555  , segment()
556  , size_on_change(LambdaSize_DoNothing)
557  , on_remap(LambdaAddressMap_DoNothing)
558  , pool_size(0)
559  , file_descriptor(-1)
560  , alignment(zakero::MemoryPool::Alignment::Bits_64)
561  , pool_can_expand(false)
562  {
563  }
564 
565 
572  {
573  on_remap = LambdaAddressMap_DoNothing;
574  segment.clear();
575  name.clear();
576 
577  if(memory != nullptr)
578  {
579  #ifdef ZAKERO_MEMORYPOOL_ZERO_ON_FREE
580  memset(memory, '\0', pool_size);
581  #endif
582 
583  munmap(memory, pool_size);
584  memory = nullptr;
585  }
586 
587  close(file_descriptor);
588  file_descriptor = -1;
589 
590  pool_size = 0;
591  }
592 
593 
628  std::error_code MemoryPool::init(const size_t size
629  , const bool expandable
630  , const MemoryPool::Alignment alignment
631  ) noexcept
632  {
633  //ZAKERO_PROFILER_DURATION("MemoryPool", "init");
634 
635  if(this->file_descriptor != -1)
636  {
637  return ZAKERO_MEMORYPOOL__ERROR(Error_Already_Initialized);
638  }
639 
640  if(size <= 0)
641  {
642  return ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Small);
643  }
644 
645  if(size > MemoryPool::Size_Max)
646  {
647  return ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Large);
648  }
649 
650  size_t pool_size = calculateActualSize(size, alignment);
651 
652  #ifdef __linux__
653  int fd = memfd_create(name.c_str(), 0);
654  #else
655  #error Need more code...
656  #endif
657 
658  if(fd == -1)
659  {
660  return ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Create_File);
661  }
662 
663  if(ftruncate(fd, pool_size) == -1)
664  {
665  return ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Resize_File);
666  }
667 
668  uint8_t* memory = (uint8_t*)mmap(nullptr
669  , pool_size
670  , PROT_READ | PROT_WRITE
671  , MAP_SHARED | MAP_NORESERVE
672  , fd
673  , 0
674  );
675 
676  if(memory == MAP_FAILED)
677  {
678  close(fd);
679 
680  return ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Map_File);
681  }
682 
683  this->segment.push_back(
684  { .offset = 0
685  , .size = (off_t)pool_size
686  , .in_use = false
687  });
688 
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;
694 
695  return ZAKERO_MEMORYPOOL__ERROR(Error_None);
696  }
697 
698 
722  int MemoryPool::fd() const noexcept
723  {
724  return file_descriptor;
725  }
726 
727 
735  size_t MemoryPool::size() const noexcept
736  {
737  return pool_size;
738  }
739 
740 
771  ) noexcept
772  {
773  if(lambda == nullptr)
774  {
775  size_on_change = LambdaSize_DoNothing;
776  }
777  else
778  {
779  size_on_change = lambda;
780  }
781  }
782 
783 
802  off_t MemoryPool::alloc(const size_t size
803  ) noexcept
804  {
805  std::error_code error;
806 
807  return alloc(size, error);
808  }
809 
810 
835  off_t MemoryPool::alloc(const size_t size
836  , std::error_code& error
837  ) noexcept
838  {
839  //ZAKERO_PROFILER_DURATION("MemoryPool", "alloc");
840 
841  // Validate Input
842 
843  if(size <= 0)
844  {
845  error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Small);
846  return -1;
847  }
848 
849  if(size > MemoryPool::Size_Max)
850  {
851  error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Large);
852  return -1;
853  }
854 
855  const size_t segment_size = calculateActualSize(size, alignment);
856 
857  // Allocate from the pool
858 
859  std::lock_guard<std::mutex> lock(mutex);
860 
861  size_t index = 0;
862 
863  if(segmentFindBestFit(segment_size, index) == false)
864  {
865  if(pool_can_expand == false)
866  {
867  error = ZAKERO_MEMORYPOOL__ERROR(Error_Out_Of_Memory);
868  return -1;
869  }
870 
871  if(expandToFit(segment_size, index) == false)
872  {
873  error = ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Resize_File);
874  return -1;
875  }
876  }
877 
878  error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
879 
880  segment[index].in_use = true;
881 
882  if((size_t)segment[index].size == segment_size)
883  {
884  return segment[index].offset;
885  }
886 
887  segmentSplit(index, segment_size);
888 
889  return segment[index].offset;
890  }
891 
892 
911  off_t MemoryPool::alloc(size_t size
912  , uint8_t value
913  ) noexcept
914  {
915  std::error_code error;
916 
917  return alloc(size, value, error);
918  }
919 
920 
945  off_t MemoryPool::alloc(size_t size
946  , uint8_t value
947  , std::error_code& error
948  ) noexcept
949  {
950  off_t offset = alloc(size, error);
951 
952  if(offset < 0)
953  {
954  return offset;
955  }
956 
957  uint8_t* ptr = addressOf(offset);
958 
959  memset(ptr, value, size);
960 
961  return offset;
962  }
963 
964 
986  off_t MemoryPool::alloc(size_t size
987  , uint32_t value
988  ) noexcept
989  {
990  std::error_code error;
991 
992  return alloc(size, value, error);
993  }
994 
995 
1024  off_t MemoryPool::alloc(size_t size
1025  , uint32_t value
1026  , std::error_code& error
1027  ) noexcept
1028  {
1029  off_t offset = alloc(size);
1030 
1031  if(offset < 0)
1032  {
1033  return offset;
1034  }
1035 
1036  size_t count = size / 4;
1037  uint32_t* p = (uint32_t*)addressOf(offset);
1038 
1039  for(uint32_t i = 0; i < count; i++)
1040  {
1041  p[i] = value;
1042  }
1043 
1044  return offset;
1045  }
1046 
1047 
1068  void MemoryPool::free(off_t& offset
1069  ) noexcept
1070  {
1071  //ZAKERO_PROFILER_DURATION("MemoryPool", "free");
1072 
1073  std::lock_guard<std::mutex> lock(mutex);
1074 
1075  size_t index = 0;
1076 
1077  if(segmentFindInUse(offset, index) == false)
1078  {
1079  return;
1080  }
1081 
1082  #ifdef ZAKERO_MEMORYPOOL_ZERO_ON_FREE
1083  memset(memory + segment[index].offset
1084  , '\0'
1085  , segment[index].size
1086  );
1087  #endif
1088 
1089  segment[index].in_use = false;
1090 
1091  segmentMergeNext(index);
1092  segmentMergePrev(index);
1093 
1094  offset = -1;
1095  }
1096 
1097 
1125  off_t MemoryPool::realloc(off_t offset
1126  , size_t size
1127  ) noexcept
1128  {
1129  std::error_code error;
1130 
1131  return realloc(offset, size, error);
1132  }
1133 
1134 
1171  off_t MemoryPool::realloc(off_t offset
1172  , size_t size
1173  , std::error_code& error
1174  ) noexcept
1175  {
1176  //ZAKERO_PROFILER_DURATION("MemoryPool", "realloc");
1177 
1178  // Validate Input
1179 
1180  if(size <= 0)
1181  {
1182  error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Small);
1183  return -1;
1184  }
1185 
1186  if(size > MemoryPool::Size_Max)
1187  {
1188  error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Large);
1189  return -1;
1190  }
1191 
1192  const size_t segment_size = calculateActualSize(size, alignment);
1193 
1194  std::lock_guard<std::mutex> lock(mutex);
1195 
1196  size_t index_src = 0;
1197 
1198  if(segmentFindInUse(offset, index_src) == false)
1199  {
1200  error = ZAKERO_MEMORYPOOL__ERROR(Error_Invalid_Offset);
1201  return -1;
1202  }
1203 
1204  Segment& segment_src = segment[index_src];
1205 
1206  // Same size, nothing to do
1207  if(segment_size == (size_t)segment_src.size)
1208  {
1209  error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1210  return offset;
1211  }
1212 
1213  // New size is smaller, shrink segment
1214  if(segment_size < (size_t)segment_src.size)
1215  {
1216  segmentSplit(index_src, segment_size);
1217 
1218  error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1219  return offset;
1220  }
1221 
1222  // Larger, try to expand into the next segment
1223  if(segmentExpand(index_src, segment_size) == true)
1224  {
1225  error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1226  return offset;
1227  }
1228 
1229  // Larger, find a new location
1230  size_t index_dst = 0;
1231 
1232  if(segmentFindBestFit(segment_size, index_dst) == false)
1233  {
1234  if(pool_can_expand == false)
1235  {
1236  error = ZAKERO_MEMORYPOOL__ERROR(Error_Out_Of_Memory);
1237  return -1;
1238  }
1239 
1240  if(expandToFit(segment_size, index_dst) == false)
1241  {
1242  error = ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Resize_File);
1243  return -1;
1244  }
1245  }
1246 
1247  index_dst = segmentMove(index_src, segment_size, index_dst);
1248 
1249  offset = segment[index_dst].offset;
1250 
1251  error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1252  return offset;
1253  }
1254 
1255 
1275  uint8_t* MemoryPool::addressOf(off_t offset
1276  ) const noexcept
1277  {
1278  //ZAKERO_PROFILER_DURATION("MemoryPool", "addressOf");
1279 
1280  size_t index = 0;
1281 
1282  if(segmentFindInUse(offset, index) == false)
1283  {
1284  return nullptr;
1285  }
1286 
1287  return memory + offset;
1288  }
1289 
1290 
1324  ) noexcept
1325  {
1326  if(lambda == nullptr)
1327  {
1328  on_remap = LambdaAddressMap_DoNothing;
1329  }
1330  else
1331  {
1332  on_remap = lambda;
1333  }
1334  }
1335 
1336 
1351  uint8_t* MemoryPool::remap(const MemoryPool::AddressMap& addr_map
1352  , uint8_t* address
1353  ) noexcept
1354  {
1355  if(addr_map.contains(address))
1356  {
1357  return addr_map.at(address);
1358  }
1359 
1360  return address;
1361  }
1362 
1363 
1393  std::string MemoryPool::dump(size_t bytes_per_character
1394  , size_t characters_per_line
1395  ) const noexcept
1396  {
1397  std::lock_guard<std::mutex> lock(mutex);
1398 
1399  std::string str = "{ \"name\": \"" + name + "\"\n"
1400  + ", \"pool_size\": " + std::to_string(pool_size) + "\n"
1401  ;
1402 
1403  size_t count = 0;
1404 
1405  str += ", \"Segment\":\n";
1406  std::string delim = " [ ";
1407  for(const auto& seg : segment)
1408  {
1409  str += delim;
1410  str += "{ \"offset\": " + std::to_string(seg.offset)
1411  + ", \"size\": " + std::to_string(seg.size)
1412  + ", \"in_use\": " + std::to_string(seg.in_use)
1413  + " }\n";
1414 
1415  count++;
1416  delim = " , ";
1417  }
1418  str += " ]\n";
1419 
1420  count = 0;
1421 
1422  int character = 'A';
1423 
1424  str += ", \"Layout\":\n [ \"";
1425  for(const auto& seg : segment)
1426  {
1427  int ch = '.';
1428  if(seg.in_use)
1429  {
1430  ch = character;
1431  }
1432 
1433  size_t segment_count = seg.size / bytes_per_character;
1434 
1435  while(segment_count > 0)
1436  {
1437  if((count + segment_count) >= characters_per_line)
1438  {
1439  size_t t = characters_per_line - count;
1440 
1441  str += std::string(t, ch) + "\"\n , \"";
1442 
1443  count = 0;
1444  segment_count -= t;
1445  }
1446  else
1447  {
1448  str += std::string(segment_count, ch);
1449 
1450  count += segment_count;
1451  segment_count = 0;
1452  }
1453  }
1454 
1455  if(ch != '.')
1456  {
1457  if(character == 'Z')
1458  {
1459  character = 'A';
1460  }
1461  else
1462  {
1463  character++;
1464  }
1465  }
1466  }
1467 
1468  str += "\"\n ]\n}";
1469 
1470  return str;
1471  }
1472 
1473 
1480  bool MemoryPool::expandToFit(size_t size_increase
1481  , size_t& index
1482  ) noexcept
1483  {
1484  //ZAKERO_PROFILER_DURATION("MemoryPool", "expandToFit");
1485 
1486  if(pool_can_expand == false)
1487  {
1488  return false;
1489  }
1490 
1491  if(segment.back().in_use == false)
1492  {
1493  size_increase -= segment.back().size;
1494  }
1495 
1496  if(pool_size + size_increase > MemoryPool::Size_Max)
1497  {
1498  return false;
1499  }
1500 
1501  uint8_t* old_memory = memory;
1502  const size_t old_size = pool_size;
1503  const size_t new_size = pool_size + size_increase;
1504 
1505  if(ftruncate(file_descriptor, new_size) == -1)
1506  {
1507  return false;
1508  }
1509 
1510  void* new_memory = mremap(old_memory
1511  , old_size
1512  , new_size
1513  , MREMAP_MAYMOVE
1514  );
1515 
1516  if(new_memory == MAP_FAILED)
1517  {
1518  return false;
1519  }
1520 
1521  pool_size = new_size;
1522 
1523  index = segment.size();
1524  if(index > 0
1525  && segment[index - 1].in_use == false
1526  )
1527  {
1528  index--;
1529  segment[index].size += (off_t)size_increase;
1530  }
1531  else
1532  {
1533  segment.push_back(
1534  { .offset = (off_t)old_size
1535  , .size = (off_t)size_increase
1536  , .in_use = false
1537  });
1538  }
1539 
1540  size_on_change(pool_size);
1541 
1542  if(new_memory != old_memory)
1543  {
1544  memory = (uint8_t*)new_memory;
1545 
1546  MemoryPool::AddressMap addr_map;
1547 
1548  for(const auto& seg : segment)
1549  {
1550  if(seg.in_use)
1551  {
1552  uint8_t* old_addr = old_memory + seg.offset;
1553  uint8_t* new_addr = memory + seg.offset;
1554 
1555  addr_map[old_addr] = new_addr;
1556  }
1557  }
1558 
1559  on_remap(addr_map);
1560  }
1561 
1562  return true;
1563  }
1564 
1565 
1576  bool MemoryPool::segmentExpand(const size_t index
1577  , const size_t size
1578  ) noexcept
1579  {
1580  //ZAKERO_PROFILER_DURATION("MemoryPool::Segment", "Expand");
1581 
1582  const size_t index_next = index + 1;
1583  if(index_next >= segment.size())
1584  {
1585  return false;
1586  }
1587 
1588  Segment& segment_next = segment[index_next];
1589 
1590  if(segment_next.in_use == true)
1591  {
1592  return false;
1593  }
1594 
1595  Segment& segment_this = segment[index];
1596 
1597  if(size > size_t(segment_this.size + segment_next.size))
1598  {
1599  return false;
1600  }
1601 
1602  const size_t size_next = size - segment_this.size;
1603 
1604  segment_this.size = size;
1605 
1606  if(size_next == 0)
1607  {
1608  segment.erase(std::begin(segment) + index_next);
1609  }
1610  else
1611  {
1612  segment_next.offset += size_next;
1613  segment_next.size -= size_next;
1614  }
1615 
1616  return true;
1617  }
1618 
1619 
1628  bool MemoryPool::segmentFindBestFit(const size_t size
1629  , size_t& index
1630  ) noexcept
1631  {
1632  //ZAKERO_PROFILER_DURATION("MemoryPool::Segment", "FindBestFit");
1633 
1634  for(size_t i = 0; i < segment.size(); i++)
1635  {
1636  const Segment& seg = segment[i];
1637 
1638  if(seg.in_use == false && (size_t)seg.size >= size)
1639  {
1640  index = i;
1641 
1642  return true;
1643  }
1644  }
1645 
1646  return false;
1647  }
1648 
1649 
1657  bool MemoryPool::segmentFindInUse(const off_t offset
1658  , size_t& index
1659  ) const noexcept
1660  {
1661  //ZAKERO_PROFILER_DURATION("MemoryPool::Segment", "FindInUse");
1662 
1663  for(size_t i = 0; i < segment.size(); i++)
1664  {
1665  const Segment& seg = segment[i];
1666 
1667  if(seg.offset > offset)
1668  {
1669  return false;
1670  }
1671 
1672  if(seg.offset == offset && seg.in_use == true)
1673  {
1674  index = i;
1675 
1676  return true;
1677  }
1678  }
1679 
1680  return false;
1681  }
1682 
1683 
1694  void MemoryPool::segmentMergeNext(const size_t index
1695  ) noexcept
1696  {
1697  //ZAKERO_PROFILER_DURATION("MemoryPool::Segment", "MergeNext");
1698 
1699  size_t index_next = index + 1;
1700 
1701  if(index_next >= segment.size())
1702  {
1703  return;
1704  }
1705 
1706  Segment& segment_next = segment[index_next];
1707 
1708  if(segment_next.in_use == true)
1709  {
1710  return;
1711  }
1712 
1713  segment[index].size += segment[index_next].size;
1714 
1715  segment.erase(std::begin(segment) + index_next);
1716  }
1717 
1718 
1733  void MemoryPool::segmentMergePrev(const size_t index
1734  ) noexcept
1735  {
1736  //ZAKERO_PROFILER_DURATION("MemoryPool::Segment", "MergePrev");
1737 
1738  if(index == 0)
1739  {
1740  return;
1741  }
1742 
1743  const size_t index_prev = index - 1;
1744 
1745  Segment& segment_prev = segment[index_prev];
1746 
1747  if(segment_prev.in_use == true)
1748  {
1749  return;
1750  }
1751 
1752  segment_prev.size += segment[index].size;
1753 
1754  segment.erase(std::begin(segment) + index);
1755  }
1756 
1757 
1775  size_t MemoryPool::segmentMove(const size_t src_index
1776  , const size_t dst_size
1777  , size_t& dst_index
1778  ) noexcept
1779  {
1780  //ZAKERO_PROFILER_DURATION("MemoryPool::Segment", "Move");
1781 
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;
1785 
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;
1789 
1790  dst_segment.in_use = true;
1791 
1792  memcpy(dst_addr, src_addr, src_size);
1793 
1794  #ifdef ZAKERO_MEMORYPOOL_ZERO_ON_FREE
1795  memset(src_addr, '\0', src_size);
1796  #endif
1797  src_segment.in_use = false;
1798 
1799  if(src_index > dst_index)
1800  {
1801  segmentMergeNext(src_index);
1802  segmentMergePrev(src_index);
1803 
1804  segmentSplit(dst_index, dst_size);
1805  }
1806  else
1807  {
1808  segmentSplit(dst_index, dst_size);
1809 
1810  segmentMergeNext(src_index);
1811  segmentMergePrev(src_index);
1812 
1813  segmentFindInUse(dst_offset, dst_index);
1814  }
1815 
1816  return dst_index;
1817  }
1818 
1819 
1838  void MemoryPool::segmentSplit(size_t index
1839  , size_t size
1840  ) noexcept
1841  {
1842  //ZAKERO_PROFILER_DURATION("MemoryPool::Segment", "Split");
1843 
1844  Segment& this_segment = segment[index];
1845 
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;
1849 
1850  this_segment.size = size;
1851 
1852  if(this_segment.in_use)
1853  {
1854  uint8_t* addr = memory + offset_next;
1855 
1856  memset(addr, '\0', size_next);
1857  }
1858 
1859  if(index_next >= segment.size())
1860  {
1861  segment.push_back(
1862  { .offset = offset_next
1863  , .size = size_next
1864  , .in_use = false
1865  });
1866  }
1867  else
1868  {
1869  Segment& segment_next = segment[index_next];
1870 
1871  if(segment_next.in_use)
1872  {
1873  segment.insert(std::begin(segment) + index_next,
1874  { .offset = offset_next
1875  , .size = size_next
1876  , .in_use = false
1877  });
1878  }
1879  else // Not in use
1880  {
1881  segment_next.offset = offset_next;
1882  segment_next.size += size_next;
1883  }
1884  }
1885  }
1886 }
1887 
1888 #endif
1889 
1890 // }}}
1891 
1892 #endif // zakero_MemoryPool_h
zakero::MemoryPool::size
size_t size() const noexcept
The size of the memory pool.
Definition: Zakero_MemoryPool.h:735
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:1125
zakero::MemoryPool::sizeOnChange
void sizeOnChange(MemoryPool::LambdaSize) noexcept
Set the Size Event callback.
Definition: Zakero_MemoryPool.h:770
zakero::MemoryPool::MemoryPool
MemoryPool(const std::string &) noexcept
Constructor.
Definition: Zakero_MemoryPool.h:550
zakero::MemoryPool::fd
int fd() const noexcept
The backing file descriptor.
Definition: Zakero_MemoryPool.h:722
zakero::MemoryPool::alloc
off_t alloc(const size_t) noexcept
Allocate memory from the pool.
Definition: Zakero_MemoryPool.h:802
zakero::MemoryPool::remap
static uint8_t * remap(const MemoryPool::AddressMap &, uint8_t *) noexcept
Get the new memory address.
Definition: Zakero_MemoryPool.h:1351
zakero::MemoryPool::free
void free(off_t &) noexcept
Free allocated memory.
Definition: Zakero_MemoryPool.h:1068
zakero::MemoryPool::onRemap
void onRemap(MemoryPool::LambdaAddressMap) noexcept
Set the Remap Event callback.
Definition: Zakero_MemoryPool.h:1323
zakero::MemoryPool::addressOf
uint8_t * addressOf(off_t) const noexcept
Convert an offset into a pointer.
Definition: Zakero_MemoryPool.h:1275
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:628
zakero::MemoryPool::~MemoryPool
~MemoryPool() noexcept
Destructor.
Definition: Zakero_MemoryPool.h:571
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:1393