X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fvfm.c;h=45d9c28e8b436284bc795bd033b4479cf8a62433;hb=f0ca6129a78f32f13277dc7b56ec63ef771be340;hp=3a3bfde836cc4c475577fa05cb165df20d07b705;hpb=fcb9e49b2a2d57af7c001ae5d2eda9ac443ba36b;p=pspp diff --git a/src/vfm.c b/src/vfm.c index 3a3bfde836..45d9c28e8b 100644 --- a/src/vfm.c +++ b/src/vfm.c @@ -17,24 +17,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* AIX requires this to be the first thing in the file. */ #include -#if __GNUC__ -#define alloca __builtin_alloca -#else -#if HAVE_ALLOCA_H -#include -#else -#ifdef _AIX -#pragma alloca -#else -#ifndef alloca /* predefined by HP cc +Olibcalls */ -char *alloca (); -#endif -#endif -#endif -#endif - +#include "vfm.h" +#include "vfmP.h" #include #include #include @@ -53,9 +38,7 @@ char *alloca (); #include "str.h" #include "tab.h" #include "var.h" -#include "vector.h" -#include "vfm.h" -#include "vfmP.h" +#include "value-labels.h" /* Virtual File Manager (vfm): @@ -67,15 +50,18 @@ char *alloca (); #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; @@ -115,14 +101,6 @@ static int paging = 0; /* 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; @@ -133,53 +111,67 @@ static int lag_head; /* Index where next case will be added. */ 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); /* 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 (default_dict.n_splits && 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); } /* 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; @@ -192,28 +184,35 @@ 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; @@ -247,21 +246,13 @@ process_active_file_write_case (void) && !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; } @@ -297,9 +288,10 @@ prepare_for_writing (void) file--it's just a waste of time and space. */ vfm_sink_info.ncases = 0; - vfm_sink_info.nval = default_dict.nval; + vfm_sink_info.nval = dict_get_value_cnt (default_dict); vfm_sink_info.case_size = (sizeof (struct ccase) - + (default_dict.nval - 1) * sizeof (union value)); + + ((dict_get_value_cnt (default_dict) - 1) + * sizeof (union value))); if (vfm_sink == NULL) { @@ -329,19 +321,24 @@ arrange_compaction (void) int i; /* Count up the number of `value's that will be output. */ - for (i = 0; i < temp_dict->nvar; i++) - if (temp_dict->var[i]->name[0] != '#') - { - assert (temp_dict->var[i]->nv > 0); - count_values += temp_dict->var[i]->nv; - } - assert (temporary == 2 || count_values <= temp_dict->nval); + for (i = 0; i < dict_get_var_cnt (temp_dict); i++) + { + struct variable *v = dict_get_var (temp_dict, i); + + if (v->name[0] != '#') + { + assert (v->nv > 0); + count_values += v->nv; + } + } + assert (temporary == 2 || count_values <= dict_get_value_cnt (temp_dict)); } /* Compaction is only necessary if the number of `value's to output differs from the number already present. */ compaction_nval = count_values; - compaction_necessary = temporary == 2 || count_values != temp_dict->nval; + compaction_necessary = (temporary == 2 + || count_values != dict_get_value_cnt (temp_dict)); if (vfm_sink->init) vfm_sink->init (); @@ -377,67 +374,43 @@ index_to_varname (int ccase_index) } #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. */ static void setup_filter (void) { - filter_index = -1; + filter_var = dict_get_filter (default_dict); - if (default_dict.filter_var[0]) + if (filter_var != NULL) { - struct variable *fv = find_variable (default_dict.filter_var); - - if (fv == NULL || fv->type == ALPHA) - default_dict.filter_var[0] = 0; - else - { - filter_index = fv->index; - filter_var = fv; - } + assert (filter_var->type == NUMERIC); + filter_index = filter_var->index; + } else { + filter_index = -1; } } @@ -454,7 +427,8 @@ setup_lag (void) lag_head = 0; lag_queue = xmalloc (n_lag * sizeof *lag_queue); for (i = 0; i < n_lag; i++) - lag_queue[i] = xmalloc (temp_dict->nval * sizeof **lag_queue); + lag_queue[i] = xmalloc (dict_get_value_cnt (temp_dict) + * sizeof **lag_queue); } /* There is a lot of potential confusion in the vfm and related @@ -503,7 +477,7 @@ open_active_file (void) if (!temporary) { temp_trns = n_trns; - temp_dict = &default_dict; + temp_dict = default_dict; } /* No cases passed to the procedure yet. */ @@ -514,7 +488,6 @@ open_active_file (void) arrange_compaction (); make_temp_case (); vector_initialization (); - setup_randomize (); discard_ctl_stack (); setup_filter (); setup_lag (); @@ -531,11 +504,11 @@ open_active_file (void) /* 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) @@ -552,12 +525,14 @@ close_active_file (void) off TEMPORARY. */ if (temporary) { - restore_dictionary (temp_dict); + dict_destroy (default_dict); + default_dict = temp_dict; temp_dict = NULL; } - /* The default dictionary assumes the compacted data size. */ - default_dict.nval = compaction_nval; + /* Finish compaction. */ + if (compaction_necessary) + finish_compaction (); /* Old data sink --> New data source. */ if (vfm_source && vfm_source->destroy_source) @@ -574,9 +549,7 @@ close_active_file (void) /* Old data sink is gone now. */ vfm_sink = NULL; - /* Finish compaction. */ - if (compaction_necessary) - finish_compaction (); + /* Cancel TEMPORARY. */ cancel_temporary (); /* Free temporary cases. */ @@ -591,31 +564,17 @@ close_active_file (void) process_if_expr = NULL; /* Cancel FILTER if temporary. */ - if (filter_index != -1 && !FILTER_before_TEMPORARY) - default_dict.filter_var[0] = 0; + if (filter_var != NULL && !FILTER_before_TEMPORARY) + dict_set_filter (default_dict, NULL); /* 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. */ - default_dict.N = 0; + dict_set_case_limit (default_dict, 0); /* Clear VECTOR vectors. */ - { - int i; - - for (i = 0; i < nvec; i++) - free (vec[i].v); - free (vec); - vec = NULL; - nvec = 0; - } + dict_clear_vectors (default_dict); debug_printf (("vfm: procedure complete\n\n")); } @@ -643,7 +602,7 @@ disk_stream_init (void) /* 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; @@ -658,7 +617,7 @@ disk_stream_read (void) return; } - if (!write_case ()) + if (!write_case (wc_data)) return; } } @@ -762,7 +721,7 @@ memory_stream_init (void) /* 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) { @@ -774,7 +733,7 @@ memory_stream_read (void) free (current); } - if (!write_case ()) + if (!write_case (wc_data)) return; } } @@ -950,7 +909,8 @@ lag_case (void) { if (lag_count < n_lag) lag_count++; - memcpy (lag_queue[lag_head], temp_case, sizeof (union value) * temp_dict->nval); + memcpy (lag_queue[lag_head], temp_case, + sizeof (union value) * dict_get_value_cnt (temp_dict)); if (++lag_head >= n_lag) lag_head = 0; } @@ -977,7 +937,7 @@ lagged_case (int n_before) 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; @@ -1001,8 +961,9 @@ procedure_write_case (void) vfm_sink_info.ncases++; vfm_sink->write (); - if (default_dict.N) - more_cases = vfm_sink_info.ncases < default_dict.N; + if (dict_get_case_limit (default_dict)) + more_cases = (vfm_sink_info.ncases + < dict_get_case_limit (default_dict)); } /* Are we done? */ @@ -1034,36 +995,51 @@ procedure_write_case (void) } /* 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 @@ -1102,22 +1078,25 @@ cancel_transformations (void) static void dump_splits (struct ccase *c) { - struct variable **iter; + struct variable *const *split; struct tab_table *t; + size_t split_cnt; int i; - t = tab_create (3, default_dict.n_splits + 1, 0); + split_cnt = dict_get_split_cnt (default_dict); + t = tab_create (3, split_cnt + 1, 0); tab_dim (t, tab_natural_dimensions); - tab_vline (t, TAL_1 | TAL_SPACING, 1, 0, default_dict.n_splits); - tab_vline (t, TAL_1 | TAL_SPACING, 2, 0, default_dict.n_splits); + tab_vline (t, TAL_1 | TAL_SPACING, 1, 0, split_cnt); + tab_vline (t, TAL_1 | TAL_SPACING, 2, 0, split_cnt); tab_text (t, 0, 0, TAB_NONE, _("Variable")); tab_text (t, 1, 0, TAB_LEFT, _("Value")); tab_text (t, 2, 0, TAB_LEFT, _("Label")); - for (iter = default_dict.splits, i = 0; *iter; iter++, i++) + split = dict_get_split_vars (default_dict); + for (i = 0; i < split_cnt; i++) { - struct variable *v = *iter; + struct variable *v = split[i]; char temp_buf[80]; - char *val_lab; + const char *val_lab; assert (v->type == NUMERIC || v->type == ALPHA); tab_text (t, 0, i + 1, TAB_LEFT | TAT_PRINTF, "%s", v->name); @@ -1132,7 +1111,7 @@ dump_splits (struct ccase *c) temp_buf[v->print.w] = 0; tab_text (t, 1, i + 1, TAT_PRINTF, "%.*s", v->print.w, temp_buf); - val_lab = get_val_lab (v, c->data[v->fv], 0); + val_lab = val_labs_find (v->val_labs, c->data[v->fv]); if (val_lab) tab_text (t, 2, i + 1, TAB_LEFT, val_lab); } @@ -1144,10 +1123,13 @@ dump_splits (struct ccase *c) 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 **iter; + struct variable *const *split; + size_t split_cnt; + size_t i; /* The first case always begins a new series. We also need to preserve the values of the case for later comparison. */ @@ -1159,17 +1141,19 @@ SPLIT_FILE_procfunc (struct ccase *c) 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 the previous case. */ - for (iter = default_dict.splits; *iter; iter++) + split = dict_get_split_vars (default_dict); + split_cnt = dict_get_split_cnt (default_dict); + for (i = 0; i < split_cnt; i++) { - struct variable *v = *iter; + struct variable *v = split[i]; switch (v->type) { @@ -1185,19 +1169,19 @@ SPLIT_FILE_procfunc (struct ccase *c) 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); } /* Case compaction. */ @@ -1208,6 +1192,7 @@ compact_case (struct ccase *dest, const struct ccase *src) { int i; int nval = 0; + size_t var_cnt; assert (compaction_necessary); @@ -1220,9 +1205,10 @@ compact_case (struct ccase *dest, const struct ccase *src) /* Copy all the variables except the scratch variables from SRC to DEST. */ - for (i = 0; i < default_dict.nvar; i++) + var_cnt = dict_get_var_cnt (default_dict); + for (i = 0; i < var_cnt; i++) { - struct variable *v = default_dict.var[i]; + struct variable *v = dict_get_var (default_dict, i); if (v->name[0] == '#') continue; @@ -1243,35 +1229,18 @@ compact_case (struct ccase *dest, const struct ccase *src) static void finish_compaction (void) { - int copy_index = 0; - int nval = 0; int i; - for (i = 0; i < default_dict.nvar; i++) + for (i = 0; i < dict_get_var_cnt (default_dict); ) { - struct variable *v = default_dict.var[i]; - - if (v->name[0] == '#') - { - clear_variable (&default_dict, v); - free (v); - continue; - } + struct variable *v = dict_get_var (default_dict, i); - v->fv = nval; - if (v->type == NUMERIC) - nval++; + if (v->name[0] == '#') + dict_delete_var (default_dict, v); else - nval += DIV_RND_UP (v->width, sizeof (union value)); - - default_dict.var[copy_index++] = v; - } - if (copy_index != default_dict.nvar) - { - default_dict.var = xrealloc (default_dict.var, - sizeof *default_dict.var * copy_index); - default_dict.nvar = copy_index; + i++; } + dict_compact_values (default_dict); }