aboutsummaryrefslogtreecommitdiffstats
path: root/old-extension/spec_array.c
diff options
context:
space:
mode:
Diffstat (limited to 'old-extension/spec_array.c')
-rw-r--r--old-extension/spec_array.c416
1 files changed, 416 insertions, 0 deletions
diff --git a/old-extension/spec_array.c b/old-extension/spec_array.c
new file mode 100644
index 00000000..78b24018
--- /dev/null
+++ b/old-extension/spec_array.c
@@ -0,0 +1,416 @@
+/*
+ * spec_array.c - Support for specialized associative arrays.
+ */
+
+/*
+ * Copyright (C) 2012 the Free Software Foundation, Inc.
+ *
+ * This file is part of GAWK, the GNU implementation of the
+ * AWK Programming Language.
+ *
+ * GAWK is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GAWK is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "awk.h"
+#include "spec_array.h"
+
+typedef struct spec_array {
+ Fetch_func_t fetch_func;
+ Store_func_t store_func;
+ Load_func_t load_func;
+ void *data;
+} array_t;
+
+/*
+ * The array_t structure is attached to the array itself without
+ * the necessity to maintain a list of symbols; this works only
+ * because there is just enough free space in the NODE strcture when
+ * the base array is str_array.
+ */
+
+#define SUPER(F) (*str_array_func[AFUNC(F)])
+
+
+/*
+ * deferred_array --- Deferred loading of array at run-time.
+ *
+ * The load routine takes two arguments, the array and
+ * a void * data:
+ *
+ * void load_func(NODE *array, void *data)
+ *
+ * Use register_deferred_array(array, load_func, void *data) to
+ * bind an array to the load routine.
+ */
+
+static NODE **deferred_array_init(NODE *, NODE *);
+static NODE **deferred_array_lookup(NODE *, NODE *);
+static NODE **deferred_array_exists(NODE *, NODE *);
+static NODE **deferred_array_remove(NODE *, NODE *);
+static NODE **deferred_array_clear(NODE *, NODE *);
+static NODE **deferred_array_list(NODE *, NODE *);
+static NODE **deferred_array_copy(NODE *, NODE *);
+static NODE **deferred_array_length(NODE *, NODE *);
+
+static afunc_t deferred_array_func[] = {
+ deferred_array_init,
+ (afunc_t) 0, /* typeof */
+ deferred_array_length,
+ deferred_array_lookup,
+ deferred_array_exists,
+ deferred_array_clear,
+ deferred_array_remove,
+ deferred_array_list,
+ deferred_array_copy,
+ null_afunc, /* dump */
+ (afunc_t) 0, /* store */
+};
+
+
+/* deferred_array_init --- called when array becomes empty, e.g: delete BOUND_ARRAY */
+
+static NODE **
+deferred_array_init(NODE *symbol, NODE *subs)
+{
+ if (symbol != NULL) {
+ array_t *av = (array_t *) symbol->xarray;
+ symbol->xarray = NULL; /* this is to avoid an assertion failure in null_array */
+ null_array(symbol); /* typeless empty array */
+ if (symbol->parent_array == NULL) {
+ /* main array */
+ symbol->array_funcs = deferred_array_func; /* restore type */
+ symbol->xarray = (NODE *) av;
+ } else if (av) /* sub-array */
+ efree(av);
+ }
+ return NULL;
+}
+
+/* deferred_array_length --- get the length of the array */
+
+static NODE **
+deferred_array_length(NODE *symbol, NODE *subs)
+{
+ static NODE *length_node;
+ array_t *av = (array_t *) symbol->xarray;
+ if (av) {
+ symbol->xarray = NULL;
+ (*av->load_func)(symbol, av->data);
+ symbol->xarray = (NODE *) av;
+ }
+ length_node = symbol;
+ return & length_node;
+}
+
+#define DEF_ARR(F) static NODE ** \
+deferred_array_##F(NODE *symbol, NODE *subs) \
+{ \
+ array_t *av = (array_t *) symbol->xarray; \
+ if (av) { \
+ symbol->xarray = NULL; \
+ (*av->load_func)(symbol, av->data); \
+ symbol->xarray = (NODE *) av; \
+ } \
+ return SUPER(a##F)(symbol, subs); \
+}
+
+/* the rest of the routines */
+
+DEF_ARR(exists)
+DEF_ARR(lookup)
+DEF_ARR(list)
+DEF_ARR(copy)
+
+#undef DEF_ARR
+
+/* deferred_array_remove --- remove the index from the array */
+
+static NODE **
+deferred_array_remove(NODE *symbol, NODE *subs)
+{
+ array_t *av = (array_t *) symbol->xarray;
+
+ (void) SUPER(aremove)(symbol, subs);
+ if (av) {
+ symbol->xarray = NULL;
+ (*av->load_func)(symbol, av->data);
+ symbol->xarray = (NODE *) av;
+ }
+ return NULL;
+}
+
+/* deferred_array_clear --- flush all the values in symbol[] */
+
+static NODE **
+deferred_array_clear(NODE *symbol, NODE *subs)
+{
+ array_t *av = (array_t *) symbol->xarray;
+
+ (void) SUPER(aclear)(symbol, subs);
+ if (av) {
+ symbol->xarray = NULL;
+ (*av->load_func)(symbol, av->data);
+ symbol->xarray = (NODE *) av;
+ }
+ return NULL;
+}
+
+
+/*
+ * dyn_array --- array with triggers for reading and writing
+ * an element.
+ *
+ * The fetch routine should expect three arguments, the array,
+ * the subscript and optional void * data. It should return the value
+ * if it exists or NULL otherwise.
+ *
+ * NODE *fetch_func(NODE *array, NODE *subs, void *data)
+ *
+ * The store routine must take an additional argument for the
+ * value. The value can be NULL if the specific element is
+ * removed from the array. The subscript (and the value) is NULL
+ * when the entire array is deleted.
+ *
+ * void store_func(NODE *array, NODE *subs, NODE *value, void *data)
+ *
+ * Use register_dyn_array(array, fetch_func, store_func, void *data) to
+ * bind an array to the fetch/store routine.
+ */
+
+
+static NODE **dyn_array_init(NODE *, NODE *);
+static NODE **dyn_array_lookup(NODE *, NODE *);
+static NODE **dyn_array_exists(NODE *, NODE *);
+static NODE **dyn_array_remove(NODE *, NODE *);
+static NODE **dyn_array_clear(NODE *, NODE *);
+static NODE **dyn_array_list(NODE *, NODE *);
+static NODE **dyn_array_copy(NODE *, NODE *);
+static NODE **dyn_array_store(NODE *, NODE *);
+
+static afunc_t dyn_array_func[] = {
+ dyn_array_init,
+ (afunc_t) 0, /* typeof */
+ null_length, /* length */
+ dyn_array_lookup,
+ dyn_array_exists,
+ dyn_array_clear,
+ dyn_array_remove,
+ dyn_array_list,
+ dyn_array_copy,
+ null_afunc, /* dump */
+ dyn_array_store,
+};
+
+/* dyn_array_init --- called when array becomes empty */
+
+static NODE **
+dyn_array_init(NODE *symbol, NODE *subs)
+{
+ if (symbol != NULL) {
+ array_t *av = (array_t *) symbol->xarray;
+ symbol->xarray = NULL;
+ null_array(symbol); /* typeless empty array */
+ if (symbol->parent_array == NULL) {
+ /* main array */
+ symbol->array_funcs = dyn_array_func; /* restore type */
+ symbol->xarray = (NODE *) av;
+ } else if (av) /* sub-array */
+ efree(av);
+ }
+ return NULL;
+}
+
+/* dyn_array_exists --- check if the SUBS exists */
+
+static NODE **
+dyn_array_exists(NODE *symbol, NODE *subs)
+{
+ NODE *r;
+ array_t *av = (array_t *) symbol->xarray;
+
+ if (av && av->fetch_func) {
+ symbol->xarray = NULL;
+ r = (*av->fetch_func)(symbol, subs, av->data);
+ symbol->xarray = (NODE *) av;
+ if (r != NULL) {
+ NODE **lhs;
+ lhs = SUPER(alookup)(symbol, subs);
+ unref(*lhs);
+ *lhs = r;
+ return lhs;
+ }
+ }
+
+ return SUPER(aexists)(symbol, subs);
+}
+
+/* dyn_array_lookup --- lookup SUBS and return a pointer to store its value */
+
+static NODE **
+dyn_array_lookup(NODE *symbol, NODE *subs)
+{
+ NODE **lhs;
+ NODE *r;
+ array_t *av = (array_t *) symbol->xarray;
+
+ lhs = SUPER(alookup)(symbol, subs);
+ if (av && av->fetch_func) {
+ symbol->xarray = NULL;
+ r = (*av->fetch_func)(symbol, subs, av->data);
+ symbol->xarray = (NODE *) av;
+ if (r != NULL) {
+ unref(*lhs);
+ *lhs = r;
+ }
+ }
+ return lhs;
+}
+
+/* dyn_array_store --- call the store routine after an assignment */
+
+static NODE **
+dyn_array_store(NODE *symbol, NODE *subs)
+{
+ array_t *av = (array_t *) symbol->xarray;
+
+ if (av && av->store_func) {
+ NODE **lhs;
+ lhs = SUPER(aexists)(symbol, subs);
+ symbol->xarray = NULL;
+ (*av->store_func)(symbol, subs, *lhs, av->data);
+ symbol->xarray = (NODE *) av;
+ }
+ return NULL;
+}
+
+/* dyn_array_remove --- remove the index from the array */
+
+static NODE **
+dyn_array_remove(NODE *symbol, NODE *subs)
+{
+ array_t *av = (array_t *) symbol->xarray;
+
+ (void) SUPER(aremove)(symbol, subs);
+ if (av && av->store_func) {
+ symbol->xarray = NULL;
+ (*av->store_func)(symbol, subs, NULL, av->data);
+ symbol->xarray = (NODE *) av;
+ }
+ return NULL;
+}
+
+/* dyn_array_clear --- flush all the values in symbol[] */
+
+static NODE **
+dyn_array_clear(NODE *symbol, NODE *subs)
+{
+ array_t *av = (array_t *) symbol->xarray;
+
+ (void) SUPER(aclear)(symbol, subs);
+ if (av && av->store_func) {
+ symbol->xarray = NULL;
+ (*av->store_func)(symbol, NULL, NULL, av->data);
+ symbol->xarray = (NODE *) av;
+ }
+ return NULL;
+}
+
+/* dyn_array_list --- return a list of items in symbol[] */
+
+static NODE **
+dyn_array_list(NODE *symbol, NODE *subs)
+{
+ return SUPER(alist)(symbol, subs);
+}
+
+/* dyn_array_copy --- duplicate the array */
+
+static NODE **
+dyn_array_copy(NODE *symbol, NODE *subs)
+{
+ return SUPER(acopy)(symbol, subs);
+}
+
+/* register_array_s --- attach the specified routine(s) to an array */
+
+static void
+register_array_s(NODE *symbol, Fetch_func_t fetch_func,
+ Store_func_t store_func, Load_func_t load_func, void *data)
+{
+ array_t *av;
+
+ if (symbol->type != Node_var_array)
+ fatal(_("register_array_s: argument is not an array"));
+
+ if (symbol->array_funcs == deferred_array_func
+ || symbol->array_funcs == dyn_array_func)
+ fatal(_("register_array_s: `%s' already is a deferred/dyn array"),
+ array_vname(symbol));
+
+ assoc_clear(symbol);
+ assert(symbol->xarray == NULL);
+ emalloc(av, array_t *, sizeof (array_t), "register_spec_array");
+ av->fetch_func = fetch_func;
+ av->store_func = store_func;
+ av->load_func = load_func;
+ av->data = data;
+ symbol->xarray = (NODE *) av;
+}
+
+/* register_deferred_array --- make the array to be loaded at run-time */
+
+void
+register_deferred_array(NODE *symbol, Load_func_t load_func, void *dq)
+{
+ if (! load_func)
+ fatal(_("register_deferred_array: null load function"));
+ register_array_s(symbol, 0, 0, load_func, dq);
+ symbol->array_funcs = deferred_array_func;
+}
+
+/* register_dyn_array --- attach read and write triggers to an array */
+
+void
+register_dyn_array(NODE *symbol, Fetch_func_t fetch_func,
+ Store_func_t store_func, void *dq)
+{
+ register_array_s(symbol, fetch_func, store_func, 0, dq);
+ symbol->array_funcs = dyn_array_func;
+}
+
+/* unregister_array_s --- un-special the array */
+
+void *
+unregister_array_s(NODE *symbol)
+{
+ void *data = NULL;
+ if (symbol->type != Node_var_array)
+ fatal(_("unregister_array_s: argument is not an array"));
+
+ if (symbol->array_funcs == dyn_array_func
+ || symbol->array_funcs == deferred_array_func
+ ) {
+ array_t *av;
+
+ av = (array_t *) symbol->xarray;
+ assert(av != NULL);
+ data = av->data;
+ efree(av);
+ symbol->array_funcs = str_array_func;
+ symbol->xarray = NULL;
+ /* FIXME: do we assoc_clear the array ? */
+ }
+ return data;
+}