#include "debug-print.h"
+/* Procedure execution data. */
+struct write_case_data
+ {
+ void (*beginfunc) (void *);
+ int (*procfunc) (struct ccase *, void *);
+ void (*endfunc) (void *);
+ void *aux;
+ };
+
/* This is used to read from the active file. */
struct case_stream *vfm_source;
-/* `value' indexes to initialize to particular values for certain cases. */
-struct long_vec reinit_sysmis; /* SYSMIS for every case. */
-struct long_vec reinit_blanks; /* Blanks for every case. */
-struct long_vec init_zero; /* Zero for first case only. */
-struct long_vec init_blanks; /* Blanks for first case only. */
-
/* This is used to write to the replacement active file. */
struct case_stream *vfm_sink;
/* Time at which vfm was last invoked. */
time_t last_vfm_invocation;
-/* Functions called during procedure processing. */
-static int (*proc_func) (struct ccase *); /* Called for each case. */
-static int (*virt_proc_func) (struct ccase *); /* From SPLIT_FILE_procfunc. */
-static void (*begin_func) (void); /* Called at beginning of a series. */
-static void (*virt_begin_func) (void); /* Called by SPLIT_FILE_procfunc. */
-static void (*end_func) (void); /* Called after end of a series. */
-int (*write_case) (void);
-
/* Number of cases passed to proc_func(). */
static int case_count;
static struct ccase **lag_queue; /* Array of n_lag ccase * elements. */
static void open_active_file (void);
-static void close_active_file (void);
-static int SPLIT_FILE_procfunc (struct ccase *);
+static void close_active_file (struct write_case_data *);
+static int SPLIT_FILE_procfunc (struct ccase *, void *);
static void finish_compaction (void);
static void lag_case (void);
-static int procedure_write_case (void);
+static int procedure_write_case (struct write_case_data *);
+static void clear_temp_case (void);
\f
/* Public functions. */
-/* Reads all the cases from the active file, transforms them by the
- active set of transformations, calls PROCFUNC with CURCASE set to
- the case and CASENUM set to the case number, and writes them to a
- new active file.
+/* Reads all the cases from the active file, transforms them by
+ the active set of transformations, calls PROCFUNC with CURCASE
+ set to the case , and writes them to a new active file.
Divides the active file into zero or more series of one or more
cases each. BEGINFUNC is called before each series. ENDFUNC is
- called after each series. */
+ called after each series.
+
+ Arbitrary user-specified data AUX is passed to BEGINFUNC,
+ PROCFUNC, and ENDFUNC as auxiliary data. */
void
-procedure (void (*beginfunc) (void),
- int (*procfunc) (struct ccase *curcase),
- void (*endfunc) (void))
+procedure (void (*beginfunc) (void *),
+ int (*procfunc) (struct ccase *curcase, void *),
+ void (*endfunc) (void *),
+ void *aux)
{
- end_func = endfunc;
- write_case = procedure_write_case;
+ struct write_case_data procedure_write_data;
+ struct write_case_data split_file_data;
- if (dict_get_split_cnt (default_dict) != 0 && procfunc != NULL)
+ if (dict_get_split_cnt (default_dict) == 0)
{
- virt_proc_func = procfunc;
- proc_func = SPLIT_FILE_procfunc;
-
- virt_begin_func = beginfunc;
- begin_func = NULL;
- } else {
- begin_func = beginfunc;
- proc_func = procfunc;
+ /* Normally we just use the data passed by the user. */
+ procedure_write_data.beginfunc = beginfunc;
+ procedure_write_data.procfunc = procfunc;
+ procedure_write_data.endfunc = endfunc;
+ procedure_write_data.aux = aux;
+ }
+ else
+ {
+ /* Under SPLIT FILE, we add a layer of indirection. */
+ procedure_write_data.beginfunc = NULL;
+ procedure_write_data.procfunc = SPLIT_FILE_procfunc;
+ procedure_write_data.endfunc = endfunc;
+ procedure_write_data.aux = &split_file_data;
+
+ split_file_data.beginfunc = beginfunc;
+ split_file_data.procfunc = procfunc;
+ split_file_data.endfunc = endfunc;
+ split_file_data.aux = aux;
}
last_vfm_invocation = time (NULL);
open_active_file ();
- vfm_source->read ();
- close_active_file ();
+ vfm_source->read (procedure_write_case, &procedure_write_data);
+ close_active_file (&procedure_write_data);
}
\f
/* Active file processing support. Subtly different semantics from
procedure(). */
-static int process_active_file_write_case (void);
+static int process_active_file_write_case (struct write_case_data *data);
/* The casefunc might want us to stop calling it. */
static int not_canceled;
process_active_file() ignores TEMPORARY, SPLIT FILE, and N. */
void
-process_active_file (void (*beginfunc) (void),
- int (*casefunc) (struct ccase *curcase),
- void (*endfunc) (void))
+process_active_file (void (*beginfunc) (void *),
+ int (*casefunc) (struct ccase *curcase, void *),
+ void (*endfunc) (void *),
+ void *aux)
{
- proc_func = casefunc;
- write_case = process_active_file_write_case;
+ struct write_case_data process_active_write_data;
+
+ process_active_write_data.beginfunc = beginfunc;
+ process_active_write_data.procfunc = casefunc;
+ process_active_write_data.endfunc = endfunc;
+ process_active_write_data.aux = aux;
+
not_canceled = 1;
open_active_file ();
- beginfunc ();
+ beginfunc (aux);
/* There doesn't necessarily need to be an active file. */
if (vfm_source)
- vfm_source->read ();
+ vfm_source->read (process_active_file_write_case,
+ &process_active_write_data);
- endfunc ();
- close_active_file ();
+ endfunc (aux);
+ close_active_file (&process_active_write_data);
}
/* Pass the current case to casefunc. */
static int
-process_active_file_write_case (void)
+process_active_file_write_case (struct write_case_data *data)
{
/* Index of current transformation. */
int cur_trns;
&& !FILTERED
&& (process_if_expr == NULL ||
expr_evaluate (process_if_expr, temp_case, NULL) == 1.0))
- not_canceled = proc_func (temp_case);
+ not_canceled = data->procfunc (temp_case, data->aux);
case_count++;
done:
- {
- long *lp;
+ clear_temp_case ();
- /* This case is finished. Initialize the variables for the next case. */
- for (lp = reinit_sysmis.vec; *lp != -1;)
- temp_case->data[*lp++].f = SYSMIS;
- for (lp = reinit_blanks.vec; *lp != -1;)
- memset (temp_case->data[*lp++].s, ' ', MAX_SHORT_STRING);
- }
-
return 1;
}
}
#endif
-/* Initializes temp_case from the vectors that say which `value's need
- to be initialized just once, and which ones need to be
+/* Initializes temp_case from the vectors that say which `value's
+ need to be initialized just once, and which ones need to be
re-initialized before every case. */
static void
vector_initialization (void)
{
- int i;
- long *lp;
-
- /* Just once. */
- for (i = 0; i < init_zero.n; i++)
- temp_case->data[init_zero.vec[i]].f = 0.0;
- for (i = 0; i < init_blanks.n; i++)
- memset (temp_case->data[init_blanks.vec[i]].s, ' ', MAX_SHORT_STRING);
-
- /* These vectors need to be repeatedly accessed, so we add a
- sentinel to (hopefully) improve speed. */
- vec_insert (&reinit_sysmis, -1);
- vec_insert (&reinit_blanks, -1);
-
- for (lp = reinit_sysmis.vec; *lp != -1;)
- temp_case->data[*lp++].f = SYSMIS;
- for (lp = reinit_blanks.vec; *lp != -1;)
- memset (temp_case->data[*lp++].s, ' ', MAX_SHORT_STRING);
+ size_t var_cnt = dict_get_var_cnt (default_dict);
+ size_t i;
-#if DEBUGGING
- printf ("vfm: init_zero=");
- for (i = 0; i < init_zero.n; i++)
- printf ("%s%s", i ? "," : "", index_to_varname (init_zero.vec[i]));
- printf (" init_blanks=");
- for (i = 0; i < init_blanks.n; i++)
- printf ("%s%s", i ? "," : "", index_to_varname (init_blanks.vec[i]));
- printf (" reinit_sysmis=");
- for (lp = reinit_sysmis.vec; *lp != -1; lp++)
- printf ("%s%s", lp != reinit_sysmis.vec ? "," : "",
- index_to_varname (*lp));
- printf (" reinit_blanks=");
- for (lp = reinit_blanks.vec; *lp != -1; lp++)
- printf ("%s%s", lp != reinit_blanks.vec ? "," : "",
- index_to_varname (*lp));
- printf ("\n");
-#endif
+ for (i = 0; i < var_cnt; i++)
+ {
+ struct variable *v = dict_get_var (default_dict, i);
+
+ if (v->type == NUMERIC)
+ {
+ if (v->reinit)
+ temp_case->data[v->fv].f = 0.0;
+ else
+ temp_case->data[v->fv].f = SYSMIS;
+ }
+ else
+ memset (temp_case->data[v->fv].s, ' ', v->width);
+ }
}
/* Sets filter_index to an appropriate value. */
\f
/* Closes the active file. */
static void
-close_active_file (void)
+close_active_file (struct write_case_data *data)
{
/* Close the current case group. */
- if (case_count && end_func != NULL)
- end_func ();
+ if (case_count && data->endfunc != NULL)
+ data->endfunc (data->aux);
/* Stop lagging (catch up?). */
if (n_lag)
/* Cancel transformations. */
cancel_transformations ();
- /* Clear value-initialization vectors. */
- vec_clear (&init_zero);
- vec_clear (&init_blanks);
- vec_clear (&reinit_sysmis);
- vec_clear (&reinit_blanks);
-
/* Turn off case limiter. */
dict_set_case_limit (default_dict, 0);
/* Reads all cases from the disk source and passes them one by one to
write_case(). */
static void
-disk_stream_read (void)
+disk_stream_read (write_case_func *write_case, write_case_data wc_data)
{
int i;
return;
}
- if (!write_case ())
+ if (!write_case (wc_data))
return;
}
}
/* Reads the case stream from memory and passes it to write_case(). */
static void
-memory_stream_read (void)
+memory_stream_read (write_case_func *write_case, write_case_data wc_data)
{
while (memory_source_cases != NULL)
{
free (current);
}
- if (!write_case ())
+ if (!write_case (wc_data))
return;
}
}
otherwise. Do not call this function again after it has returned
zero once. */
int
-procedure_write_case (void)
+procedure_write_case (write_case_data wc_data)
{
/* Index of current transformation. */
int cur_trns;
}
/* Call the beginning of group function. */
- if (!case_count && begin_func != NULL)
- begin_func ();
+ if (!case_count && wc_data->beginfunc != NULL)
+ wc_data->beginfunc (wc_data->aux);
/* Call the procedure if there is one and FILTER and PROCESS IF
don't prohibit it. */
- if (proc_func != NULL
+ if (wc_data->procfunc != NULL
&& !FILTERED
&& (process_if_expr == NULL ||
expr_evaluate (process_if_expr, temp_case, NULL) == 1.0))
- proc_func (temp_case);
+ wc_data->procfunc (temp_case, wc_data->aux);
case_count++;
done:
debug_putc ('\n', stdout);
-
- {
- long *lp;
- /* This case is finished. Initialize the variables for the next case. */
- for (lp = reinit_sysmis.vec; *lp != -1;)
- temp_case->data[*lp++].f = SYSMIS;
- for (lp = reinit_blanks.vec; *lp != -1;)
- memset (temp_case->data[*lp++].s, ' ', MAX_SHORT_STRING);
- }
+ clear_temp_case ();
/* Return previously determined value. */
return more_cases;
}
+/* Clears the variables in the temporary case that need to be
+ cleared between processing cases. */
+static void
+clear_temp_case (void)
+{
+ /* FIXME? This is linear in the number of variables, but
+ doesn't need to be, so it's an easy optimization target. */
+ size_t var_cnt = dict_get_var_cnt (default_dict);
+ size_t i;
+
+ for (i = 0; i < var_cnt; i++)
+ {
+ struct variable *v = dict_get_var (default_dict, i);
+ if (v->init && v->reinit)
+ {
+ if (v->type == NUMERIC)
+ temp_case->data[v->fv].f = SYSMIS;
+ else
+ memset (temp_case->data[v->fv].s, ' ', v->width);
+ }
+ }
+}
+
/* Appends TRNS to t_trns[], the list of all transformations to be
performed on data as it is read from the active file. */
void
SPLIT FILE is active. This function forms a wrapper around that
procfunc by dividing the input into series. */
static int
-SPLIT_FILE_procfunc (struct ccase *c)
+SPLIT_FILE_procfunc (struct ccase *c, void *data_)
{
+ struct write_case_data *data = data_;
static struct ccase *prev_case;
struct variable *const *split;
size_t split_cnt;
memcpy (prev_case, c, vfm_sink_info.case_size);
dump_splits (c);
- if (virt_begin_func != NULL)
- virt_begin_func ();
+ if (data->beginfunc != NULL)
+ data->beginfunc (data->aux);
- return virt_proc_func (c);
+ return data->procfunc (c, data->aux);
}
/* Compare the value of each SPLIT FILE variable to the values on
assert (0);
}
}
- return virt_proc_func (c);
+ return data->procfunc (c, data->aux);
not_equal:
/* The values of the SPLIT FILE variable are different from the
values on the previous case. That means that it's time to begin
a new series. */
- if (end_func != NULL)
- end_func ();
+ if (data->endfunc != NULL)
+ data->endfunc (data->aux);
dump_splits (c);
- if (virt_begin_func != NULL)
- virt_begin_func ();
+ if (data->beginfunc != NULL)
+ data->beginfunc (data->aux);
memcpy (prev_case, c, vfm_sink_info.case_size);
- return virt_proc_func (c);
+ return data->procfunc (c, data->aux);
}
\f
/* Case compaction. */