Linked list management cleanup

This commit is contained in:
Bruce Hill 2021-07-30 13:36:29 -07:00
parent 253bb1dbe1
commit f3954ac151
2 changed files with 59 additions and 67 deletions

112
match.c
View File

@ -17,12 +17,6 @@
#include "utils.h"
#include "utf8.h"
#ifdef DEBUG_HEAP
// Doubly-linked list operations:
#define DLL_PREPEND(head, node) do { (node)->home = &(head); (node)->next = head; if (head) (head)->home = &(node)->next; head = node; } while(false)
#define DLL_REMOVE(node) do { *(node)->home = (node)->next; if ((node)->next) (node)->next->home = (node)->home; } while(false)
#endif
// New match objects are either recycled from unused match objects or allocated
// from the heap. While it is in use, the match object is stored in the
// `in_use_matches` linked list. Once it is no longer needed, it is moved to
@ -30,9 +24,7 @@
// additional calls to malloc/free. Thus, it is an invariant that every match
// object is in one of these two lists:
static match_t *unused_matches = NULL;
#ifdef DEBUG_HEAP
static match_t *in_use_matches = NULL;
#endif
typedef struct {
size_t size, occupancy;
@ -41,6 +33,8 @@ typedef struct {
#define MAX_CACHE_SIZE (1<<14)
#define MATCHES(...) (match_t*[]){__VA_ARGS__, NULL}
cache_t cache = {0, 0, NULL};
__attribute__((nonnull(1)))
@ -74,19 +68,33 @@ static inline void remove_ownership(match_t** owner)
}
}
// Helper method for concisely allocating children matches
// static match_t** _alloc_children(size_t n, match_t* matches[])
// {
// if (n == 0) return NULL;
// match_t **ret = xcalloc(n+1, sizeof(match_t*));
// for (size_t i = 0; i < n; i++)
// add_owner(&ret[i], matches[i]);
// return ret;
// }
// Prepend to a doubly linked list
static inline void list_prepend(match_t **head, match_t *m, match_dll_t *node)
{
if (node->home)
errx(1, "Node already has a home");
node->home = head;
node->next = *head;
if (*head) {
match_dll_t *head_node = (match_dll_t*)((char*)(*head) + ((char*)node - (char*)m));
head_node->home = &node->next;
}
*head = m;
}
// #define MATCHES(...) _alloc_children((sizeof((match_t*[]){__VA_ARGS__}))/sizeof(match_t*), (match_t*[]){__VA_ARGS__})
#define MATCHES(...) (match_t*[]){__VA_ARGS__, NULL}
// Remove from a doubly linked list
static inline void list_remove(match_t *m, match_dll_t *node)
{
if (!node->home)
errx(1, "Attempt to remove something that isn't in a list");
*node->home = node->next;
if (node->next) {
match_dll_t *next_node = (match_dll_t*)((char*)(node->next) + ((char*)node - (char*)m));
next_node->home = node->home;
}
node->home = NULL;
node->next = NULL;
}
static inline size_t hash(const char *str, pat_t *pat)
{
@ -97,7 +105,7 @@ static match_t *cache_lookup(def_t *defs, const char *str, pat_t *pat)
{
if (!cache.matches) return NULL;
size_t h = hash(str, pat) & (cache.size-1);
for (match_t *c = cache.matches[h]; c; c = c->cache_next) {
for (match_t *c = cache.matches[h]; c; c = c->cache.next) {
if (c->pat == pat && c->defs_id == defs->id && c->start == str)
return c;
}
@ -106,11 +114,11 @@ static match_t *cache_lookup(def_t *defs, const char *str, pat_t *pat)
static void cache_remove(match_t *m)
{
if (!m->cache_home) return;
*m->cache_home = m->cache_next;
if (m->cache_next) m->cache_next->cache_home = m->cache_home;
m->cache_next = NULL;
m->cache_home = NULL;
if (!m->cache.home) return;
*m->cache.home = m->cache.next;
if (m->cache.next) m->cache.next->cache.home = m->cache.home;
m->cache.next = NULL;
m->cache.home = NULL;
remove_ownership(&m);
--cache.occupancy;
}
@ -122,7 +130,7 @@ static void cache_save(match_t *m)
size_t h = hash(m->start, m->pat) & (cache.size-1);
for (int quota = 2; cache.matches[h] && quota > 0; quota--) {
match_t *last = cache.matches[h];
while (last->cache_next) last = last->cache_next;
while (last->cache.next) last = last->cache.next;
cache_remove(last);
}
} else {
@ -134,12 +142,12 @@ static void cache_save(match_t *m)
if (old_cache.matches) {
for (size_t i = 0; i < old_cache.size; i++) {
for (match_t *o; (o = old_cache.matches[i]); ) {
*o->cache_home = o->cache_next;
if (o->cache_next) o->cache_next->cache_home = o->cache_home;
*o->cache.home = o->cache.next;
if (o->cache.next) o->cache.next->cache.home = o->cache.home;
size_t h = hash(o->start, o->pat) & (cache.size-1);
o->cache_home = &(cache.matches[h]);
o->cache_next = cache.matches[h];
if (cache.matches[h]) cache.matches[h]->cache_home = &o->cache_next;
o->cache.home = &(cache.matches[h]);
o->cache.next = cache.matches[h];
if (cache.matches[h]) cache.matches[h]->cache.home = &o->cache.next;
cache.matches[h] = o;
}
}
@ -149,9 +157,9 @@ static void cache_save(match_t *m)
}
size_t h = hash(m->start, m->pat) & (cache.size-1);
m->cache_home = &(cache.matches[h]);
m->cache_next = cache.matches[h];
if (cache.matches[h]) cache.matches[h]->cache_home = &m->cache_next;
m->cache.home = &(cache.matches[h]);
m->cache.next = cache.matches[h];
if (cache.matches[h]) cache.matches[h]->cache.home = &m->cache.next;
cache.matches[h] = NULL;
add_owner(&cache.matches[h], m);
++cache.occupancy;
@ -162,7 +170,7 @@ static void cache_prune(const char *start, const char *end)
if (!cache.matches) return;
for (size_t i = 0; i < cache.size; i++) {
for (match_t *m = cache.matches[i], *next = NULL; m; m = next) {
next = m->cache_next;
next = m->cache.next;
if (m->start < start || (m->end ? m->end : m->start) > end)
cache_remove(m);
}
@ -706,26 +714,15 @@ match_t *get_capture(match_t *m, const char **id)
static match_t *new_match(def_t *defs, pat_t *pat, const char *start, const char *end, match_t *children[])
{
match_t *m;
#ifdef DEBUG_HEAP
if (unused_matches) {
m = unused_matches;
DLL_REMOVE(m);
list_remove(m, &m->gc);
memset(m, 0, sizeof(match_t));
} else {
m = new(match_t);
}
// Keep track of the object:
DLL_PREPEND(in_use_matches, m);
#else
if (unused_matches) {
m = unused_matches;
unused_matches = unused_matches->next;
(void)memset(m, 0, sizeof(match_t));
} else {
m = new(match_t);
}
#endif
list_prepend(&in_use_matches, m, &m->gc);
m->pat = pat;
m->start = start;
@ -760,16 +757,9 @@ void recycle_if_unused(match_t **at_m)
xfree(&m->children);
}
#ifdef DEBUG_HEAP
DLL_REMOVE(m); // Remove from in_use_matches
list_remove(m, &m->gc);
(void)memset(m, 0, sizeof(match_t));
DLL_PREPEND(unused_matches, m);
#else
(void)memset(m, 0, sizeof(match_t));
m->next = unused_matches;
unused_matches = m;
#endif
list_prepend(&unused_matches, m, &m->gc);
*at_m = NULL;
}
@ -782,10 +772,10 @@ size_t recycle_all_matches(void)
size_t count = 0;
while (in_use_matches) {
match_t *m = in_use_matches;
DLL_REMOVE(m);
list_remove(m, &m->gc);
if (m->children && m->children != m->_children)
xfree(&m->children);
DLL_PREPEND(unused_matches, m);
list_prepend(&unused_matches, m, &m->gc);
++count;
}
return count;
@ -800,7 +790,7 @@ size_t free_all_matches(void)
recycle_all_matches();
while (unused_matches) {
match_t *m = unused_matches;
DLL_REMOVE(m);
list_remove(m, &m->gc);
free(m);
++count;
}

14
types.h
View File

@ -92,6 +92,12 @@ typedef struct pat_s {
size_t id;
} pat_t;
typedef struct {
struct match_s **home, *next;
} match_dll_t;
// #define MATCH_FROM(node, name) ((match_t*)((char*)node + (size_t)(&((match_t*)0)->name)))
//
// Pattern matching result object
//
@ -99,12 +105,8 @@ typedef struct match_s {
// Where the match starts and ends (end is after the last character)
const char *start, *end;
pat_t *pat;
// Intrusive linked list nodes for garbage collection:
struct match_s *next;
#ifdef DEBUG_HEAP
struct match_s **home;
#endif
struct match_s *cache_next, **cache_home;
// Intrusive linked list nodes for garbage collection and cache buckets:
match_dll_t gc, cache;
size_t defs_id;
int refcount;
// If skip_replacement is set to 1, that means the user wants to not print