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.

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