/* * Remove the provided entry from whichever list it's currently in. This * assumes that the entry is in a list. You don't need to provide the list * because the lists are circular, so the list's pointers will automatically * be updated if the first or last entries are removed. */ private static void List_remove(List_t *entry) { List_t *prev = entry->prev; List_t *next = entry->next; prev->next = next; next->prev = prev; }
/* * Append the provided entry to the end of the list. This assumes the entry * isn't in a list already because it overwrites the linked list pointers. */ private static void List_push(List_t *list, List_t *entry) { List_t *prev = list->prev; entry->prev = prev; entry->next = list; prev->next = entry; list->prev = entry; }
/* * Remove and return the first entry in the list or NULL if the list is empty. */ private static List_t *List_pop(List_t *list) { List_t *back = list->prev; if (back == list) { return(null); } List_remove(back); return(back); }
/* * This array represents a linearized binary tree of bits. Every possible * allocation larger than MIN_ALLOC has a node in this tree (and therefore a * bit in this array). * * Given the index for a node, linearized binary trees allow you to traverse to * the parent node or the child nodes just by doing simple arithmetic on the * index: * * - Move to parent: index = (index - 1) / 2; * - Move to left child: index = index * 2 + 1; * - Move to right child: index = index * 2 + 2; * - Move to sibling: index = ((index - 1) ^ 1) + 1; * * Each node in this tree can be in one of several states: * * - UNUSED (both children are UNUSED) * - SPLIT (one child is UNUSED and the other child isn't) * - USED (neither children are UNUSED) * * These states take two bits to store. However, it turns out we have enough * information to distinguish between UNUSED and USED from context, so we only * need to store SPLIT or not, which only takes a single bit. * * Note that we don't need to store any nodes for allocations of size MIN_ALLOC * since we only ever care about parent nodes. */ //static uint8_t node_is_split[(1 << (BUCKET_COUNT - 1)) / 8]; //protected byte* node_is_split; /* * This is the starting address of the address range for this allocator. Every * returned allocation will be an offset of this pointer from 0 to MAX_ALLOC. */ //void* base_ptr; /* * This is the maximum address that has ever been used by the allocator. It's * used to know when to call "brk" to request more memory from the kernel. */ //void* max_ptr; /* * Make sure all addresses before "new_value" are valid and can be used. Memory * is allocated in a 2gb address range but that memory is not reserved up * front. It's only reserved when it's needed by calling this function. This * will return false if the memory could not be reserved. */ //bool update_max_ptr(void* new_value) //{ // if (new_value > max_ptr) // { // if (brk(new_value)) // { // return true; // } // max_ptr = new_value; // } // return false; //} /* * Initialize a list to empty. Because these are circular lists, an "empty" * list is an entry where both links point to itself. This makes insertion * and removal simpler because they don't need any branches. */ private static void List_init(List_t *list) { list->prev = list; list->next = list; }