General Purpose library for Freestanding C++ and POSIX systems
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

362 lines
8.4 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. #pragma once
  2. #include "gp_config.hpp"
  3. #include "gp/algorithm/modifiers.hpp"
  4. #include "gp/algorithm/tmp_manip.hpp"
  5. #include "gp/allocator/dummy.hpp"
  6. #include "gp/array.hpp"
  7. #include "gp/buffer.hpp"
  8. #include "gp/math/integer_math.hpp"
  9. #include "gp/allocator/allocator.hpp"
  10. #include <type_traits>
  11. namespace gp{
  12. /**
  13. * @brief An allocator that uses the buddy algorithm to divide the space into allocatable memory.
  14. *
  15. * This is not resilient to memory fragmentation, but the smallest memory unit should always be allocatable unless the memory is actually full.
  16. *
  17. * @tparam max_msb The log2 rounded up of the maximum space you expect to address in bytes
  18. * @tparam align The smallest size of memory you expect the allocator to allocate
  19. */
  20. template<size_t max_msb = 24, size_t align = 8>
  21. class buddy : public allocator {
  22. struct twig {
  23. bool used : 1;
  24. bool used_children : 1;
  25. twig(uint8_t src) {
  26. used = 1 & src;
  27. used_children = 2 & src;
  28. }
  29. operator uint8_t() {
  30. return 1 * used + 2 * used_children;
  31. }
  32. };
  33. struct bundle {
  34. uint8_t a : 2;
  35. uint8_t b : 2;
  36. uint8_t c : 2;
  37. uint8_t d : 2;
  38. bundle() {
  39. a = 0; b = 0; c = 0; d = 0;
  40. }
  41. };
  42. gp::optional<gp::reference_wrapper<allocator>> allocator_v;
  43. gp::buffer<char> data;
  44. const size_t max_depth;
  45. const size_t twig_explore_length;
  46. /**
  47. * @brief The depth of the tree required to allocate
  48. */
  49. static constexpr size_t max_theoric_depth = max_msb - gp::math::msb(align);
  50. /**
  51. * @brief The actual number of twigs to support the specified depth
  52. */
  53. static constexpr size_t required_twigs = (1 << (max_theoric_depth + 1)) - 1;
  54. /**
  55. * @brief ((max allocatable size - min allocatable size) ** 2 - 1) / 4 twigs in a bundle
  56. **/
  57. static constexpr size_t span_size = required_twigs / 4 + (required_twigs % 4 != 0);
  58. /**
  59. * @brief The array of twigs (in bundles)
  60. */
  61. gp::array<bundle, span_size> stack;
  62. /**
  63. * This code has been manually checked and will always return.
  64. * If you find a case where it doesn't, please file an issue.
  65. **/
  66. #pragma clang diagnostic push
  67. #pragma clang diagnostic ignored "-Wreturn-type"
  68. #pragma GCC diagnostic push
  69. #pragma GCC diagnostic ignored "-Wreturn-type"
  70. twig get_twig(size_t idx) const {
  71. auto far = idx / 4;
  72. auto local = idx % 4;
  73. switch(local) {
  74. case 0:
  75. return stack[far].a;
  76. case 1:
  77. return stack[far].b;
  78. case 2:
  79. return stack[far].c;
  80. case 3:
  81. return stack[far].d;
  82. }
  83. }
  84. #pragma GCC diagnostic pop
  85. #pragma clang diagnostic pop
  86. void set_twig(size_t idx, twig v) {
  87. auto far = idx / 4;
  88. auto local = idx % 4;
  89. auto& group = stack[far];
  90. switch(local) {
  91. case 0:
  92. group.a = v;
  93. return;
  94. case 1:
  95. group.b = v;
  96. return;
  97. case 2:
  98. group.c = v;
  99. return;
  100. case 3:
  101. group.d = v;
  102. return;
  103. }
  104. }
  105. constexpr size_t size_to_depth(size_t sz) {
  106. size_t pow2 = gp::math::msb(sz) - gp::math::msb(align);
  107. return gp::clamp(
  108. (size_t)0 ,
  109. max_depth - pow2,
  110. max_depth
  111. );
  112. }
  113. constexpr size_t depth_to_size(size_t depth) {
  114. return 1 << (max_depth - depth + gp::math::msb(align));
  115. }
  116. constexpr size_t get_left(size_t index) const {
  117. return ((index + 1) << 1) - 1;
  118. }
  119. constexpr size_t get_right(size_t index) const {
  120. return ((index + 1) << 1);
  121. }
  122. template<typename function>
  123. void all_under(size_t index, function func) {
  124. size_t left = get_left(index);
  125. size_t right = get_right(index);
  126. all_under(left, func);
  127. all_under(right, func);
  128. func(left);
  129. func(right);
  130. }
  131. template<typename function>
  132. void all_over(size_t index, function func) {
  133. if(index != 0) {
  134. size_t parent = ((index + 1) >> 1) - 1;
  135. func(parent);
  136. if(parent != 0)
  137. all_over(parent, func);
  138. }
  139. }
  140. template<typename function>
  141. bool is_any_child(size_t index, function func) const {
  142. size_t left = get_left(index);
  143. size_t right = get_right(index);
  144. if(left < twig_explore_length && right < twig_explore_length) {
  145. if(func(left)) return true;
  146. if(func(right)) return true;
  147. if(is_any_child(left, func)) return true;
  148. if(is_any_child(right, func)) return true;
  149. }
  150. return false;
  151. }
  152. static constexpr size_t no_twig = -1;
  153. size_t find_free_twig(size_t depth, size_t root = 0, size_t explored = 0) const {
  154. auto v = get_twig(root);
  155. if(depth == explored) {
  156. if(v.used || v.used_children)
  157. {
  158. return no_twig;
  159. } else {
  160. return root;
  161. }
  162. } else {
  163. if(v.used)
  164. {
  165. return no_twig;
  166. }
  167. ++explored;
  168. auto ret = find_free_twig(depth, get_right(root), explored);
  169. if(ret != no_twig)
  170. {
  171. return ret;
  172. }
  173. ret = find_free_twig(depth, get_left(root), explored);
  174. if(ret != no_twig)
  175. {
  176. return ret;
  177. }
  178. }
  179. return no_twig;
  180. }
  181. size_t find_used_twig(size_t offset, size_t root = 0, size_t explored = 0) {
  182. auto v = get_twig(root);
  183. if(v.used && offset == 0)
  184. {
  185. return root;
  186. }
  187. ++explored;
  188. if(explored > max_depth) return no_twig;
  189. size_t cut = (1 << (max_depth + gp::math::log2(align))) >> explored;
  190. if(offset >= cut)
  191. {
  192. return find_used_twig(offset-cut, get_right(root), explored);
  193. } else {
  194. return find_used_twig(offset, get_left(root), explored);
  195. }
  196. }
  197. static bool empty_node(const buddy* me, size_t node) {
  198. gp_config::assertion(node < me->twig_explore_length, "bad emptyness test");
  199. auto p = me->get_twig(node);
  200. return !(
  201. p.used | p.used_children
  202. );
  203. }
  204. public:
  205. /**
  206. * @brief Construct a new empty buddy object
  207. */
  208. buddy()
  209. : data(gp::buffer<char>(nullptr,nullptr))
  210. , max_depth(0)
  211. , twig_explore_length(1 << max_depth)
  212. {}
  213. /**
  214. * @brief Construct a new buddy object from another allocator
  215. *
  216. * @param sz the size to allocate
  217. * @param allocator_p the source of the allocated memory
  218. */
  219. buddy(size_t sz, allocator& allocator_p)
  220. : allocator_v(allocator_p)
  221. , data(nullptr,nullptr)
  222. , max_depth(gp::math::msb(sz)-gp::math::msb(align))
  223. , twig_explore_length(1 << max_depth)
  224. {
  225. if(sz!=0 && (sz & (sz - 1)) == 0)
  226. {
  227. auto v=allocator_v.value().get().allocate(sz);
  228. if(v!=nullptr)
  229. {
  230. if((reinterpret_cast<intptr_t>(v) % align) ==0)
  231. {
  232. data=gp::buffer<char>(reinterpret_cast<char*>(v),reinterpret_cast<char*>(v)+sz);
  233. }
  234. else
  235. {
  236. allocator_v.value().get().deallocate(v);
  237. }
  238. }
  239. }
  240. }
  241. /**
  242. * @brief Construct a new buddy object from a memory location
  243. *
  244. * @param pos the location of the memory to manage
  245. * @param sz the size of the span to manage
  246. */
  247. buddy(char* pos, size_t sz)
  248. : data(pos,pos+sz)
  249. , max_depth(gp::math::msb(sz)-gp::math::msb(align))
  250. , twig_explore_length(1 << max_depth)
  251. {
  252. }
  253. virtual void* allocate(size_t sz)
  254. {
  255. auto depth = size_to_depth(sz);
  256. auto index = find_free_twig(depth);
  257. if(index == no_twig)
  258. {
  259. return nullptr;
  260. }
  261. auto pot = reinterpret_cast<char*>(
  262. (index - (1 << depth) + 1)*depth_to_size(depth)
  263. + reinterpret_cast<intptr_t>(&*data.begin())
  264. );
  265. if(!data.contains(pot)) {
  266. return nullptr;
  267. }
  268. all_over(index, [&](size_t idx){
  269. auto t = get_twig(idx);
  270. t.used_children = true;
  271. set_twig(idx, t);
  272. });
  273. auto t = get_twig(index);
  274. t.used = true;
  275. set_twig(index, t);
  276. return pot;
  277. }
  278. virtual bool try_reallocate(void*, size_t) {
  279. return false;
  280. }
  281. virtual bool deallocate(void* ptr)
  282. {
  283. if(data.contains((char*)ptr))
  284. {
  285. size_t integral_offset = reinterpret_cast<intptr_t>(ptr) - reinterpret_cast<intptr_t>(&*data.begin());
  286. auto index = find_used_twig(integral_offset);
  287. if(index == no_twig)
  288. {
  289. return false;
  290. }
  291. twig v = get_twig(index);
  292. v.used = false;
  293. v.used_children = false;
  294. set_twig(index, v);
  295. all_over(index, [&](size_t idx){
  296. auto l = get_twig(get_left(idx));
  297. auto r = get_twig(get_right(idx));
  298. set_twig(idx, 2*(l.used | l.used_children | r.used | r.used_children));
  299. });
  300. return true;
  301. }
  302. return false;
  303. }
  304. /**
  305. * @brief Checks if anything is still allocated in
  306. *
  307. * @return true if the allocator is completely empty
  308. * @return false if anything is still allocated in there
  309. */
  310. bool empty() const {
  311. buddy* addr = (buddy*)this;
  312. auto prepred = not_fn(&buddy::empty_node);
  313. auto pred = bind_front(prepred, addr);
  314. return empty_node(addr, 0) && !is_any_child(0, pred);
  315. }
  316. virtual ~buddy()
  317. {
  318. if(allocator_v.has_value())
  319. {
  320. allocator_v.value().get().deallocate(data.begin().data);
  321. }
  322. }
  323. };
  324. }