Zakero's C++ Header Libraries
A collection of reusable C++ libraries
Zakero_MemoryPool.h
Go to the documentation of this file.
1 /* *******************************************************************
2  * This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5  */
6 
192 #ifndef zakero_MemoryPool_h
193 #define zakero_MemoryPool_h
194 
195 // POSIX
196 #include <cstring>
197 #include <functional>
198 #include <map>
199 #include <mutex>
200 #include <sys/mman.h>
201 #include <system_error>
202 #include <unistd.h>
203 
204 // {{{ Error Codes
205 
224 #define ZAKERO_MEMORYPOOL__ERROR_CODES \
225  X(Error_None , 0 , "No Error" ) \
226  X(Error_Already_Initialized , 1 , "The Memory Pool has already been initialized." ) \
227  X(Error_Size_Too_Small , 2 , "Invalid Size: Must be greater than 0." ) \
228  X(Error_Size_Too_Large , 3 , "Invalid Size: Must be less than zakero::MemoryPool::Size_Max" ) \
229  X(Error_Failed_To_Create_File , 4 , "Unable to create file." ) \
230  X(Error_Failed_To_Resize_File , 5 , "Unable to resize file." ) \
231  X(Error_Failed_To_Map_File , 6 , "Unable to memory map the file." ) \
232  X(Error_Out_Of_Memory , 7 , "Not enough contiguous memory." ) \
233  X(Error_Invalid_Offset , 8 , "The offset is not valid." ) \
234 
235 // }}}
236 // {{{ Declaration
237 
238 namespace zakero
239 {
241  {
242  public:
243 #define X(name_, val_, mesg_) \
244  static constexpr int name_ = val_;
245  ZAKERO_MEMORYPOOL__ERROR_CODES
246 #undef X
247  static constexpr size_t Size_Max = (size_t)std::numeric_limits<off_t>::max();
248 
249  enum class Alignment : uint8_t
250  { Bits_8 = 0
251  , Bits_16 = 1
252  , Bits_32 = 3
253  , Bits_64 = 7
254  , Byte_1 = Bits_8
255  , Byte_2 = Bits_16
256  , Byte_4 = Bits_32
257  , Byte_8 = Bits_64
258  };
259 
260  using AddressMap = std::map<uint8_t*, uint8_t*>;
261 
262  using LambdaSize = std::function<void(size_t)>;
263  using LambdaAddressMap = std::function<void(const MemoryPool::AddressMap&)>;
264 
265  MemoryPool(const std::string&) noexcept;
266  ~MemoryPool() noexcept;
267 
268  std::error_condition init(const size_t, const bool = false, const MemoryPool::Alignment = MemoryPool::Alignment::Bits_64) noexcept;
269  [[nodiscard]] int fd() const noexcept;
270  [[nodiscard]] size_t size() const noexcept;
271  void sizeOnChange(MemoryPool::LambdaSize) noexcept;
272 
273  [[nodiscard]] off_t alloc(const size_t) noexcept;
274  [[nodiscard]] off_t alloc(const size_t, std::error_condition&) noexcept;
275  [[nodiscard]] off_t alloc(size_t, uint8_t) noexcept;
276  [[nodiscard]] off_t alloc(size_t, uint8_t, std::error_condition&) noexcept;
277  [[nodiscard]] off_t alloc(size_t, uint32_t) noexcept;
278  [[nodiscard]] off_t alloc(size_t, uint32_t, std::error_condition&) noexcept;
279  void free(off_t&) noexcept;
280  [[nodiscard]] off_t realloc(off_t, size_t) noexcept;
281  [[nodiscard]] off_t realloc(off_t, size_t, std::error_condition&) noexcept;
282 
283  [[nodiscard]] uint8_t* addressOf(off_t) const noexcept;
284  void onRemap(MemoryPool::LambdaAddressMap) noexcept;
285  [[nodiscard]] static uint8_t* remap(const MemoryPool::AddressMap&, uint8_t*) noexcept;
286 
287  [[nodiscard]] std::string dump(size_t, size_t) const noexcept;
288 
289  private:
290  [[nodiscard]] bool expandToFit(size_t, size_t&) noexcept;
291 
292  [[nodiscard]] bool segmentExpand(const size_t, const size_t) noexcept;
293  [[nodiscard]] bool segmentFindBestFit(const size_t, size_t&) noexcept;
294  bool segmentFindInUse(const off_t, size_t&) const noexcept;
295  void segmentMergeNext(const size_t) noexcept;
296  void segmentMergePrev(const size_t) noexcept;
297  [[nodiscard]] size_t segmentMove(const size_t, const size_t, size_t&) noexcept;
298  void segmentSplit(size_t, size_t) noexcept;
299 
300  // ------------------------------------------------- //
301 
302  struct Segment
303  {
304  off_t offset;
305  off_t size;
306  bool in_use;
307  };
308 
309  using VectorSegment = std::vector<Segment>;
310 
311  uint8_t* memory;
312  std::string name;
313  mutable std::mutex mutex;
314  MemoryPool::VectorSegment segment;
315  MemoryPool::LambdaSize size_on_change;
317  size_t pool_size;
318  int file_descriptor;
319  MemoryPool::Alignment alignment;
320  bool pool_can_expand;
321  };
322 };
323 
324 // }}}
325 // {{{ Implementation
326 
327 #if defined (ZAKERO_MEMORYPOOL_IMPLEMENTATION)
328 
329 // {{{ Dependencies
330 
331 #if defined(ZAKERO_MEMORYPOOL_PROFILER)
332 # if !defined(ZAKERO_PROFILER_IMPLEMENTATION)
333 # define ZAKERO_PROFILER_IMPLEMENTATION
334 # endif
335 # define ZAKERO_PROFILER_ENABLE
336 # include "Zakero_Profiler.h"
337 #endif
338 
339 // }}}
340 // {{{ Defines
341 
351 #define ZAKERO_MEMORYPOOL__ERROR(err_) std::error_condition(err_, MemoryPoolErrorCategory);
352 
360 #define ZAKERO_MACRO_HAS_VALUE(macro_define_) \
361  ~(~macro_define_ + 0) == 0 && ~(~macro_define_ + 1) == 1
362 
363 // {{{ Defines : Doxygen
364 
365 #if defined(ZAKERO__DOXYGEN_DEFINE_DOCS)
366 
367 // Only used for generating Doxygen documentation
368 
380 #define ZAKERO_MEMORYPOOL_IMPLEMENTATION
381 
382 
399 #define ZAKERO_MEMORYPOOL_PROFILER
400 
413 #define ZAKERO_MEMORYPOOL_PROFILER_FILE
414 
415 #endif
416 
417 // }}}
418 // {{{ Defines : Profiler
419 
420 #if defined(ZAKERO_MEMORYPOOL_PROFILER)
421 # if !defined(ZAKERO_MEMORYPOOL_PROFILER_FILE) || !ZAKERO_MACRO_HAS_VALUE(ZAKERO_MEMORYPOOL_PROFILER_FILE)
422 # define ZAKERO_MEMORYPOOL_PROFILER_FILE "./zakero_MemoryPool_profile.json"
423 # endif
424 # define ZAKERO_MEMORYPOOL__PROFILER_INIT_METADATA(output_, meta_data_) ZAKERO_PROFILER_INIT_METADATA(output_, meta_data_)
425 # define ZAKERO_MEMORYPOOL__PROFILER_DURATION(category_, name_) ZAKERO_PROFILER_DURATION(category_, name_)
426 # define ZAKERO_MEMORYPOOL__PROFILER_INSTANT(category_, name_) ZAKERO_PROFILER_INSTANT(category_, name_)
427 #else
428 # define ZAKERO_MEMORYPOOL__PROFILER_INIT_METADATA(output_, meta_data_)
429 # define ZAKERO_MEMORYPOOL__PROFILER_DURATION(category_, name_)
430 # define ZAKERO_MEMORYPOOL__PROFILER_INSTANT(category_, name_)
431 #endif
432 
433 // }}}
434 // }}}
435 
436 namespace
437 {
438  class MemoryPoolErrorCategory_
439  : public std::error_category
440  {
441  public:
442  constexpr MemoryPoolErrorCategory_() noexcept
443  {
444  }
445 
446  const char* name() const noexcept override
447  {
448  return "zakero.MemoryPool";
449  }
450 
451  std::string message(int condition) const override
452  {
453  switch(condition)
454  {
455 #define X(name_, val_, mesg_) \
456  case val_: return mesg_;
457  ZAKERO_MEMORYPOOL__ERROR_CODES
458 #undef X
459  }
460 
461  return "Unknown error condition";
462  }
463  } MemoryPoolErrorCategory;
464 
465  zakero::MemoryPool::LambdaSize LambdaSize_DoNothing = [](size_t){};
466  zakero::MemoryPool::LambdaAddressMap LambdaAddressMap_DoNothing = [](const zakero::MemoryPool::AddressMap&){};
467 
476  inline off_t calculateActualSize(const off_t size
477  , const zakero::MemoryPool::Alignment alignment
478  )
479  {
480  const off_t mod = static_cast<off_t>(alignment);
481  const off_t step = static_cast<off_t>(alignment) + 1;
482 
483  return ((size + mod) / step) * step;
484  }
485 }
486 
487 namespace zakero
488 {
554  MemoryPool::MemoryPool(const std::string& name
555  ) noexcept
556  : memory(nullptr)
557  , name(name)
558  , mutex()
559  , segment()
560  , size_on_change(LambdaSize_DoNothing)
561  , on_remap(LambdaAddressMap_DoNothing)
562  , pool_size(0)
563  , file_descriptor(-1)
564  , alignment(zakero::MemoryPool::Alignment::Bits_64)
565  , pool_can_expand(false)
566  {
567 #if defined(ZAKERO_MEMORYPOOL_PROFILER)
568  zakero::Profiler::MetaData meta_data =
569  { { "ZHL" , "Zakero_MemoryPool.h" }
570  , { "version" , "0.8.0" }
571  };
573  , meta_data
574  );
575 #endif
576  }
577 
578 
585  {
586  on_remap = LambdaAddressMap_DoNothing;
587  segment.clear();
588  name.clear();
589 
590  if(memory != nullptr)
591  {
592  #if defined (ZAKERO_MEMORYPOOL_ZERO_ON_FREE)
593  memset(memory, '\0', pool_size);
594  #endif
595 
596  munmap(memory, pool_size);
597  memory = nullptr;
598  }
599 
600  close(file_descriptor);
601  file_descriptor = -1;
602 
603  pool_size = 0;
604  }
605 
606 
641  std::error_condition MemoryPool::init(const size_t size
642  , const bool expandable
643  , const MemoryPool::Alignment alignment
644  ) noexcept
645  {
646  ZAKERO_MEMORYPOOL__PROFILER_DURATION("MemoryPool", "init");
647 
648  if(this->file_descriptor != -1)
649  {
650  return ZAKERO_MEMORYPOOL__ERROR(Error_Already_Initialized);
651  }
652 
653  if(size <= 0)
654  {
655  return ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Small);
656  }
657 
658  if(size > MemoryPool::Size_Max)
659  {
660  return ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Large);
661  }
662 
663  size_t pool_size = calculateActualSize(size, alignment);
664 
665 #if defined (__linux__)
666  int fd = memfd_create(name.c_str(), 0);
667 #else
668 #error Need more code...
669 #endif
670 
671  if(fd == -1)
672  {
673  return ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Create_File);
674  }
675 
676  if(ftruncate(fd, pool_size) == -1)
677  {
678  return ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Resize_File);
679  }
680 
681  uint8_t* memory = (uint8_t*)mmap(nullptr
682  , pool_size
683  , PROT_READ | PROT_WRITE
684  , MAP_SHARED | MAP_NORESERVE
685  , fd
686  , 0
687  );
688 
689  if(memory == MAP_FAILED)
690  {
691  close(fd);
692 
693  return ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Map_File);
694  }
695 
696  this->segment.push_back(
697  { .offset = 0
698  , .size = (off_t)pool_size
699  , .in_use = false
700  });
701 
702  this->pool_can_expand = expandable;
703  this->alignment = alignment;
704  this->pool_size = pool_size;
705  this->memory = memory;
706  this->file_descriptor = fd;
707 
708  return ZAKERO_MEMORYPOOL__ERROR(Error_None);
709  }
710 
711 
735  int MemoryPool::fd() const noexcept
736  {
737  return file_descriptor;
738  }
739 
740 
748  size_t MemoryPool::size() const noexcept
749  {
750  return pool_size;
751  }
752 
753 
784  ) noexcept
785  {
786  if(lambda == nullptr)
787  {
788  size_on_change = LambdaSize_DoNothing;
789  }
790  else
791  {
792  size_on_change = lambda;
793  }
794  }
795 
796 
815  off_t MemoryPool::alloc(const size_t size
816  ) noexcept
817  {
818  std::error_condition error;
819 
820  return alloc(size, error);
821  }
822 
823 
848  off_t MemoryPool::alloc(const size_t size
849  , std::error_condition& error
850  ) noexcept
851  {
852  ZAKERO_MEMORYPOOL__PROFILER_DURATION("MemoryPool", "alloc");
853 
854  // Validate Input
855 
856  if(size <= 0)
857  {
858  error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Small);
859  return -1;
860  }
861 
862  if(size > MemoryPool::Size_Max)
863  {
864  error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Large);
865  return -1;
866  }
867 
868  const size_t segment_size = calculateActualSize(size, alignment);
869 
870  // Allocate from the pool
871 
872  std::lock_guard<std::mutex> lock(mutex);
873 
874  size_t index = 0;
875 
876  if(segmentFindBestFit(segment_size, index) == false)
877  {
878  if(pool_can_expand == false)
879  {
880  error = ZAKERO_MEMORYPOOL__ERROR(Error_Out_Of_Memory);
881  return -1;
882  }
883 
884  if(expandToFit(segment_size, index) == false)
885  {
886  error = ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Resize_File);
887  return -1;
888  }
889  }
890 
891  error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
892 
893  segment[index].in_use = true;
894 
895  if((size_t)segment[index].size == segment_size)
896  {
897  return segment[index].offset;
898  }
899 
900  segmentSplit(index, segment_size);
901 
902  return segment[index].offset;
903  }
904 
905 
924  off_t MemoryPool::alloc(size_t size
925  , uint8_t value
926  ) noexcept
927  {
928  std::error_condition error;
929 
930  return alloc(size, value, error);
931  }
932 
933 
958  off_t MemoryPool::alloc(size_t size
959  , uint8_t value
960  , std::error_condition& error
961  ) noexcept
962  {
963  off_t offset = alloc(size, error);
964 
965  if(offset < 0)
966  {
967  return offset;
968  }
969 
970  uint8_t* ptr = addressOf(offset);
971 
972  memset(ptr, value, size);
973 
974  return offset;
975  }
976 
977 
999  off_t MemoryPool::alloc(size_t size
1000  , uint32_t value
1001  ) noexcept
1002  {
1003  std::error_condition error;
1004 
1005  return alloc(size, value, error);
1006  }
1007 
1008 
1037  off_t MemoryPool::alloc(size_t size
1038  , uint32_t value
1039  , std::error_condition& error
1040  ) noexcept
1041  {
1042  off_t offset = alloc(size);
1043 
1044  if(offset < 0)
1045  {
1046  return offset;
1047  }
1048 
1049  size_t count = size / 4;
1050  uint32_t* p = (uint32_t*)addressOf(offset);
1051 
1052  for(uint32_t i = 0; i < count; i++)
1053  {
1054  p[i] = value;
1055  }
1056 
1057  return offset;
1058  }
1059 
1060 
1081  void MemoryPool::free(off_t& offset
1082  ) noexcept
1083  {
1084  ZAKERO_MEMORYPOOL__PROFILER_DURATION("MemoryPool", "free");
1085 
1086  std::lock_guard<std::mutex> lock(mutex);
1087 
1088  size_t index = 0;
1089 
1090  if(segmentFindInUse(offset, index) == false)
1091  {
1092  return;
1093  }
1094 
1095  #if defined (ZAKERO_MEMORYPOOL_ZERO_ON_FREE)
1096  memset(memory + segment[index].offset
1097  , '\0'
1098  , segment[index].size
1099  );
1100  #endif
1101 
1102  segment[index].in_use = false;
1103 
1104  segmentMergeNext(index);
1105  segmentMergePrev(index);
1106 
1107  offset = -1;
1108  }
1109 
1110 
1138  off_t MemoryPool::realloc(off_t offset
1139  , size_t size
1140  ) noexcept
1141  {
1142  std::error_condition error;
1143 
1144  return realloc(offset, size, error);
1145  }
1146 
1147 
1184  off_t MemoryPool::realloc(off_t offset
1185  , size_t size
1186  , std::error_condition& error
1187  ) noexcept
1188  {
1189  ZAKERO_MEMORYPOOL__PROFILER_DURATION("MemoryPool", "realloc");
1190 
1191  // Validate Input
1192 
1193  if(size <= 0)
1194  {
1195  error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Small);
1196  return -1;
1197  }
1198 
1199  if(size > MemoryPool::Size_Max)
1200  {
1201  error = ZAKERO_MEMORYPOOL__ERROR(Error_Size_Too_Large);
1202  return -1;
1203  }
1204 
1205  const size_t segment_size = calculateActualSize(size, alignment);
1206 
1207  std::lock_guard<std::mutex> lock(mutex);
1208 
1209  size_t index_src = 0;
1210 
1211  if(segmentFindInUse(offset, index_src) == false)
1212  {
1213  error = ZAKERO_MEMORYPOOL__ERROR(Error_Invalid_Offset);
1214  return -1;
1215  }
1216 
1217  Segment& segment_src = segment[index_src];
1218 
1219  // Same size, nothing to do
1220  if(segment_size == (size_t)segment_src.size)
1221  {
1222  error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1223  return offset;
1224  }
1225 
1226  // New size is smaller, shrink segment
1227  if(segment_size < (size_t)segment_src.size)
1228  {
1229  segmentSplit(index_src, segment_size);
1230 
1231  error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1232  return offset;
1233  }
1234 
1235  // Larger, try to expand into the next segment
1236  if(segmentExpand(index_src, segment_size) == true)
1237  {
1238  error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1239  return offset;
1240  }
1241 
1242  // Larger, find a new location
1243  size_t index_dst = 0;
1244 
1245  if(segmentFindBestFit(segment_size, index_dst) == false)
1246  {
1247  if(pool_can_expand == false)
1248  {
1249  error = ZAKERO_MEMORYPOOL__ERROR(Error_Out_Of_Memory);
1250  return -1;
1251  }
1252 
1253  if(expandToFit(segment_size, index_dst) == false)
1254  {
1255  error = ZAKERO_MEMORYPOOL__ERROR(Error_Failed_To_Resize_File);
1256  return -1;
1257  }
1258  }
1259 
1260  index_dst = segmentMove(index_src, segment_size, index_dst);
1261 
1262  offset = segment[index_dst].offset;
1263 
1264  error = ZAKERO_MEMORYPOOL__ERROR(Error_None);
1265  return offset;
1266  }
1267 
1268 
1288  uint8_t* MemoryPool::addressOf(off_t offset
1289  ) const noexcept
1290  {
1291  ZAKERO_MEMORYPOOL__PROFILER_DURATION("MemoryPool", "addressOf");
1292 
1293  size_t index = 0;
1294 
1295  if(segmentFindInUse(offset, index) == false)
1296  {
1297  return nullptr;
1298  }
1299 
1300  return memory + offset;
1301  }
1302 
1303 
1337  ) noexcept
1338  {
1339  if(lambda == nullptr)
1340  {
1341  on_remap = LambdaAddressMap_DoNothing;
1342  }
1343  else
1344  {
1345  on_remap = lambda;
1346  }
1347  }
1348 
1349 
1364  uint8_t* MemoryPool::remap(const MemoryPool::AddressMap& addr_map
1365  , uint8_t* address
1366  ) noexcept
1367  {
1368  if(addr_map.contains(address))
1369  {
1370  return addr_map.at(address);
1371  }
1372 
1373  return address;
1374  }
1375 
1376 
1406  std::string MemoryPool::dump(size_t bytes_per_character
1407  , size_t characters_per_line
1408  ) const noexcept
1409  {
1410  std::lock_guard<std::mutex> lock(mutex);
1411 
1412  std::string str = "{ \"name\": \"" + name + "\"\n"
1413  + ", \"pool_size\": " + std::to_string(pool_size) + "\n"
1414  ;
1415 
1416  size_t count = 0;
1417 
1418  str += ", \"Segment\":\n";
1419  std::string delim = " [ ";
1420  for(const auto& seg : segment)
1421  {
1422  str += delim;
1423  str += "{ \"offset\": " + std::to_string(seg.offset)
1424  + ", \"size\": " + std::to_string(seg.size)
1425  + ", \"in_use\": " + std::to_string(seg.in_use)
1426  + " }\n";
1427 
1428  count++;
1429  delim = " , ";
1430  }
1431  str += " ]\n";
1432 
1433  count = 0;
1434 
1435  int character = 'A';
1436 
1437  str += ", \"Layout\":\n [ \"";
1438  for(const auto& seg : segment)
1439  {
1440  int ch = '.';
1441  if(seg.in_use)
1442  {
1443  ch = character;
1444  }
1445 
1446  size_t segment_count = seg.size / bytes_per_character;
1447 
1448  while(segment_count > 0)
1449  {
1450  if((count + segment_count) >= characters_per_line)
1451  {
1452  size_t t = characters_per_line - count;
1453 
1454  str += std::string(t, ch) + "\"\n , \"";
1455 
1456  count = 0;
1457  segment_count -= t;
1458  }
1459  else
1460  {
1461  str += std::string(segment_count, ch);
1462 
1463  count += segment_count;
1464  segment_count = 0;
1465  }
1466  }
1467 
1468  if(ch != '.')
1469  {
1470  if(character == 'Z')
1471  {
1472  character = 'A';
1473  }
1474  else
1475  {
1476  character++;
1477  }
1478  }
1479  }
1480 
1481  str += "\"\n ]\n}";
1482 
1483  return str;
1484  }
1485 
1486 
1493  bool MemoryPool::expandToFit(size_t size_increase
1494  , size_t& index
1495  ) noexcept
1496  {
1497  ZAKERO_MEMORYPOOL__PROFILER_DURATION("MemoryPool", "expandToFit");
1498 
1499  if(pool_can_expand == false)
1500  {
1501  return false;
1502  }
1503 
1504  if(segment.back().in_use == false)
1505  {
1506  size_increase -= segment.back().size;
1507  }
1508 
1509  if(pool_size + size_increase > MemoryPool::Size_Max)
1510  {
1511  return false;
1512  }
1513 
1514  uint8_t* old_memory = memory;
1515  const size_t old_size = pool_size;
1516  const size_t new_size = pool_size + size_increase;
1517 
1518  if(ftruncate(file_descriptor, new_size) == -1)
1519  {
1520  return false;
1521  }
1522 
1523  void* new_memory = mremap(old_memory
1524  , old_size
1525  , new_size
1526  , MREMAP_MAYMOVE
1527  );
1528 
1529  if(new_memory == MAP_FAILED)
1530  {
1531  return false;
1532  }
1533 
1534  pool_size = new_size;
1535 
1536  index = segment.size();
1537  if(index > 0
1538  && segment[index - 1].in_use == false
1539  )
1540  {
1541  index--;
1542  segment[index].size += (off_t)size_increase;
1543  }
1544  else
1545  {
1546  segment.push_back(
1547  { .offset = (off_t)old_size
1548  , .size = (off_t)size_increase
1549  , .in_use = false
1550  });
1551  }
1552 
1553  size_on_change(pool_size);
1554 
1555  if(new_memory != old_memory)
1556  {
1557  memory = (uint8_t*)new_memory;
1558 
1559  MemoryPool::AddressMap addr_map;
1560 
1561  for(const auto& seg : segment)
1562  {
1563  if(seg.in_use)
1564  {
1565  uint8_t* old_addr = old_memory + seg.offset;
1566  uint8_t* new_addr = memory + seg.offset;
1567 
1568  addr_map[old_addr] = new_addr;
1569  }
1570  }
1571 
1572  on_remap(addr_map);
1573  }
1574 
1575  return true;
1576  }
1577 
1578 
1589  bool MemoryPool::segmentExpand(const size_t index
1590  , const size_t size
1591  ) noexcept
1592  {
1593  ZAKERO_MEMORYPOOL__PROFILER_DURATION("MemoryPool::Segment", "Expand");
1594 
1595  const size_t index_next = index + 1;
1596  if(index_next >= segment.size())
1597  {
1598  return false;
1599  }
1600 
1601  Segment& segment_next = segment[index_next];
1602 
1603  if(segment_next.in_use == true)
1604  {
1605  return false;
1606  }
1607 
1608  Segment& segment_this = segment[index];
1609 
1610  if(size > size_t(segment_this.size + segment_next.size))
1611  {
1612  return false;
1613  }
1614 
1615  const size_t size_next = size - segment_this.size;
1616 
1617  segment_this.size = size;
1618 
1619  if(size_next == 0)
1620  {
1621  segment.erase(std::begin(segment) + index_next);
1622  }
1623  else
1624  {
1625  segment_next.offset += size_next;
1626  segment_next.size -= size_next;
1627  }
1628 
1629  return true;
1630  }
1631 
1632 
1641  bool MemoryPool::segmentFindBestFit(const size_t size
1642  , size_t& index
1643  ) noexcept
1644  {
1645  ZAKERO_MEMORYPOOL__PROFILER_DURATION("MemoryPool::Segment", "FindBestFit");
1646 
1647  for(size_t i = 0; i < segment.size(); i++)
1648  {
1649  if(segment[i].in_use == false
1650  && (size_t)segment[i].size >= size
1651  )
1652  {
1653  index = i;
1654 
1655  return true;
1656  }
1657  }
1658 
1659  return false;
1660  }
1661 
1662 
1670  bool MemoryPool::segmentFindInUse(const off_t offset
1671  , size_t& index
1672  ) const noexcept
1673  {
1674  ZAKERO_MEMORYPOOL__PROFILER_DURATION("MemoryPool::Segment", "FindInUse");
1675 
1676  for(size_t i = 0; i < segment.size(); i++)
1677  {
1678  if(segment[i].offset == offset
1679  && segment[i].in_use == true
1680  )
1681  {
1682  index = i;
1683  return true;
1684  }
1685 
1686  if(segment[i].offset > offset)
1687  {
1688  break;
1689  }
1690  }
1691 
1692  return false;
1693  }
1694 
1695 
1706  void MemoryPool::segmentMergeNext(const size_t index
1707  ) noexcept
1708  {
1709  ZAKERO_MEMORYPOOL__PROFILER_DURATION("MemoryPool::Segment", "MergeNext");
1710 
1711  size_t index_next = index + 1;
1712 
1713  if(index_next >= segment.size())
1714  {
1715  return;
1716  }
1717 
1718  Segment& segment_next = segment[index_next];
1719 
1720  if(segment_next.in_use == true)
1721  {
1722  return;
1723  }
1724 
1725  segment[index].size += segment[index_next].size;
1726 
1727  segment.erase(std::begin(segment) + index_next);
1728  }
1729 
1730 
1745  void MemoryPool::segmentMergePrev(const size_t index
1746  ) noexcept
1747  {
1748  ZAKERO_MEMORYPOOL__PROFILER_DURATION("MemoryPool::Segment", "MergePrev");
1749 
1750  if(index == 0)
1751  {
1752  return;
1753  }
1754 
1755  const size_t index_prev = index - 1;
1756 
1757  Segment& segment_prev = segment[index_prev];
1758 
1759  if(segment_prev.in_use == true)
1760  {
1761  return;
1762  }
1763 
1764  segment_prev.size += segment[index].size;
1765 
1766  segment.erase(std::begin(segment) + index);
1767  }
1768 
1769 
1787  size_t MemoryPool::segmentMove(const size_t src_index
1788  , const size_t dst_size
1789  , size_t& dst_index
1790  ) noexcept
1791  {
1792  ZAKERO_MEMORYPOOL__PROFILER_DURATION("MemoryPool::Segment", "Move");
1793 
1794  Segment& src_segment = segment[src_index];
1795  const uint8_t* src_addr = memory + src_segment.offset;
1796  const size_t src_size = src_segment.size;
1797 
1798  Segment& dst_segment = segment[dst_index];
1799  uint8_t* dst_addr = memory + dst_segment.offset;
1800  const size_t dst_offset = dst_segment.offset;
1801 
1802  dst_segment.in_use = true;
1803 
1804  memcpy(dst_addr, src_addr, src_size);
1805 
1806  #if defined (ZAKERO_MEMORYPOOL_ZERO_ON_FREE)
1807  memset(src_addr, '\0', src_size);
1808  #endif
1809  src_segment.in_use = false;
1810 
1811  if(src_index > dst_index)
1812  {
1813  segmentMergeNext(src_index);
1814  segmentMergePrev(src_index);
1815 
1816  segmentSplit(dst_index, dst_size);
1817  }
1818  else
1819  {
1820  segmentSplit(dst_index, dst_size);
1821 
1822  segmentMergeNext(src_index);
1823  segmentMergePrev(src_index);
1824 
1825  segmentFindInUse(dst_offset, dst_index);
1826  }
1827 
1828  return dst_index;
1829  }
1830 
1831 
1850  void MemoryPool::segmentSplit(size_t index
1851  , size_t size
1852  ) noexcept
1853  {
1854  ZAKERO_MEMORYPOOL__PROFILER_DURATION("MemoryPool::Segment", "Split");
1855 
1856  Segment& this_segment = segment[index];
1857 
1858  const size_t index_next = index + 1;
1859  const off_t offset_next = this_segment.offset + size;
1860  const off_t size_next = this_segment.size - size;
1861 
1862  this_segment.size = size;
1863 
1864  if(this_segment.in_use)
1865  {
1866  uint8_t* addr = memory + offset_next;
1867 
1868  memset(addr, '\0', size_next);
1869  }
1870 
1871  if(index_next >= segment.size())
1872  {
1873  segment.push_back(
1874  { .offset = offset_next
1875  , .size = size_next
1876  , .in_use = false
1877  });
1878  }
1879  else
1880  {
1881  Segment& segment_next = segment[index_next];
1882 
1883  if(segment_next.in_use)
1884  {
1885  segment.insert(std::begin(segment) + index_next,
1886  { .offset = offset_next
1887  , .size = size_next
1888  , .in_use = false
1889  });
1890  }
1891  else // Not in use
1892  {
1893  segment_next.offset = offset_next;
1894  segment_next.size += size_next;
1895  }
1896  }
1897  }
1898 }
1899 
1900 #endif
1901 
1902 // }}}
1903 
1904 #endif // zakero_MemoryPool_h
zakero::MemoryPool::dump
std::string dump(size_t, size_t) const noexcept
Output the internal state.
Definition: Zakero_MemoryPool.h:1406
zakero::MemoryPool::alloc
off_t alloc(const size_t) noexcept
Allocate memory from the pool.
Definition: Zakero_MemoryPool.h:815
zakero::MemoryPool::remap
static uint8_t * remap(const MemoryPool::AddressMap &, uint8_t *) noexcept
Get the new memory address.
Definition: Zakero_MemoryPool.h:1364
zakero::MemoryPool::AddressMap
std::map< uint8_t *, uint8_t * > AddressMap
A mapping of old addresses to new addresses.
Definition: Zakero_MemoryPool.h:260
zakero::MemoryPool
A pool of memory.
Definition: Zakero_MemoryPool.h:241
zakero::MemoryPool::free
void free(off_t &) noexcept
Free allocated memory.
Definition: Zakero_MemoryPool.h:1081
zakero::MemoryPool::realloc
off_t realloc(off_t, size_t) noexcept
Change the size of allocated memory.
Definition: Zakero_MemoryPool.h:1138
zakero::MemoryPool::~MemoryPool
~MemoryPool() noexcept
Destructor.
Definition: Zakero_MemoryPool.h:584
zakero::MemoryPool::init
std::error_condition init(const size_t, const bool=false, const MemoryPool::Alignment=MemoryPool::Alignment::Bits_64) noexcept
Initialize the MemoryPool.
Definition: Zakero_MemoryPool.h:641
zakero::MemoryPool::LambdaAddressMap
std::function< void(const MemoryPool::AddressMap &)> LambdaAddressMap
A lambda that receives a MemoryPool::AddressMap.
Definition: Zakero_MemoryPool.h:263
zakero::MemoryPool::MemoryPool
MemoryPool(const std::string &) noexcept
Constructor.
Definition: Zakero_MemoryPool.h:554
zakero::MemoryPool::fd
int fd() const noexcept
The backing file descriptor.
Definition: Zakero_MemoryPool.h:735
ZAKERO_MEMORYPOOL_PROFILER_FILE
#define ZAKERO_MEMORYPOOL_PROFILER_FILE
Profile data file.
Definition: Zakero_MemoryPool.h:413
zakero::MemoryPool::addressOf
uint8_t * addressOf(off_t) const noexcept
Convert an offset into a pointer.
Definition: Zakero_MemoryPool.h:1288
zakero::MemoryPool::sizeOnChange
void sizeOnChange(MemoryPool::LambdaSize) noexcept
Set the Size Event callback.
Definition: Zakero_MemoryPool.h:783
zakero::MemoryPool::Alignment
Alignment
The Byte-Alignment of the MemoryPool.
Definition: Zakero_MemoryPool.h:250
Zakero_Profiler.h
Zakero Profiler.
zakero::MemoryPool::size
size_t size() const noexcept
The size of the memory pool.
Definition: Zakero_MemoryPool.h:748
ZAKERO_PROFILER_INIT_METADATA
#define ZAKERO_PROFILER_INIT_METADATA(output_, meta_data_)
Initialize the profiler.
Definition: Zakero_Profiler.h:310
zakero::MemoryPool::LambdaSize
std::function< void(size_t)> LambdaSize
A lambda that receives a size_t argument.
Definition: Zakero_MemoryPool.h:262
zakero::MemoryPool::onRemap
void onRemap(MemoryPool::LambdaAddressMap) noexcept
Set the Remap Event callback.
Definition: Zakero_MemoryPool.h:1336