// Start of free_list.h. // An entry in the free list. May be invalid, to avoid having to // deallocate entries as soon as they are removed. There is also a // tag, to help with memory reuse. struct free_list_entry { size_t size; fl_mem_t mem; const char *tag; unsigned char valid; }; struct free_list { struct free_list_entry *entries; // Pointer to entries. int capacity; // Number of entries. int used; // Number of valid entries. }; static void free_list_init(struct free_list *l) { l->capacity = 30; // Picked arbitrarily. l->used = 0; l->entries = (struct free_list_entry*) malloc(sizeof(struct free_list_entry) * l->capacity); for (int i = 0; i < l->capacity; i++) { l->entries[i].valid = 0; } } // Remove invalid entries from the free list. static void free_list_pack(struct free_list *l) { int p = 0; for (int i = 0; i < l->capacity; i++) { if (l->entries[i].valid) { l->entries[p] = l->entries[i]; if (i > p) { l->entries[i].valid = 0; } p++; } } // Now p is the number of used elements. We don't want it to go // less than the default capacity (although in practice it's OK as // long as it doesn't become 1). if (p < 30) { p = 30; } l->entries = realloc(l->entries, p * sizeof(struct free_list_entry)); l->capacity = p; } static void free_list_destroy(struct free_list *l) { assert(l->used == 0); free(l->entries); } static int free_list_find_invalid(struct free_list *l) { int i; for (i = 0; i < l->capacity; i++) { if (!l->entries[i].valid) { break; } } return i; } static void free_list_insert(struct free_list *l, size_t size, fl_mem_t mem, const char *tag) { int i = free_list_find_invalid(l); if (i == l->capacity) { // List is full; so we have to grow it. int new_capacity = l->capacity * 2 * sizeof(struct free_list_entry); l->entries = realloc(l->entries, new_capacity); for (int j = 0; j < l->capacity; j++) { l->entries[j+l->capacity].valid = 0; } l->capacity *= 2; } // Now 'i' points to the first invalid entry. l->entries[i].valid = 1; l->entries[i].size = size; l->entries[i].mem = mem; l->entries[i].tag = tag; l->used++; } // Find and remove a memory block of the indicated tag, or if that // does not exist, another memory block with exactly the desired size. // Returns 0 on success. static int free_list_find(struct free_list *l, size_t size, size_t *size_out, fl_mem_t *mem_out) { int size_match = -1; int i; for (i = 0; i < l->capacity; i++) { if (l->entries[i].valid && size <= l->entries[i].size && (size_match < 0 || l->entries[i].size < l->entries[size_match].size)) { // If this entry is valid, has sufficient size, and is smaller than the // best entry found so far, use this entry. size_match = i; } } if (size_match >= 0) { l->entries[size_match].valid = 0; *size_out = l->entries[size_match].size; *mem_out = l->entries[size_match].mem; l->used--; return 0; } else { return 1; } } // Remove the first block in the free list. Returns 0 if a block was // removed, and nonzero if the free list was already empty. static int free_list_first(struct free_list *l, fl_mem_t *mem_out) { for (int i = 0; i < l->capacity; i++) { if (l->entries[i].valid) { l->entries[i].valid = 0; *mem_out = l->entries[i].mem; l->used--; return 0; } } return 1; } // End of free_list.h.