herald  2.0.0
memory_arena.h
1 // Copyright 2021 Herald Project Contributors
2 // SPDX-License-Identifier: Apache-2.0
3 //
4 
5 #ifndef HERALD_MEMORY_ARENA_H
6 #define HERALD_MEMORY_ARENA_H
7 
8 #include <cstddef>
9 #include <bitset>
10 #include <array>
11 
13 namespace herald {
14 namespace datatype {
15 
21  unsigned short startPageIndex = 0;
22  unsigned short byteLength = 0;
23 
24  bool isInitialised() const {
25  return 0 != byteLength;
26  }
27 };
28 
29 constexpr unsigned long pagesRequired(std::size_t size,std::size_t pageSize)
30 {
31  return (size + pageSize - 1) / pageSize;
32 }
33 
39 template <std::size_t MaxSize, std::size_t AllocationSize>
40 class MemoryArena {
41 public:
43  static constexpr std::size_t Size = MaxSize;
46  static constexpr std::size_t PageSize = AllocationSize;
47 
48  MemoryArena() noexcept
49  : arena(), pagesInUse(false)
50  {
51  ;
52  }
53 
54  ~MemoryArena() = default;
55 
56  void reserve(MemoryArenaEntry& entry,std::size_t newSize) {
57  if (newSize <= entry.byteLength) {
58  return;
59  }
60  auto newEntry = allocate(newSize);
61  for (std::size_t i = 0;i < entry.byteLength;++i) {
62  set(newEntry,i,get(entry,i));
63  }
64  deallocate(entry);
65  entry = newEntry;
66  }
67 
68  MemoryArenaEntry allocate(std::size_t size) {
69  if (0 == size) {
70  return MemoryArenaEntry{0,0};
71  }
72  // find first page location with enough space
73  unsigned long pages = pagesRequired(size,PageSize);
74  bool inEmpty = false;
75  unsigned long lastEmptyIndex = 0;
76  for (std::size_t i = 0;i < pagesInUse.size();++i) {
77  if (!pagesInUse.test(i)) {
78  // this one is empty
79  if (!inEmpty) {
80  inEmpty = true;
81  lastEmptyIndex = i;
82  }
83  if (i - lastEmptyIndex + 1 == pages) {
84  // flip bits and return
85  for (unsigned long f = lastEmptyIndex;f <= i;++f) {
86  pagesInUse.set(f,true);
87  }
88  return MemoryArenaEntry{(unsigned short)lastEmptyIndex,(unsigned short)size};
89  }
90  } else {
91  inEmpty = false;
92  }
93  }
94  // ran out of memory! Throw! (Causes catastrophic crash)
95  throw std::runtime_error("Unable to allocate memory in arena");
96  }
97 
98  void deallocate(MemoryArenaEntry& entry) {
99  if (!entry.isInitialised()) {
100  return; // guard
101  }
102  // set relevant bits to empty
103  long pages = pagesRequired(entry.byteLength,PageSize);
104  for (int i = 0;i < pages;++i) {
105  pagesInUse.set(entry.startPageIndex + i,false);
106  }
107  entry.byteLength = 0;
108  entry.startPageIndex = 0;
109  }
110 
111  void set(const MemoryArenaEntry& entry, unsigned short bytePosition, unsigned char value) {
112  if (!entry.isInitialised()) {
113  return;
114  }
115  arena[(entry.startPageIndex * PageSize) + bytePosition] = value;
116  }
117 
118  char get(const MemoryArenaEntry& entry, unsigned short bytePosition) {
119  if (!entry.isInitialised()) {
120  return '\0';
121  }
122  return arena[(entry.startPageIndex * PageSize) + bytePosition];
123  }
124 
125  std::size_t pagesFree() const {
126  return pagesRequired(Size,PageSize) - pagesInUse.count();
127  }
128 
129 private:
130  std::array<unsigned char,Size> arena;
131  std::bitset<pagesRequired(Size,PageSize)> pagesInUse;
132 };
133 
134 }
135 }
136 
137 #endif
Very basic paged memory arena class.
Definition: memory_arena.h:40
static constexpr std::size_t Size
The Maximum size to use for data (doesn't include page table)
Definition: memory_arena.h:43
static constexpr std::size_t PageSize
The allocation size for each page. Note total mem usage is Size + (Size / PageSize) Thus for MemoryAr...
Definition: memory_arena.h:46
Acts as a non-global memory arena for arbitrary classes.
Definition: aggregates.h:15
Represents an external 'pointer' to an allocated memory area.
Definition: memory_arena.h:20