aboutsummaryrefslogtreecommitdiff
path: root/files.c
diff options
context:
space:
mode:
Diffstat (limited to 'files.c')
-rw-r--r--files.c125
1 files changed, 125 insertions, 0 deletions
diff --git a/files.c b/files.c
index b78ddb9..1f9166a 100644
--- a/files.c
+++ b/files.c
@@ -14,6 +14,7 @@
#include <unistd.h>
#include "files.h"
+#include "match.h"
#include "pattern.h"
#include "utils.h"
@@ -183,6 +184,8 @@ void destroy_file(file_t **f)
}
}
+ cache_destroy(*f);
+
for (pat_t *next; (*f)->pats; (*f)->pats = next) {
next = (*f)->pats->next;
delete(&(*f)->pats);
@@ -270,4 +273,126 @@ void fprint_line(FILE *dest, file_t *f, const char *start, const char *end, cons
fprintf(dest, "\033[0m\n");
}
+//
+// Hash a string position/pattern.
+//
+static inline size_t hash(const char *str, pat_t *pat)
+{
+ return (size_t)str + 2*pat->id;
+}
+
+//
+// Check if we have memoized a pattern match at the given position for the
+// given definitions. If a result has been memoized, set *result to the
+// memoized value and return true, otherwise return false.
+//
+bool cache_get(file_t *f, def_t *defs, const char *str, pat_t *pat, match_t **result)
+{
+ if (!f->cache.matches) return NULL;
+ size_t h = hash(str, pat) & (f->cache.size-1);
+ for (match_t *c = f->cache.matches[h]; c; c = c->cache.next) {
+ if (c->pat == pat && c->defs_id == (defs?defs->id:0) && c->start == str) {
+ // If c->end == NULL, that means no match occurs here
+ *result = c->end == NULL ? NULL : c;
+ return true;
+ }
+ }
+ return false;
+}
+
+//
+// Remove an item from the cache.
+//
+static void cache_remove(file_t *f, 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->refcount == 0) recycle_if_unused(&m);
+ --f->cache.occupancy;
+}
+
+//
+// Save a match in the cache.
+//
+void cache_save(file_t *f, def_t *defs, const char *str, pat_t *pat, match_t *m)
+{
+ // As a convention, a match with {.pat=pat, .start=str, .end==NULL} is used
+ // to memoize the fact that `pat` will *not* match at `str`.
+ if (m == NULL) m = new_match(defs, pat, str, NULL, NULL);
+
+ if (f->cache.occupancy+1 > 3*f->cache.size) {
+ if (f->cache.size == MAX_CACHE_SIZE) {
+ size_t h = hash(m->start, m->pat) & (f->cache.size-1);
+ for (int quota = 2; f->cache.matches[h] && quota > 0; quota--) {
+ match_t *last = f->cache.matches[h];
+ while (last->cache.next) last = last->cache.next;
+ cache_remove(f, last);
+ }
+ } else {
+ match_t **old_matches = f->cache.matches;
+ size_t old_size = f->cache.size;
+ f->cache.size = old_size == 0 ? 16 : 2*old_size;
+ f->cache.matches = new(match_t*[f->cache.size]);
+
+ // Rehash:
+ if (old_matches) {
+ for (size_t i = 0; i < old_size; i++) {
+ for (match_t *o; (o = old_matches[i]); ) {
+ *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) & (f->cache.size-1);
+ o->cache.home = &(f->cache.matches[h]);
+ o->cache.next = f->cache.matches[h];
+ if (f->cache.matches[h]) f->cache.matches[h]->cache.home = &o->cache.next;
+ f->cache.matches[h] = o;
+ }
+ }
+ free(old_matches);
+ }
+ }
+ }
+
+ size_t h = hash(m->start, m->pat) & (f->cache.size-1);
+ m->cache.home = &(f->cache.matches[h]);
+ m->cache.next = f->cache.matches[h];
+ if (f->cache.matches[h]) f->cache.matches[h]->cache.home = &m->cache.next;
+ f->cache.matches[h] = m;
+ ++m->refcount;
+ ++f->cache.occupancy;
+}
+
+//
+// Remove all items from the cache that do not overlap `start` and `end`.
+// (This is used to remove useless items from the cache)
+//
+void cache_prune(file_t *f, const char *start, const char *end)
+{
+ if (!f->cache.matches) return;
+ for (size_t i = 0; i < f->cache.size; i++) {
+ for (match_t *m = f->cache.matches[i], *next = NULL; m; m = next) {
+ next = m->cache.next;
+ if (m->start < start || (m->end ? m->end : m->start) > end)
+ cache_remove(f, m);
+ }
+ }
+}
+
+//
+// Clear and deallocate the cache.
+//
+void cache_destroy(file_t *f)
+{
+ if (!f->cache.matches) return;
+ for (size_t i = 0; i < f->cache.size; i++) {
+ while (f->cache.matches[i])
+ cache_remove(f, f->cache.matches[i]);
+ }
+ f->cache.occupancy = 0;
+ delete(&f->cache.matches);
+ f->cache.size = 0;
+}
+
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1