memory_pool.hpp
Go to the documentation of this file.
1 #ifndef CRYPTO3_MEM_POOL_HPP
2 #define CRYPTO3_MEM_POOL_HPP
3 
4 #include <nil/crypto3/utilities/types.hpp>
5 
6 #include <mutex>
7 #include <vector>
8 
9 namespace nil {
10  namespace crypto3 {
11  namespace detail {
12  inline bool ptr_in_pool(const void *pool_ptr, size_t poolsize, const void *buf_ptr, size_t bufsize) {
13  const uintptr_t pool = reinterpret_cast<uintptr_t>(pool_ptr);
14  const uintptr_t buf = reinterpret_cast<uintptr_t>(buf_ptr);
15  return (buf >= pool) && (buf + bufsize <= pool + poolsize);
16  }
17 
18  inline size_t padding_for_alignment(size_t n, size_t alignment) {
19  const size_t mod = n % alignment;
20  if (mod == 0) {
21  return 0;
22  }
23  return alignment - mod;
24  }
25  } // namespace detail
26 
27  class memory_pool final {
28  public:
40  memory_pool(uint8_t *pool, size_t pool_size, size_t page_size, size_t min_alloc, size_t max_alloc,
41  uint8_t align_bit) :
42  m_page_size(page_size),
43  m_min_alloc(min_alloc), m_max_alloc(max_alloc), m_align_bit(align_bit) {
44  if (pool == nullptr) {
45  throw std::invalid_argument("memory_pool pool was null");
46  }
47 
48  if (m_min_alloc > m_max_alloc) {
49  throw std::invalid_argument("memory_pool min_alloc > max_alloc");
50  }
51 
52  if (m_align_bit > 6) {
53  throw std::invalid_argument("memory_pool invalid align_bit");
54  }
55 
56  // This is basically just to verify that the range is valid
57  clear_mem(pool, pool_size);
58 
59  m_pool = pool;
60  m_pool_size = pool_size;
61  m_freelist.emplace_back(0, m_pool_size);
62  }
63 
64  void *allocate(size_t req) {
65  const size_t alignment = (1 << m_align_bit);
66 
67  if (req > m_pool_size) {
68  return nullptr;
69  }
70  if (req < m_min_alloc || req > m_max_alloc) {
71  return nullptr;
72  }
73 
74  std::lock_guard<std::mutex> lock(m_mutex);
75 
76  auto best_fit = m_freelist.end();
77 
78  for (auto i = m_freelist.begin(); i != m_freelist.end(); ++i) {
79  // If we have a perfect fit, use it immediately
80  if (i->second == req && (i->first % alignment) == 0) {
81  const size_t offset = i->first;
82  m_freelist.erase(i);
83  clear_mem(m_pool + offset, req);
84 
85  BOOST_ASSERT_MSG((reinterpret_cast<uintptr_t>(m_pool) + offset) % alignment == 0,
86  "Returning correctly aligned pointer");
87 
88  return m_pool + offset;
89  }
90 
91  if (((best_fit == m_freelist.end()) || (best_fit->second > i->second)) &&
92  (i->second >= (req + detail::padding_for_alignment(i->first, alignment)))) {
93  best_fit = i;
94  }
95  }
96 
97  if (best_fit != m_freelist.end()) {
98  const size_t offset = best_fit->first;
99 
100  const size_t alignment_padding = detail::padding_for_alignment(offset, alignment);
101 
102  best_fit->first += req + alignment_padding;
103  best_fit->second -= req + alignment_padding;
104 
105  // Need to realign, split the block
106  if (alignment_padding) {
107  /*
108  If we used the entire block except for small piece used for
109  alignment at the beginning, so just update the entry already
110  in place (as it is in the correct location), rather than
111  deleting the empty range and inserting the new one in the
112  same location.
113  */
114  if (best_fit->second == 0) {
115  best_fit->first = offset;
116  best_fit->second = alignment_padding;
117  } else {
118  m_freelist.insert(best_fit, std::make_pair(offset, alignment_padding));
119  }
120  }
121 
122  clear_mem(m_pool + offset + alignment_padding, req);
123 
124  BOOST_ASSERT_MSG((reinterpret_cast<uintptr_t>(m_pool) + offset + alignment_padding) % alignment ==
125  0,
126  "Returning correctly aligned pointer");
127 
128  return m_pool + offset + alignment_padding;
129  }
130 
131  return nullptr;
132  }
133 
134  bool deallocate(void *p, std::size_t n) BOOST_NOEXCEPT {
135  if (!detail::ptr_in_pool(m_pool, m_pool_size, p, n)) {
136  return false;
137  }
138 
139  std::memset(p, 0, n);
140 
141  std::lock_guard<std::mutex> lock(m_mutex);
142 
143  const size_t start = static_cast<uint8_t *>(p) - m_pool;
144 
145  auto comp = [](std::pair<size_t, size_t> x, std::pair<size_t, size_t> y) { return x.first < y.first; };
146 
147  auto i = std::lower_bound(m_freelist.begin(), m_freelist.end(), std::make_pair(start, 0), comp);
148 
149  // try to merge with later block
150  if (i != m_freelist.end() &&
151 
152  start + n == i->first) {
153  i->first = start;
154  i->second += n;
155  n = 0;
156  }
157 
158  // try to merge with previous block
159  if (i != m_freelist.begin()) {
160  auto prev = std::prev(i);
161 
162  if (prev->first + prev->second == start) {
163  if (n) {
164  prev->second += n;
165  n = 0;
166  } else {
167  // merge adjoining
168  prev->second += i->second;
169  m_freelist.erase(i);
170  }
171  }
172  }
173 
174  if (n != 0) { // no merge possible?
175  m_freelist.insert(i, std::make_pair(start, n));
176  }
177 
178  return true;
179  }
180 
181  memory_pool(const memory_pool &) = delete;
182 
183  memory_pool &operator=(const memory_pool &) = delete;
184 
185  private:
186  const size_t m_page_size = 0;
187  const size_t m_min_alloc = 0;
188  const size_t m_max_alloc = 0;
189  const uint8_t m_align_bit = 0;
190 
191  std::mutex m_mutex;
192 
193  std::vector<std::pair<size_t, size_t>> m_freelist;
194  uint8_t *m_pool = nullptr;
195  size_t m_pool_size = 0;
196  };
197  } // namespace crypto3
198 } // namespace nil
199 
200 #endif
Definition: memory_pool.hpp:27
memory_pool & operator=(const memory_pool &)=delete
void * allocate(size_t req)
Definition: memory_pool.hpp:64
memory_pool(const memory_pool &)=delete
memory_pool(uint8_t *pool, size_t pool_size, size_t page_size, size_t min_alloc, size_t max_alloc, uint8_t align_bit)
Definition: memory_pool.hpp:40
bool deallocate(void *p, std::size_t n) BOOST_NOEXCEPT
Definition: memory_pool.hpp:134
size_t padding_for_alignment(size_t n, size_t alignment)
Definition: memory_pool.hpp:18
bool ptr_in_pool(const void *pool_ptr, size_t poolsize, const void *buf_ptr, size_t bufsize)
Definition: memory_pool.hpp:12
void clear_mem(T *ptr, size_t n)
Definition: memory_operations.hpp:175
Definition: pair.hpp:31