Itoyori  v0.0.1
freelist.hpp
Go to the documentation of this file.
1 #pragma once
2 
3 #include <list>
4 #include <vector>
5 #include <optional>
6 #include <random>
7 #include <algorithm>
8 
9 #include "ityr/common/util.hpp"
10 
11 namespace ityr::common {
12 
13 class freelist {
14 public:
15  freelist() {}
16  freelist(uintptr_t addr, std::size_t size) : fl_(1, entry{addr, size}) {}
17 
18  std::optional<uintptr_t> get(std::size_t size) {
19  auto it = fl_.begin();
20  while (it != fl_.end()) {
21  if (it->size == size) {
22  auto ret = it->addr;
23  fl_.erase(it);
24  return ret;
25  } else if (it->size > size) {
26  auto ret = it->addr;
27  it->addr += size;
28  it->size -= size;
29  return ret;
30  }
31  it = std::next(it);
32  }
33  return std::nullopt;
34  }
35 
36  std::optional<uintptr_t> get(std::size_t size, std::size_t alignment) {
37  // TODO: consider better implementation
38  auto s = get(size);
39  if (!s.has_value()) {
40  return std::nullopt;
41  }
42 
43  if (*s % alignment == 0) {
44  return *s;
45  }
46 
47  add(*s, size);
48 
49  std::size_t req_size = size + alignment;
50 
51  s = get(req_size);
52  if (!s.has_value()) {
53  return std::nullopt;
54  }
55 
56  auto addr_got = *s;
57  auto addr_ret = round_up_pow2(addr_got, alignment);
58 
59  ITYR_CHECK(addr_ret >= addr_got);
60  ITYR_CHECK(addr_got + req_size >= addr_ret + size);
61 
62  add(addr_got, addr_ret - addr_got);
63  add(addr_ret + size, (addr_got + req_size) - (addr_ret + size));
64 
65  return addr_ret;
66  }
67 
68  void add(uintptr_t addr, std::size_t size) {
69  if (size == 0) return;
70 
71  auto it = fl_.begin();
72  while (it != fl_.end()) {
73  if (addr + size == it->addr) {
74  it->addr = addr;
75  it->size += size;
76  return;
77  } else if (addr + size < it->addr) {
78  fl_.insert(it, entry{addr, size});
79  return;
80  } else if (addr == it->addr + it->size) {
81  it->size += size;
82  auto next_it = std::next(it);
83  if (next_it != fl_.end() &&
84  next_it->addr == it->addr + it->size) {
85  it->size += next_it->size;
86  fl_.erase(next_it);
87  }
88  return;
89  }
90  it = std::next(it);
91  }
92  fl_.insert(it, entry{addr, size});
93  }
94 
95  std::size_t count() const { return fl_.size(); }
96 
97 private:
98  struct entry {
99  uintptr_t addr;
100  std::size_t size;
101  };
102 
103  std::list<entry> fl_;
104 };
105 
106 ITYR_TEST_CASE("[ityr::common::freelist] freelist management") {
107  uintptr_t addr = 100;
108  std::size_t size = 920;
109  freelist fl(addr, size);
110 
111  std::vector<uintptr_t> got;
112 
113  std::size_t n = 100;
114  for (std::size_t i = 0; i < size / n; i++) {
115  auto s = fl.get(n);
116  ITYR_CHECK(s.has_value());
117  got.push_back(*s);
118  }
119  ITYR_CHECK(!fl.get(n).has_value());
120 
121  // check for no overlap
122  for (std::size_t i = 0; i < got.size(); i++) {
123  for (std::size_t j = 0; j < got.size(); j++) {
124  if (i != j) {
125  ITYR_CHECK((got[i] + n <= got[j] ||
126  got[j] + n <= got[i]));
127  }
128  }
129  }
130 
131  // random shuffle
132  std::random_device seed_gen;
133  std::mt19937 engine(seed_gen());
134  std::shuffle(got.begin(), got.end(), engine);
135 
136  for (auto&& s : got) {
137  fl.add(s, n);
138  }
139 
140  ITYR_CHECK(fl.count() == 1);
141  ITYR_CHECK(*fl.get(size) == addr);
142 }
143 
144 }
Definition: freelist.hpp:13
std::optional< uintptr_t > get(std::size_t size, std::size_t alignment)
Definition: freelist.hpp:36
freelist(uintptr_t addr, std::size_t size)
Definition: freelist.hpp:16
std::size_t count() const
Definition: freelist.hpp:95
void add(uintptr_t addr, std::size_t size)
Definition: freelist.hpp:68
std::optional< uintptr_t > get(std::size_t size)
Definition: freelist.hpp:18
freelist()
Definition: freelist.hpp:15
#define ITYR_CHECK(cond)
Definition: util.hpp:48
Definition: allocator.hpp:16
T round_up_pow2(T x, T alignment)
Definition: util.hpp:142
constexpr auto size(const span< T > &s) noexcept
Definition: span.hpp:61