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.

479 lines
11 KiB

пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 3 година
пре 4 година
пре 3 година
пре 4 година
пре 3 година
пре 4 година
пре 4 година
пре 3 година
пре 4 година
пре 4 година
пре 3 година
пре 4 година
пре 4 година
пре 3 година
пре 4 година
пре 3 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 3 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 3 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 3 година
пре 4 година
пре 4 година
пре 4 година
пре 3 година
пре 4 година
пре 4 година
пре 3 година
пре 4 година
пре 3 година
пре 4 година
пре 3 година
пре 4 година
пре 3 година
пре 4 година
пре 3 година
пре 4 година
пре 4 година
пре 4 година
пре 3 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 3 година
пре 4 година
пре 3 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 3 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 3 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
пре 3 година
пре 4 година
пре 4 година
пре 4 година
пре 3 година
пре 4 година
пре 4 година
пре 4 година
пре 3 година
пре 4 година
пре 4 година
пре 4 година
пре 3 година
пре 4 година
пре 3 година
пре 4 година
пре 4 година
пре 4 година
пре 4 година
  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. /**
  23. * @brief an allocation tree node, made into an easily packable struct
  24. */
  25. struct twig {
  26. bool used : 1;
  27. bool used_children : 1;
  28. twig(uint8_t src) noexcept {
  29. used = 1 & src;
  30. used_children = 2 & src;
  31. }
  32. operator uint8_t() noexcept {
  33. return 1 * used + 2 * used_children;
  34. }
  35. };
  36. /**
  37. * @brief a structure to pack 4 twig within
  38. */
  39. struct bundle {
  40. uint8_t a : 2;
  41. uint8_t b : 2;
  42. uint8_t c : 2;
  43. uint8_t d : 2;
  44. bundle() noexcept {
  45. a = 0; b = 0; c = 0; d = 0;
  46. }
  47. };
  48. /**
  49. * @brief a meta allocator to allocate the memory used and deallocate it later
  50. */
  51. gp::optional<gp::reference_wrapper<allocator>> allocator_v;
  52. /**
  53. * @brief The actual buffer where data is stored
  54. *
  55. */
  56. gp::buffer<char> data;
  57. const size_t max_depth;
  58. const size_t twig_explore_length;
  59. /**
  60. * @brief The depth of the tree required to allocate
  61. */
  62. static constexpr size_t max_theoric_depth = max_msb - gp::math::msb(align);
  63. /**
  64. * @brief The actual number of twigs to support the specified depth
  65. */
  66. static constexpr size_t required_twigs = (1 << (max_theoric_depth + 1)) - 1;
  67. /**
  68. * @brief ((max allocatable size - min allocatable size) ** 2 - 1) / 4 twigs in a bundle
  69. **/
  70. static constexpr size_t span_size = required_twigs / 4 + (required_twigs % 4 != 0);
  71. /**
  72. * @brief The array of twigs (in bundles) aka the metadata
  73. */
  74. gp::array<bundle, span_size> stack;
  75. /**
  76. * This code has been manually checked and will always return.
  77. * If you find a case where it doesn't, please file an issue.
  78. **/
  79. #pragma clang diagnostic push
  80. #pragma clang diagnostic ignored "-Wreturn-type"
  81. #pragma GCC diagnostic push
  82. #pragma GCC diagnostic ignored "-Wreturn-type"
  83. /**
  84. * @brief Get the value of the twig at the specified index
  85. *
  86. * @param idx
  87. * @return twig A copy of the specified twig
  88. */
  89. twig get_twig(size_t idx) const noexcept {
  90. auto far = idx / 4;
  91. auto local = idx % 4;
  92. switch(local) {
  93. case 0:
  94. return stack[far].a;
  95. case 1:
  96. return stack[far].b;
  97. case 2:
  98. return stack[far].c;
  99. case 3:
  100. return stack[far].d;
  101. }
  102. }
  103. #pragma GCC diagnostic pop
  104. #pragma clang diagnostic pop
  105. /**
  106. * @brief Set the twig at the specified index to the value of the heredescribed twig
  107. *
  108. * @param idx
  109. * @param v
  110. */
  111. void set_twig(size_t idx, twig v) noexcept {
  112. auto far = idx / 4;
  113. auto local = idx % 4;
  114. auto& group = stack[far];
  115. switch(local) {
  116. case 0:
  117. group.a = v;
  118. return;
  119. case 1:
  120. group.b = v;
  121. return;
  122. case 2:
  123. group.c = v;
  124. return;
  125. case 3:
  126. group.d = v;
  127. return;
  128. }
  129. }
  130. /**
  131. * @brief Deduces the depth to explore to allocate the specified size
  132. *
  133. * @param sz
  134. * @return size_t
  135. */
  136. constexpr size_t size_to_depth(size_t sz) noexcept {
  137. size_t pow2 = gp::math::msb(sz) - gp::math::msb(align);
  138. return gp::clamp(
  139. (size_t)0 ,
  140. max_depth - pow2,
  141. max_depth
  142. );
  143. }
  144. /**
  145. * @brief Deduces the size from the specified depth
  146. *
  147. * @param depth
  148. * @return size_t
  149. */
  150. constexpr size_t depth_to_size(size_t depth) noexcept {
  151. return 1 << (max_depth - depth + gp::math::msb(align));
  152. }
  153. /**
  154. * @brief Get the left child index from the specified index
  155. *
  156. * @param index
  157. * @return size_t
  158. */
  159. constexpr size_t get_left(size_t index) const noexcept {
  160. return ((index + 1) << 1) - 1;
  161. }
  162. /**
  163. * @brief Get the right child index from the specified index
  164. *
  165. * @param index
  166. * @return size_t
  167. */
  168. constexpr size_t get_right(size_t index) const noexcept{
  169. return ((index + 1) << 1);
  170. }
  171. /**
  172. * @brief Recursively applies a function to the children of a given node
  173. *
  174. * @param index the starting node
  175. * @param func the function to apply downwards
  176. */
  177. template<typename function>
  178. void all_under(size_t index, function func) {
  179. size_t left = get_left(index);
  180. size_t right = get_right(index);
  181. all_under(left, func);
  182. all_under(right, func);
  183. func(left);
  184. func(right);
  185. }
  186. /**
  187. * @brief Recursively applies a function to the chain of parents of the given node
  188. *
  189. * @param index the starting node
  190. * @param func the function to apply upwards
  191. */
  192. template<typename function>
  193. void all_over(size_t index, function func) {
  194. if(index != 0) {
  195. size_t parent = ((index + 1) >> 1) - 1;
  196. func(parent);
  197. if(parent != 0)
  198. all_over(parent, func);
  199. }
  200. }
  201. /**
  202. * @brief Recursively checks if any child matches a given predicate
  203. *
  204. * @param index The parent
  205. * @param func The predicate
  206. * @return true if the predicate matched once
  207. * @return false if the predicate never matched
  208. */
  209. template<typename function>
  210. bool is_any_child(size_t index, function func) const {
  211. size_t left = get_left(index);
  212. size_t right = get_right(index);
  213. if(left < twig_explore_length && right < twig_explore_length) {
  214. if(func(left)) return true;
  215. if(func(right)) return true;
  216. if(is_any_child(left, func)) return true;
  217. if(is_any_child(right, func)) return true;
  218. }
  219. return false;
  220. }
  221. /**
  222. * @brief a constant used to specify that no twig has been found
  223. */
  224. static constexpr size_t no_twig = -1;
  225. /**
  226. * @brief Finds a twig that represent free memory
  227. *
  228. * It's free real estate!
  229. *
  230. * @param depth the depth to allocate
  231. * @param root the root to look from
  232. * @param explored the current depth we sit at
  233. * @return size_t
  234. */
  235. size_t find_free_twig(size_t depth, size_t root = 0, size_t explored = 0) const {
  236. auto v = get_twig(root);
  237. if(depth == explored) {
  238. if(v.used || v.used_children)
  239. {
  240. return no_twig;
  241. } else {
  242. return root;
  243. }
  244. } else {
  245. if(v.used)
  246. {
  247. return no_twig;
  248. }
  249. ++explored;
  250. auto ret = find_free_twig(depth, get_right(root), explored);
  251. if(ret != no_twig)
  252. {
  253. return ret;
  254. }
  255. ret = find_free_twig(depth, get_left(root), explored);
  256. if(ret != no_twig)
  257. {
  258. return ret;
  259. }
  260. }
  261. return no_twig;
  262. }
  263. /**
  264. * @brief Recursively looks for the highest filled nodewith the given offset
  265. *
  266. * @param offset The offset to look for
  267. * @param root the root to look from
  268. * @param explored the explored depth
  269. * @return size_t
  270. */
  271. size_t find_used_twig(size_t offset, size_t root = 0, size_t explored = 0) {
  272. auto v = get_twig(root);
  273. if(v.used && offset == 0)
  274. {
  275. return root;
  276. }
  277. ++explored;
  278. if(explored > max_depth) return no_twig;
  279. size_t cut = (1 << (max_depth + gp::math::log2(align))) >> explored;
  280. if(offset >= cut)
  281. {
  282. return find_used_twig(offset-cut, get_right(root), explored);
  283. } else {
  284. return find_used_twig(offset, get_left(root), explored);
  285. }
  286. }
  287. static bool empty_node(const buddy* me, size_t node) {
  288. gp_config::assertion(node < me->twig_explore_length, "bad emptyness test");
  289. auto p = me->get_twig(node);
  290. return !(
  291. p.used | p.used_children
  292. );
  293. }
  294. public:
  295. /**
  296. * @brief Construct a new empty buddy object
  297. */
  298. buddy()
  299. : data(gp::buffer<char>(nullptr,nullptr))
  300. , max_depth(0)
  301. , twig_explore_length(1 << max_depth)
  302. {}
  303. /**
  304. * @brief Construct a new buddy object from another allocator
  305. *
  306. * @param sz the size to allocate
  307. * @param allocator_p the source of the allocated memory
  308. */
  309. buddy(size_t sz, allocator& allocator_p)
  310. : allocator_v(allocator_p)
  311. , data(nullptr,nullptr)
  312. , max_depth(gp::math::msb(sz)-gp::math::msb(align))
  313. , twig_explore_length(1 << max_depth)
  314. {
  315. if(sz!=0 && (sz & (sz - 1)) == 0)
  316. {
  317. auto v=allocator_v.value().get().allocate(sz);
  318. if(v!=nullptr)
  319. {
  320. if((reinterpret_cast<intptr_t>(v) % align) ==0)
  321. {
  322. data=gp::buffer<char>(reinterpret_cast<char*>(v),reinterpret_cast<char*>(v)+sz);
  323. }
  324. else
  325. {
  326. allocator_v.value().get().deallocate(v);
  327. }
  328. }
  329. }
  330. }
  331. /**
  332. * @brief Construct a new buddy object from a memory location
  333. *
  334. * @param pos the location of the memory to manage
  335. * @param sz the size of the span to manage
  336. */
  337. buddy(char* pos, size_t sz)
  338. : data(pos,pos+sz)
  339. , max_depth(gp::math::msb(sz)-gp::math::msb(align))
  340. , twig_explore_length(1 << max_depth)
  341. {
  342. }
  343. /**
  344. * @brief Allocates memory
  345. *
  346. * @param sz The size of memory to allocate
  347. * @return void* the allocated memory OR nullptr
  348. */
  349. virtual void* allocate(size_t sz)
  350. {
  351. auto depth = size_to_depth(sz);
  352. auto index = find_free_twig(depth);
  353. if(index == no_twig)
  354. {
  355. return nullptr;
  356. }
  357. auto pot = reinterpret_cast<char*>(
  358. (index - (1 << depth) + 1)*depth_to_size(depth)
  359. + reinterpret_cast<intptr_t>(&*data.begin())
  360. );
  361. if(!data.contains(pot)) {
  362. return nullptr;
  363. }
  364. all_over(index, [&](size_t idx){
  365. auto t = get_twig(idx);
  366. t.used_children = true;
  367. set_twig(idx, t);
  368. });
  369. auto t = get_twig(index);
  370. t.used = true;
  371. set_twig(index, t);
  372. return pot;
  373. }
  374. /**
  375. * @brief Tries to reallocate (UNIMPLEMENTED)
  376. *
  377. * @return false
  378. */
  379. virtual bool try_reallocate(void*, size_t) {
  380. return false;
  381. }
  382. /**
  383. * @brief Tries to deallocate the given memory
  384. *
  385. * @param ptr the pointer to the memory to deallocate
  386. * @return true if everything went fine
  387. * @return false if deallocation failed
  388. */
  389. virtual bool deallocate(void* ptr)
  390. {
  391. if(data.contains((char*)ptr))
  392. {
  393. size_t integral_offset = reinterpret_cast<intptr_t>(ptr) - reinterpret_cast<intptr_t>(&*data.begin());
  394. auto index = find_used_twig(integral_offset);
  395. if(index == no_twig)
  396. {
  397. return false;
  398. }
  399. twig v = get_twig(index);
  400. v.used = false;
  401. v.used_children = false;
  402. set_twig(index, v);
  403. all_over(index, [&](size_t idx){
  404. auto l = get_twig(get_left(idx));
  405. auto r = get_twig(get_right(idx));
  406. // And now for the tricky bit:
  407. // Sets the "used_children" bit if any of the collected twigs has a bit set
  408. set_twig(idx, 2*(l.used | l.used_children | r.used | r.used_children));
  409. });
  410. return true;
  411. }
  412. return false;
  413. }
  414. /**
  415. * @brief Checks if anything is still allocated in
  416. *
  417. * @return true if the allocator is completely empty
  418. * @return false if anything is still allocated in there
  419. */
  420. bool empty() const noexcept {
  421. const buddy* addr = (buddy*)this;
  422. const auto prepred = not_fn(&buddy::empty_node);
  423. const auto pred = bind_front(prepred, addr);
  424. return empty_node(addr, 0) && !is_any_child(0, pred);
  425. }
  426. /**
  427. * @brief Destroy the buddy object, will wrink out the space if it was created with an allocator
  428. */
  429. virtual ~buddy()
  430. {
  431. if(allocator_v.has_value())
  432. {
  433. allocator_v.value().get().deallocate(data.begin().data);
  434. }
  435. }
  436. };
  437. }