From a3cf50388fde97242175c45b92c76dc6bc8ff620 Mon Sep 17 00:00:00 2001
From: Andrea Shepard <andrea@persephoneslair.org>
Date: Sun, 7 May 2017 22:47:05 +0000
Subject: [PATCH] Add copy/free params mechanism

---
 src/hammer.c   | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/hammer.h   | 29 +++++++++++++++++++++
 src/internal.h | 14 ++++++++++
 3 files changed, 112 insertions(+)

diff --git a/src/hammer.c b/src/hammer.c
index 44ff2e66..c72c59fb 100644
--- a/src/hammer.c
+++ b/src/hammer.c
@@ -55,6 +55,75 @@ HParserBackend h_get_default_backend(void) {
   return h_get_default_backend__int();
 }
 
+/*
+ * Copy an HParserBackendWithParams, using the backend-supplied copy
+ * method.
+ */
+
+HParserBackendWithParams * h_copy_backend_with_params(
+    HParserBackendWithParams *be_with_params) {
+  return h_copy_backend_with_params__m(&system_allocator, be_with_params);
+}
+
+HParserBackendWithParams * h_copy_backend_with_params__m(HAllocator *mm__,
+    HParserBackendWithParams *be_with_params) {
+  HParserBackendWithParams *r = NULL;
+  int s;
+
+  if (be_with_params && be_with_params->backend >= PB_MIN &&
+      be_with_params->backend <= PB_MAX) {
+    if (mm__ == NULL) {
+      /* use the allocator from the input */
+      mm__ = be_with_params->mm__;
+    }
+
+    /* got an allocator? */
+    if (mm__) {
+      r = h_new(HParserBackendWithParams, 1);
+      if (r) {
+        r->mm__ = mm__;
+        r->backend = be_with_params->backend;
+        if (backends[be_with_params->backend]->copy_params) {
+          s = backends[be_with_params->backend]->copy_params(mm__,
+              &(r->params), be_with_params->params);
+          if (s != 0) {
+            /* copy_params() failed */
+            h_free(r);
+            r = NULL;
+          }
+        } else {
+          /* else just ignore it and set it to NULL */
+          r->params = NULL;
+        }
+      }
+      /* else fail out */
+    }
+    /* else give up and return NULL */
+  }
+  /* else return NULL, nothing to copy */
+
+  return r;
+}
+
+/* Free this HParserBackendWithParams, and free the params too */
+
+void h_free_backend_with_params(HParserBackendWithParams *be_with_params) {
+  HAllocator *mm__ = &system_allocator;
+
+  if (be_with_params) {
+    if (be_with_params->mm__) mm__ = be_with_params->mm__;
+
+    if (h_is_backend_available(be_with_params->backend)) {
+      if (backends[be_with_params->backend]->free_params) {
+        backends[be_with_params->backend]->
+          free_params(be_with_params->mm__, be_with_params->params);
+      }
+    }
+
+    h_free(be_with_params);
+  }
+}
+
 #define DEFAULT_ENDIANNESS (BIT_BIG_ENDIAN | BYTE_BIG_ENDIAN)
 
 HParseResult* h_parse(const HParser* parser, const uint8_t* input, size_t length) {
diff --git a/src/hammer.h b/src/hammer.h
index a9ace572..e838720a 100644
--- a/src/hammer.h
+++ b/src/hammer.h
@@ -54,6 +54,20 @@ typedef enum HParserBackend_ {
   PB_MAX = PB_GLR
 } HParserBackend;
 
+typedef struct HParserBackendWithParams_ {
+  /* The backend */
+  HParserBackend backend;
+  /*
+   * Backend-specific parameters - if this needs to be freed, the backend
+   * should provide a free_params method in its vtable; currently no backends
+   * do this - PB_PACKRAT and PB_REGULAR take no params, and PB_LLk, PB_LALR
+   * and PB_GLR take an integer cast to void *
+   */
+  void *params;
+  /* Allocator to use to free this (and the params if necessary) */
+  HAllocator *mm__;
+} HParserBackendWithParams;
+
 typedef enum HTokenType_ {
   // Before you change the explicit values of these, think of the poor bindings ;_;
   TT_INVALID = 0,
@@ -277,6 +291,21 @@ int h_is_backend_available(HParserBackend backend);
 
 HParserBackend h_get_default_backend(void);
 
+/**
+ * Copy a backend+params, using the backend-supplied copy method; the
+ * allocator used is the one passed in, or call the __m version with
+ * a NULL allocator to use the one from the source HParserBackendWithParams
+ */
+
+HAMMER_FN_DECL(HParserBackendWithParams *, h_copy_backend_with_params,
+               HParserBackendWithParams *be_with_params);
+
+/**
+ * Free a backend+params
+ */
+
+void h_free_backend_with_params(HParserBackendWithParams *be_with_params);
+
 /**
  * Top-level function to call a parser that has been built over some
  * piece of input (of known size).
diff --git a/src/internal.h b/src/internal.h
index 51898bf0..22407cca 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -238,6 +238,20 @@ typedef struct HParserBackendVTable_ {
   HParseResult *(*parse_finish)(HSuspendedParser *s);
     // parse_finish must free s->backend_state.
     // parse_finish will not be called before parse_chunk reports done.
+
+  /* The backend knows how to free its params */
+  void (*free_params)(HAllocator *mm__, void *p);
+  /*
+   * ..and how to copy them
+   *
+   * Since the backend params need not actually be an allocated object,
+   * (and in fact no current backends use this, although it is permissible),
+   * but might (as in PB_GLR) be some numeric constant cast to void * which
+   * copy_params() should just pass through, we can't use returning NULL
+   * to signal allocation failure.  Hence, passing the result out in a
+   * void ** and returning a status code (0 indicates success).
+   */
+  int (*copy_params)(HAllocator *mm__, void **out, void *in);
 } HParserBackendVTable;
 
 
-- 
GitLab