aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--match.c112
-rw-r--r--types.h14
2 files changed, 59 insertions, 67 deletions
diff --git a/match.c b/match.c
index 5b99c64..62ad3fd 100644
--- a/match.c
+++ b/match.c
@@ -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;
-// }
-
-// #define MATCHES(...) _alloc_children((sizeof((match_t*[]){__VA_ARGS__}))/sizeof(match_t*), (match_t*[]){__VA_ARGS__})
+// 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(...) (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;
}
diff --git a/types.h b/types.h
index 4085218..158178d 100644
--- a/types.h
+++ b/types.h
@@ -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