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.

315 lines
6.9 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
  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 <type_traits>
  10. namespace gp{
  11. template<typename page_allocator = gp::dummy_allocator, size_t max_msb = 24, size_t align = 8>
  12. class buddy{
  13. struct twig {
  14. bool used : 1;
  15. bool used_children : 1;
  16. twig(uint8_t src) {
  17. used = 1 & src;
  18. used_children = 2 & src;
  19. }
  20. operator uint8_t() {
  21. return 1 * used + 2 * used_children;
  22. }
  23. };
  24. struct bundle {
  25. uint8_t a : 2;
  26. uint8_t b : 2;
  27. uint8_t c : 2;
  28. uint8_t d : 2;
  29. bundle() {
  30. a = 0; b = 0; c = 0; d = 0;
  31. }
  32. };
  33. page_allocator allocator;
  34. gp::buffer<char> data;
  35. const size_t max_depth;
  36. const size_t twig_explore_length;
  37. static constexpr size_t max_theoric_depth = max_msb - gp::math::msb(align);
  38. static constexpr size_t required_twigs = (1 << (max_theoric_depth + 1)) - 1;
  39. /**
  40. * ((max allocatable size - min allocatable size) ** 2 - 1) / 4 twigs in a bundle
  41. **/
  42. static constexpr size_t span_size = required_twigs / 4 + (required_twigs % 4 != 0);
  43. gp::array<bundle, span_size> stack;
  44. /**
  45. * This code has been manually hecked and will always return.
  46. * If you find a case where it doesn't, please file an issue.
  47. **/
  48. #pragma clang diagnostic push
  49. #pragma clang diagnostic ignored "-Wreturn-type"
  50. #pragma GCC diagnostic push
  51. #pragma GCC diagnostic ignored "-Wreturn-type"
  52. twig get_twig(size_t idx) const {
  53. auto far = idx / 4;
  54. auto local = idx % 4;
  55. switch(local) {
  56. case 0:
  57. return stack[far].a;
  58. case 1:
  59. return stack[far].b;
  60. case 2:
  61. return stack[far].c;
  62. case 3:
  63. return stack[far].d;
  64. }
  65. }
  66. #pragma GCC diagnostic pop
  67. #pragma clang diagnostic pop
  68. void set_twig(size_t idx, twig v) {
  69. auto far = idx / 4;
  70. auto local = idx % 4;
  71. auto& group = stack[far];
  72. switch(local) {
  73. case 0:
  74. group.a = v;
  75. return;
  76. case 1:
  77. group.b = v;
  78. return;
  79. case 2:
  80. group.c = v;
  81. return;
  82. case 3:
  83. group.d = v;
  84. return;
  85. }
  86. }
  87. constexpr size_t size_to_depth(size_t sz) {
  88. size_t pow2 = gp::math::msb(sz) - gp::math::msb(align);
  89. return gp::clamp(
  90. (size_t)0 ,
  91. max_depth - pow2,
  92. max_depth
  93. );
  94. }
  95. constexpr size_t depth_to_size(size_t depth) {
  96. return 1 << (max_depth - depth + gp::math::msb(align));
  97. }
  98. constexpr size_t get_left(size_t index) const {
  99. return ((index + 1) << 1) - 1;
  100. }
  101. constexpr size_t get_right(size_t index) const {
  102. return ((index + 1) << 1);
  103. }
  104. template<typename function>
  105. void all_under(size_t index, function func) {
  106. size_t left = get_left(index);
  107. size_t right = get_right(index);
  108. all_under(left, func);
  109. all_under(right, func);
  110. func(left);
  111. func(right);
  112. }
  113. template<typename function>
  114. void all_over(size_t index, function func) {
  115. if(index != 0) {
  116. size_t parent = ((index + 1) >> 1) - 1;
  117. func(parent);
  118. if(parent != 0)
  119. all_over(parent, func);
  120. }
  121. }
  122. template<typename function>
  123. bool is_any_child(size_t index, function func) const {
  124. size_t left = get_left(index);
  125. size_t right = get_right(index);
  126. if(left < twig_explore_length && right < twig_explore_length) {
  127. if(func(left)) return true;
  128. if(func(right)) return true;
  129. if(is_any_child(left, func)) return true;
  130. if(is_any_child(right, func)) return true;
  131. }
  132. return false;
  133. }
  134. static constexpr size_t no_twig = -1;
  135. size_t find_free_twig(size_t depth, size_t root = 0, size_t explored = 0) const {
  136. auto v = get_twig(root);
  137. if(depth == explored) {
  138. if(v.used || v.used_children)
  139. {
  140. return no_twig;
  141. } else {
  142. return root;
  143. }
  144. } else {
  145. if(v.used)
  146. {
  147. return no_twig;
  148. }
  149. ++explored;
  150. auto ret = find_free_twig(depth, get_right(root), explored);
  151. if(ret != no_twig)
  152. {
  153. return ret;
  154. }
  155. ret = find_free_twig(depth, get_left(root), explored);
  156. if(ret != no_twig)
  157. {
  158. return ret;
  159. }
  160. }
  161. return no_twig;
  162. }
  163. size_t find_used_twig(size_t offset, size_t root = 0, size_t explored = 0) {
  164. auto v = get_twig(root);
  165. if(v.used && offset == 0)
  166. {
  167. return root;
  168. }
  169. ++explored;
  170. if(explored > max_depth) return no_twig;
  171. size_t cut = (1 << (max_depth + gp::math::log2(align))) >> explored;
  172. if(offset >= cut)
  173. {
  174. return find_used_twig(offset-cut, get_right(root), explored);
  175. } else {
  176. return find_used_twig(offset, get_left(root), explored);
  177. }
  178. }
  179. static bool empty_node(const buddy* me, size_t node) {
  180. gp_config::assertion(node < me->twig_explore_length, "bad emptyness test");
  181. auto p = me->get_twig(node);
  182. return !(
  183. p.used | p.used_children
  184. );
  185. }
  186. public:
  187. buddy()
  188. : data(gp::buffer<char>(nullptr,nullptr))
  189. , max_depth(0)
  190. , twig_explore_length(1 << max_depth)
  191. {}
  192. buddy(size_t sz)
  193. : data(nullptr,nullptr)
  194. , max_depth(gp::math::msb(sz)-gp::math::msb(align))
  195. , twig_explore_length(1 << max_depth)
  196. {
  197. if(sz!=0 && (sz & (sz - 1)) == 0)
  198. {
  199. auto v=allocator.allocate(sz);
  200. if(v!=nullptr)
  201. {
  202. if((reinterpret_cast<intptr_t>(v) % align) ==0)
  203. {
  204. data=gp::buffer<char>(reinterpret_cast<char*>(v),reinterpret_cast<char*>(v)+sz);
  205. }
  206. else
  207. {
  208. allocator.deallocate(v);
  209. }
  210. }
  211. }
  212. }
  213. buddy(char* pos, size_t sz)
  214. : data(pos,pos+sz)
  215. , max_depth(gp::math::msb(sz)-gp::math::msb(align))
  216. , twig_explore_length(1 << max_depth)
  217. {
  218. }
  219. void* allocate(size_t sz)
  220. {
  221. auto depth = size_to_depth(sz);
  222. auto index = find_free_twig(depth);
  223. if(index == no_twig)
  224. {
  225. return nullptr;
  226. }
  227. auto pot = reinterpret_cast<char*>(
  228. (index - (1 << depth) + 1)*depth_to_size(depth)
  229. + reinterpret_cast<intptr_t>(&*data.begin())
  230. );
  231. if(!data.contains(pot)) {
  232. return nullptr;
  233. }
  234. all_over(index, [&](size_t idx){
  235. auto t = get_twig(idx);
  236. t.used_children = true;
  237. set_twig(idx, t);
  238. });
  239. auto t = get_twig(index);
  240. t.used = true;
  241. set_twig(index, t);
  242. return pot;
  243. }
  244. constexpr bool try_reallocate(void*, size_t) {
  245. return false;
  246. }
  247. bool deallocate(void* ptr)
  248. {
  249. if(data.contains((char*)ptr))
  250. {
  251. size_t integral_offset = reinterpret_cast<intptr_t>(ptr) - reinterpret_cast<intptr_t>(&*data.begin());
  252. auto index = find_used_twig(integral_offset);
  253. if(index == no_twig)
  254. {
  255. return false;
  256. }
  257. twig v = get_twig(index);
  258. v.used = false;
  259. v.used_children = false;
  260. set_twig(index, v);
  261. all_over(index, [&](size_t idx){
  262. auto l = get_twig(get_left(idx));
  263. auto r = get_twig(get_right(idx));
  264. set_twig(idx, 2*(l.used | l.used_children | r.used | r.used_children));
  265. });
  266. return true;
  267. }
  268. return false;
  269. }
  270. bool empty() const {
  271. buddy* addr = (buddy*)this;
  272. auto prepred = not_fn(&buddy::empty_node);
  273. auto pred = bind_front(prepred, addr);
  274. return empty_node(addr, 0) && !is_any_child(0, pred);
  275. }
  276. ~buddy()
  277. {
  278. allocator.deallocate(data.begin().data);
  279. }
  280. };
  281. }