No Description
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.

tree_alloc.c 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. #include <stddef.h>
  2. #include <stdalign.h>
  3. #include "allocator_internal.h"
  4. TreeAlloc *insert_node_at(void *address, uintptr_t padding, uintptr_t align, uintptr_t size) {
  5. return NULL;
  6. }
  7. /*
  8. * Search for the node whose allocated region contains an address.
  9. */
  10. TreeAlloc *search_by_address(TreeAlloc *root, void *address) {
  11. TreeAlloc *head = root;
  12. while (1) {
  13. if (head > (TreeAlloc*) address) {
  14. if (head->left == NULL) {
  15. return NULL;
  16. } else {
  17. head = head->left;
  18. }
  19. } else {
  20. if (head->right == NULL || head->right > (TreeAlloc*) address) {
  21. return head;
  22. } else {
  23. head = head->right;
  24. }
  25. }
  26. }
  27. }
  28. static uintptr_t effective_size(TreeAlloc *head, uintptr_t padding, uintptr_t align) {
  29. return head->size - (align_after(head + padding, align) - (void*) head);
  30. }
  31. /*
  32. * This is the most optimistic estimate of size that we can use which also preserves the ordering over
  33. * the tree. I had planned to use effective_size before I realized that it would break the tree
  34. * ordering.
  35. */
  36. static uintptr_t pessimistic_size(TreeAlloc *head, uintptr_t padding, uintptr_t align) {
  37. return head->size - padding - align + 1;
  38. }
  39. TreeAlloc *search_by_size(TreeAlloc *root, uintptr_t padding, uintptr_t align, uintptr_t size) {
  40. TreeAlloc *head = root;
  41. while (1) {
  42. uintptr_t esize = pessimistic_size(head, padding, align);
  43. if (esize < size) {
  44. if (head->right == NULL) {
  45. return NULL;
  46. } else {
  47. head = head->right;
  48. }
  49. } else {
  50. if (head->left == NULL || pessimistic_size(head->left, padding, align) < size) {
  51. return head;
  52. } else {
  53. head = head->left;
  54. }
  55. }
  56. }
  57. }
  58. TreeAlloc *get_sibling(TreeAlloc *ta) {
  59. TreeAlloc *p = ta->parent;
  60. if (!p)
  61. return NULL;
  62. else if (p->left == ta)
  63. return p->right;
  64. else
  65. return p->left;
  66. }
  67. void rotate_left(TreeAlloc **root_ptr, TreeAlloc *ta) {
  68. TreeAlloc *parent, *tmp;
  69. parent = ta->parent;
  70. tmp = ta->right;
  71. if (!tmp) return;
  72. ta->right = tmp->left;
  73. tmp->left = ta;
  74. ta->parent = tmp;
  75. if (ta->right) ta->right->parent = ta;
  76. if (parent == NULL) {
  77. *root_ptr = tmp;
  78. } else {
  79. if (ta == parent->left)
  80. parent->left = tmp;
  81. else
  82. parent->right = tmp;
  83. }
  84. tmp->parent = parent;
  85. }
  86. void rotate_right(TreeAlloc **root_ptr, TreeAlloc *ta) {
  87. TreeAlloc *parent, *tmp;
  88. parent = ta->parent;
  89. tmp = ta->left;
  90. if (!tmp) return;
  91. ta->left = tmp->right;
  92. tmp->right = ta;
  93. ta->parent = tmp;
  94. if (ta->left) ta->left->parent = ta;
  95. if (parent == NULL) {
  96. *root_ptr = tmp;
  97. } else {
  98. if (ta == parent->left)
  99. parent->left = tmp;
  100. else
  101. parent->right = tmp;
  102. }
  103. tmp->parent = parent;
  104. }
  105. void repair_tree_after_insert(TreeAlloc **root_ptr, TreeAlloc *ta) {
  106. TreeAlloc *parent = ta->parent;
  107. if (parent == NULL) {
  108. ta->color = COLOR_BLACK;
  109. return;
  110. }
  111. TreeAlloc *grandparent = parent->parent;
  112. TreeAlloc *uncle = get_sibling(parent);
  113. if (parent->color == COLOR_RED) {
  114. if (uncle != NULL && uncle->color == COLOR_RED) {
  115. parent->color = COLOR_BLACK;
  116. uncle->color = COLOR_BLACK;
  117. grandparent->color = COLOR_RED;
  118. repair_tree_after_insert(root_ptr, grandparent);
  119. } else {
  120. if (ta == parent->left && parent == grandparent->left) {
  121. rotate_left(root_ptr, parent);
  122. ta = ta->left;
  123. } else {
  124. rotate_right(root_ptr, parent);
  125. ta = ta->right;
  126. }
  127. parent = ta->parent;
  128. grandparent = parent->parent;
  129. if (ta == parent->left) {
  130. rotate_right(root_ptr, grandparent);
  131. } else {
  132. rotate_left(root_ptr, grandparent);
  133. }
  134. parent->color = COLOR_BLACK;
  135. grandparent->color = COLOR_RED;
  136. }
  137. }
  138. }
  139. // TODO: Rewrite for self-balancing tree.
  140. void remove_node(TreeAlloc** root_ptr, TreeAlloc* node) {
  141. TreeAlloc *replace = NULL;
  142. if (node->left == NULL) {
  143. replace = node->right;
  144. } else if (node->right != NULL) {
  145. replace = node->right;
  146. TreeAlloc *head = node->left;
  147. while (head->right != NULL) {
  148. head = head->right;
  149. }
  150. head->right = head->parent->right;
  151. head->right->parent = head;
  152. }
  153. if (node->parent == NULL) {
  154. replace->parent = NULL;
  155. *root_ptr = replace;
  156. } else {
  157. if (node == node->parent->left) {
  158. node->parent->left = replace;
  159. } else {
  160. node->parent->right = replace;
  161. }
  162. }
  163. }
  164. // Inserts a node into an empty tree.
  165. void insert_singleton(TreeAlloc **root_ptr, TreeAlloc *to_insert) {
  166. *root_ptr = to_insert;
  167. to_insert->parent = NULL;
  168. repair_tree_after_insert(root_ptr, to_insert);
  169. }
  170. void insert_right(TreeAlloc** root_ptr, TreeAlloc* to_insert, TreeAlloc* after) {
  171. if (after->right != NULL) {
  172. after->right->parent = to_insert;
  173. to_insert->right = after->right;
  174. }
  175. after->right = to_insert;
  176. to_insert->parent = after;
  177. repair_tree_after_insert(root_ptr, to_insert);
  178. }
  179. void insert_left(TreeAlloc** root_ptr, TreeAlloc* to_insert, TreeAlloc* before) {
  180. if (before->left != NULL) {
  181. before->left->parent = to_insert;
  182. to_insert->left = before->left;
  183. }
  184. before->left = to_insert;
  185. to_insert->parent = before;
  186. repair_tree_after_insert(root_ptr, to_insert);
  187. }
  188. int add_new_region(Arena *arena, uintptr_t size, uintptr_t padding, uintptr_t align) {
  189. uintptr_t realsize = size + align + alignof(WatermarkAlloc) + padding - 1;
  190. if (realsize < MIN_NEW_MEM_SIZE) {
  191. realsize = MIN_NEW_MEM_SIZE;
  192. }
  193. FreeSpace *reg = (FreeSpace*) arena->get_new_region(realsize);
  194. if (reg == NULL) {
  195. arena->error("can't allocate a new memory region!");
  196. return 0;
  197. }
  198. FreeSpace *newreg = align_after(reg, alignof(WatermarkAlloc));
  199. newreg->left = NULL;
  200. newreg->right = NULL;
  201. realsize -= (void*) newreg - (void*) reg;
  202. realsize -= realsize % alignof(WatermarkAlloc);
  203. if (arena->root_freespace == NULL) {
  204. insert_singleton((TreeAlloc**) &arena->root_freespace, (TreeAlloc*) newreg);
  205. } else {
  206. FreeSpace *head = arena->root_freespace;
  207. while (head->right != NULL) {
  208. head = head->right;
  209. }
  210. insert_right((TreeAlloc**) arena->root_freespace, (TreeAlloc*) newreg, (TreeAlloc*) newreg);
  211. }
  212. return 1;
  213. }
  214. void unalloc(Arena *arena, void *addr) {
  215. if (arena->root_treealloc == NULL) {
  216. arena->error("attempt to unallocate when there are no allocations!");
  217. return;
  218. }
  219. // Find the node this address belongs to
  220. TreeAlloc *node = search_by_address(arena->root_treealloc, addr);
  221. if (node == NULL) {
  222. arena->error("attempt to free memory outside any allocations!");
  223. return;
  224. }
  225. // Handle the watermark allocator in this region
  226. if (node->type == RT_WATERMARK) {
  227. // TODO: handle watermark deallocation
  228. return;
  229. }
  230. // Get rid of it
  231. remove_node(&arena->root_treealloc, node);
  232. // If there's free space on either side of it, merge it with the free space into a bigger chunk of
  233. // free space.
  234. uintptr_t size = node->size;
  235. FreeSpace *start = (FreeSpace*) node;
  236. if (node->before != NULL && node->before->type == RT_FREESPACE) {
  237. start = (FreeSpace*) node->before;
  238. size += node->before->size;
  239. remove_node((TreeAlloc**) &arena->root_freespace, node->before);
  240. }
  241. if (node->after != NULL && node->after->type == RT_FREESPACE) {
  242. size += node->after->size;
  243. remove_node((TreeAlloc**) &arena->root_freespace, node->after);
  244. }
  245. start->type = RT_FREESPACE;
  246. start->size = size;
  247. // And finally, insert the resulting free space.
  248. if (arena->root_freespace == NULL) {
  249. insert_singleton((TreeAlloc**) &arena->root_freespace, (TreeAlloc*) start);
  250. } else {
  251. TreeAlloc *insert_point = search_by_size((TreeAlloc*) arena->root_freespace, 0, 1, size);
  252. if (insert_point == NULL) {
  253. TreeAlloc *head = (TreeAlloc*) arena->root_freespace;
  254. while (head->right != NULL) {
  255. head = head->right;
  256. }
  257. insert_right((TreeAlloc**) &arena->root_freespace, (TreeAlloc*) start, head);
  258. } else {
  259. insert_left((TreeAlloc**) &arena->root_freespace, (TreeAlloc*) start, insert_point);
  260. }
  261. }
  262. }
  263. void *alloc(Arena *arena, uintptr_t size, uintptr_t align) {
  264. uintptr_t actual_align = lcm(alignof(struct WatermarkAlloc), align);
  265. if (arena->root_freespace == NULL) {
  266. // Handle being out of freespace.
  267. if (!add_new_region(arena, size, sizeof(TreeAlloc), actual_align)) {
  268. return NULL;
  269. }
  270. return alloc(arena, size, align);
  271. } else {
  272. TreeAlloc *region = search_by_size((TreeAlloc*) arena->root_freespace, sizeof(TreeAlloc), actual_align, size);
  273. if (region == NULL) {
  274. // Handle insufficient freespace or fragmentation.
  275. if (!add_new_region(arena, size, sizeof(TreeAlloc), actual_align)) {
  276. return NULL;
  277. }
  278. return alloc(arena, size, align);
  279. }
  280. remove_node((TreeAlloc**) &arena->root_freespace, region);
  281. void *true_end = align_after(align_after(region + sizeof(TreeAlloc), actual_align) + size, alignof(WatermarkAlloc));
  282. // The size of the new allocation (adjusted for region header and alignment
  283. uintptr_t new_size = true_end - (void*) region;
  284. // The size of the free space region following the new allocation
  285. uintptr_t new_free_size = region->size - new_size;
  286. region->right = NULL;
  287. region->left = NULL;
  288. region->type = RT_TREE_NODE;
  289. if (arena->root_treealloc == NULL) {
  290. insert_singleton((TreeAlloc**) &arena->root_treealloc, region);
  291. } else {
  292. TreeAlloc *insert_point = search_by_address((TreeAlloc*) arena->root_treealloc, region);
  293. insert_right(&arena->root_treealloc, region, insert_point);
  294. }
  295. if (arena->root_treealloc == NULL) {
  296. insert_singleton(&arena->root_treealloc, region);
  297. } else {
  298. TreeAlloc *insert_point = search_by_address((TreeAlloc*) arena->root_treealloc, region);
  299. insert_right(&arena->root_treealloc, region, insert_point);
  300. }
  301. if (new_free_size >= sizeof(FreeSpace)) {
  302. // If there's enough free space after the allocation, use it!
  303. region->size = new_size; // Safe because the allocated region tree is not sorted by size.
  304. FreeSpace *new_free = (FreeSpace*) ((void*) region + new_size);
  305. new_free->left = NULL;
  306. new_free->right = NULL;
  307. new_free->type = RT_FREESPACE;
  308. new_free->size = new_free_size;
  309. if (arena->root_freespace == NULL) {
  310. insert_singleton((TreeAlloc**) &arena->root_freespace, (TreeAlloc*) new_free);
  311. } else {
  312. FreeSpace *insert_point = (FreeSpace*) search_by_size((TreeAlloc*) arena->root_freespace, 0, 1, new_free_size);
  313. insert_left((TreeAlloc**) &arena->root_freespace, (TreeAlloc*) new_free, (TreeAlloc*) insert_point);
  314. }
  315. }
  316. return align_after(region + sizeof(TreeAlloc), actual_align);
  317. }
  318. }
  319. void *alloc_growable(Arena *arena, uintptr_t size, uintptr_t align) {
  320. // TODO: Basically the same as above, but put the allocated region in the center of the largest free
  321. // space. Due to alignment and whatnot, the code will be gory.
  322. return NULL;
  323. }