General Purpose library for Freestanding C++ and POSIX systems
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

310 строки
6.8 KiB

4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
4 лет назад
  1. #pragma once
  2. #include "gp_config.hpp"
  3. #include "gp/buffer.hpp"
  4. #include "gp/array.hpp"
  5. #include "gp/math.hpp"
  6. #include <type_traits>
  7. #include <gp/algorithm/tmp_manip.hpp>
  8. #include <gp/algorithm/modifiers.hpp>
  9. #include <gp/allocator/dummy.hpp>
  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. twig get_twig(size_t idx) const {
  51. auto far = idx / 4;
  52. auto local = idx % 4;
  53. switch(local) {
  54. case 0:
  55. return stack[far].a;
  56. case 1:
  57. return stack[far].b;
  58. case 2:
  59. return stack[far].c;
  60. case 3:
  61. return stack[far].d;
  62. }
  63. }
  64. #pragma clang diagnostic pop
  65. void set_twig(size_t idx, twig v) {
  66. auto far = idx / 4;
  67. auto local = idx % 4;
  68. auto& group = stack[far];
  69. switch(local) {
  70. case 0:
  71. group.a = v;
  72. return;
  73. case 1:
  74. group.b = v;
  75. return;
  76. case 2:
  77. group.c = v;
  78. return;
  79. case 3:
  80. group.d = v;
  81. return;
  82. }
  83. }
  84. constexpr size_t size_to_depth(size_t sz) {
  85. size_t pow2 = gp::math::msb(sz) - gp::math::msb(align);
  86. return gp::clamp(
  87. (size_t)0 ,
  88. max_depth - pow2,
  89. max_depth
  90. );
  91. }
  92. constexpr size_t depth_to_size(size_t depth) {
  93. return 1 << (max_depth - depth + gp::math::msb(align));
  94. }
  95. constexpr size_t get_left(size_t index) const {
  96. return ((index + 1) << 1) - 1;
  97. }
  98. constexpr size_t get_right(size_t index) const {
  99. return ((index + 1) << 1);
  100. }
  101. template<typename function>
  102. void all_under(size_t index, function func) {
  103. size_t left = get_left(index);
  104. size_t right = get_right(index);
  105. all_under(left, func);
  106. all_under(right, func);
  107. func(left);
  108. func(right);
  109. }
  110. template<typename function>
  111. void all_over(size_t index, function func) {
  112. if(index != 0) {
  113. size_t parent = ((index + 1) >> 1) - 1;
  114. func(parent);
  115. if(parent != 0)
  116. all_over(parent, func);
  117. }
  118. }
  119. template<typename function>
  120. bool is_any_child(size_t index, function func) const {
  121. size_t left = get_left(index);
  122. size_t right = get_right(index);
  123. if(left < twig_explore_length && right < twig_explore_length) {
  124. if(func(left)) return true;
  125. if(func(right)) return true;
  126. if(is_any_child(left, func)) return true;
  127. if(is_any_child(right, func)) return true;
  128. }
  129. return false;
  130. }
  131. static constexpr size_t no_twig = -1;
  132. size_t find_free_twig(size_t depth, size_t root = 0, size_t explored = 0) const {
  133. auto v = get_twig(root);
  134. if(depth == explored) {
  135. if(v.used || v.used_children)
  136. {
  137. return no_twig;
  138. } else {
  139. return root;
  140. }
  141. } else {
  142. if(v.used)
  143. {
  144. return no_twig;
  145. }
  146. ++explored;
  147. auto ret = find_free_twig(depth, get_right(root), explored);
  148. if(ret != no_twig)
  149. {
  150. return ret;
  151. }
  152. ret = find_free_twig(depth, get_left(root), explored);
  153. if(ret != no_twig)
  154. {
  155. return ret;
  156. }
  157. }
  158. return no_twig;
  159. }
  160. size_t find_used_twig(size_t offset, size_t root = 0, size_t explored = 0) {
  161. auto v = get_twig(root);
  162. if(v.used && offset == 0)
  163. {
  164. return root;
  165. }
  166. ++explored;
  167. if(explored > max_depth) return no_twig;
  168. size_t cut = (1 << (max_depth + gp::math::log2(align))) >> explored;
  169. if(offset >= cut)
  170. {
  171. return find_used_twig(offset-cut, get_right(root), explored);
  172. } else {
  173. return find_used_twig(offset, get_left(root), explored);
  174. }
  175. }
  176. static bool empty_node(const buddy* me, size_t node) {
  177. gp_config::assertion(node < me->twig_explore_length, "bad emptyness test");
  178. auto p = me->get_twig(node);
  179. return !(
  180. p.used | p.used_children
  181. );
  182. }
  183. public:
  184. buddy()
  185. : data(gp::buffer<char>(nullptr,nullptr))
  186. , max_depth(0)
  187. , twig_explore_length(1 << max_depth)
  188. {}
  189. buddy(size_t sz)
  190. : data(nullptr,nullptr)
  191. , max_depth(gp::math::msb(sz)-gp::math::msb(align))
  192. , twig_explore_length(1 << max_depth)
  193. {
  194. if(sz!=0 && (sz & (sz - 1)) == 0)
  195. {
  196. auto v=allocator.allocate(sz);
  197. if(v!=nullptr)
  198. {
  199. if((reinterpret_cast<intptr_t>(v) % align) ==0)
  200. {
  201. data=gp::buffer<char>(reinterpret_cast<char*>(v),reinterpret_cast<char*>(v)+sz);
  202. }
  203. else
  204. {
  205. allocator.deallocate(v);
  206. }
  207. }
  208. }
  209. }
  210. buddy(char* pos, size_t sz)
  211. : data(pos,pos+sz)
  212. , max_depth(gp::math::msb(sz)-gp::math::msb(align))
  213. , twig_explore_length(1 << max_depth)
  214. {
  215. }
  216. void* allocate(size_t sz)
  217. {
  218. auto depth = size_to_depth(sz);
  219. auto index = find_free_twig(depth);
  220. if(index == no_twig)
  221. {
  222. return nullptr;
  223. }
  224. auto pot = reinterpret_cast<char*>(
  225. (index - (1 << depth) + 1)*depth_to_size(depth)
  226. + reinterpret_cast<intptr_t>(&*data.begin())
  227. );
  228. if(!data.contains(pot)) {
  229. return nullptr;
  230. }
  231. all_over(index, [&](size_t idx){
  232. auto t = get_twig(idx);
  233. t.used_children = true;
  234. set_twig(idx, t);
  235. });
  236. auto t = get_twig(index);
  237. t.used = true;
  238. set_twig(index, t);
  239. return pot;
  240. }
  241. constexpr bool try_reallocate(void*, size_t) {
  242. return false;
  243. }
  244. bool deallocate(void* ptr)
  245. {
  246. if(data.contains((char*)ptr))
  247. {
  248. size_t integral_offset = reinterpret_cast<intptr_t>(ptr) - reinterpret_cast<intptr_t>(&*data.begin());
  249. auto index = find_used_twig(integral_offset);
  250. if(index == no_twig)
  251. {
  252. return false;
  253. }
  254. twig v = get_twig(index);
  255. v.used = false;
  256. v.used_children = false;
  257. set_twig(index, v);
  258. all_over(index, [&](size_t idx){
  259. auto l = get_twig(get_left(idx));
  260. auto r = get_twig(get_right(idx));
  261. set_twig(idx, 2*(l.used | l.used_children | r.used | r.used_children));
  262. });
  263. return true;
  264. }
  265. return false;
  266. }
  267. bool empty() const {
  268. buddy* addr = (buddy*)this;
  269. auto prepred = not_fn(&buddy::empty_node);
  270. auto pred = bind_front(prepred, addr);
  271. return empty_node(addr, 0) && !is_any_child(0, pred);
  272. }
  273. ~buddy()
  274. {
  275. allocator.deallocate(data.begin().data);
  276. }
  277. };
  278. }