text: // 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.
  };
  
  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. */
  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;
  }
  
  // End of free_list.h.
  