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.

341 lines
10 KiB

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