/* Free list management */ /* 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. }; void free_list_init(struct free_list *l) { l->capacity = 30; // Picked arbitrarily. l->used = 0; l->entries = 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. */ 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]; p++; } } // Now p == l->used. l->entries = realloc(l->entries, l->used * sizeof(struct free_list_entry)); l->capacity = l->used; } void free_list_destroy(struct free_list *l) { assert(l->used == 0); free(l->entries); } 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; } 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 at least the desired size and tag. Returns 0 on success. */ int free_list_find(struct free_list *l, const char *tag, size_t *size_out, fl_mem_t *mem_out) { int i; for (i = 0; i < l->capacity; i++) { if (l->entries[i].valid && l->entries[i].tag == tag) { l->entries[i].valid = 0; *size_out = l->entries[i].size; *mem_out = l->entries[i].mem; l->used--; return 0; } } 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. */ 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; }