#include "data/value-labels.h"
#include "data/vardict.h"
#include "data/variable.h"
+#include "data/varset.h"
#include "data/vector.h"
#include "libpspp/array.h"
#include "libpspp/assertion.h"
struct caseproto *proto; /* Prototype for dictionary cases
(updated lazily). */
struct hmap name_map; /* Variable index by name. */
- int next_value_idx; /* Index of next `union value' to allocate. */
const struct variable **split; /* SPLIT FILE vars. */
size_t n_splits; /* SPLIT FILE count. */
enum split_type split_type;
struct attrset attributes; /* Custom attributes. */
struct mrset **mrsets; /* Multiple response sets. */
size_t n_mrsets; /* Number of multiple response sets. */
+ struct varset **varsets; /* Variable sets. */
+ size_t n_varsets; /* Number of variable sets. */
/* Whether variable names must be valid identifiers. Normally, this is
true, but sometimes a dictionary is prepared for external use
static void dict_unset_split_var (struct dictionary *, struct variable *, bool);
static void dict_unset_mrset_var (struct dictionary *, struct variable *);
+static void dict_unset_varset_var (struct dictionary *, struct variable *);
/* Compares two double pointers to variables, which should point
to elements of a struct dictionary's `var' member array. */
}
}
-/* Sets the case_index in V's vardict to CASE_INDEX. */
-static void
-set_var_case_index (struct variable *v, int case_index)
-{
- var_get_vardict (v)->case_index = case_index;
-}
-
/* Removes the dictionary variables with indexes from FROM to TO (exclusive)
from name_map. */
static void
return d->encoding ;
}
+/* Checks whether UTF-8 string ID is an acceptable identifier in DICT's
+ encoding. Returns true if it is, otherwise an error message that the caller
+ must free(). */
+char * WARN_UNUSED_RESULT
+dict_id_is_valid__ (const struct dictionary *dict, const char *id)
+{
+ if (!dict->names_must_be_ids)
+ return NULL;
+ return id_is_valid__ (id, dict->encoding);
+}
+
+static bool
+error_to_bool (char *error)
+{
+ if (error)
+ {
+ free (error);
+ return false;
+ }
+ else
+ return true;
+}
+
/* Returns true if UTF-8 string ID is an acceptable identifier in DICT's
- encoding, false otherwise. If ISSUE_ERROR is true, issues an explanatory
- error message on failure. */
+ encoding, false otherwise. */
bool
-dict_id_is_valid (const struct dictionary *dict, const char *id,
- bool issue_error)
+dict_id_is_valid (const struct dictionary *dict, const char *id)
{
- return (!dict->names_must_be_ids
- || id_is_valid (id, dict->encoding, issue_error));
+ return error_to_bool (dict_id_is_valid__ (dict, id));
}
void
void
dict_dump (const struct dictionary *d)
{
- int i;
- for (i = 0 ; i < d->n_vars ; ++i)
- {
- const struct variable *v = d->vars[i].var;
- printf ("Name: %s;\tdict_idx: %zu; case_idx: %zu\n",
- var_get_name (v),
- var_get_dict_index (v),
- var_get_case_index (v));
-
- }
+ for (size_t i = 0; i < d->n_vars; ++i)
+ printf ("%zu: %s\n", i, var_get_name (d->vars[i].var));
}
/* Associate CALLBACKS with DICT. Callbacks will be invoked whenever
/* Creates and returns a (deep) copy of an existing
dictionary.
- The new dictionary's case indexes are copied from the old
- dictionary. If the new dictionary won't be used to access
- cases produced with the old dictionary, then the new
- dictionary's case indexes should be compacted with
- dict_compact_values to save space.
-
Callbacks are not cloned. */
struct dictionary *
dict_clone (const struct dictionary *s)
{
- struct dictionary *d;
- size_t i;
-
- d = dict_create (s->encoding);
+ struct dictionary *d = dict_create (s->encoding);
dict_set_names_must_be_ids (d, dict_get_names_must_be_ids (s));
- for (i = 0; i < s->n_vars; i++)
+ for (size_t i = 0; i < s->n_vars; i++)
{
struct variable *sv = s->vars[i].var;
struct variable *dv = dict_clone_var_assert (d, sv);
- size_t i;
-
- for (i = 0; i < var_get_n_short_names (sv); i++)
- var_set_short_name (dv, i, var_get_short_name (sv, i));
- var_get_vardict (dv)->case_index = var_get_vardict (sv)->case_index;
+ for (size_t j = 0; j < var_get_n_short_names (sv); j++)
+ var_set_short_name (dv, j, var_get_short_name (sv, j));
}
- d->next_value_idx = s->next_value_idx;
-
d->n_splits = s->n_splits;
if (d->n_splits > 0)
{
d->split = xnmalloc (d->n_splits, sizeof *d->split);
- for (i = 0; i < d->n_splits; i++)
+ for (size_t i = 0; i < d->n_splits; i++)
d->split[i] = dict_lookup_var_assert (d, var_get_name (s->split[i]));
}
d->split_type = s->split_type;
d->n_vectors = s->n_vectors;
d->vector = xnmalloc (d->n_vectors, sizeof *d->vector);
- for (i = 0; i < s->n_vectors; i++)
+ for (size_t i = 0; i < s->n_vectors; i++)
d->vector[i] = vector_clone (s->vector[i], s, d);
dict_set_attributes (d, dict_get_attributes (s));
- for (i = 0; i < s->n_mrsets; i++)
+ for (size_t i = 0; i < s->n_mrsets; i++)
{
const struct mrset *old = s->mrsets[i];
struct mrset *new;
dict_add_mrset (d, new);
}
- return d;
-}
+ for (size_t i = 0; i < s->n_varsets; i++)
+ {
+ const struct varset *old = s->varsets[i];
+ /* Clone old varset, then replace vars from D by vars from S. */
+ struct varset *new = varset_clone (old);
+ for (size_t j = 0; j < new->n_vars; j++)
+ new->vars[j] = dict_lookup_var_assert (d, var_get_name (new->vars[j]));
+ dict_add_varset (d, new);
+ }
+
+ return d;
+}
\f
/* Returns the SPLIT FILE vars (see cmd_split_file()). Call
dict_get_n_splits() to determine how many SPLIT FILE vars
assert (n == 0 || split != NULL);
d->n_splits = n;
- d->split_type = type == SPLIT_NONE ? SPLIT_LAYERED : type;
+ d->split_type = (n == 0 ? SPLIT_NONE
+ : type == SPLIT_NONE ? SPLIT_LAYERED
+ : type);
if (n > 0)
- {
- d->split = xnrealloc (d->split, n, sizeof *d->split) ;
- memcpy (d->split, split, n * sizeof *d->split);
- }
+ {
+ d->split = xnrealloc (d->split, n, sizeof *d->split) ;
+ memcpy (d->split, split, n * sizeof *d->split);
+ }
else
- {
- free (d->split);
- d->split = NULL;
- }
+ {
+ free (d->split);
+ d->split = NULL;
+ }
- if (!skip_callbacks)
+ if (!skip_callbacks)
{
if (d->changed) d->changed (d, d->changed_data);
if (d->callbacks && d->callbacks->split_changed)
dict_delete_var__ (struct dictionary *d, struct variable *v, bool skip_callbacks)
{
int dict_index = var_get_dict_index (v);
- const int case_index = var_get_case_index (v);
assert (dict_contains_var (d, v));
dict_unset_split_var (d, v, skip_callbacks);
dict_unset_mrset_var (d, v);
+ dict_unset_varset_var (d, v);
if (d->weight == v)
dict_set_weight (d, NULL);
if (! skip_callbacks)
{
if (d->changed) d->changed (d, d->changed_data);
- if (d->callbacks && d->callbacks->var_deleted)
- d->callbacks->var_deleted (d, v, dict_index, case_index, d->cb_data);
+ if (d->callbacks && d->callbacks->vars_deleted)
+ d->callbacks->vars_deleted (d, dict_index, 1, d->cb_data);
}
invalidate_proto (d);
dict_delete_var (struct dictionary *d, struct variable *v)
{
dict_delete_var__ (d, v, false);
+ invalidate_proto (d);
}
while (count-- > 0)
dict_delete_var (d, *vars++);
+ invalidate_proto (d);
}
/* Deletes the COUNT variables in D starting at index IDX. This
{
assert (idx + count <= d->n_vars);
- /* We need to store the variable and the corresponding case_index
- for the delete callbacks later. We store them in a linked list.*/
- struct delvar {
- struct ll ll;
- struct variable *var;
- int case_index;
- };
- struct ll_list list = LL_INITIALIZER (list);
+ struct variable **vars = xnmalloc (count, sizeof *vars);
- for (size_t i = idx; i < idx + count; i++)
+ for (size_t i = 0; i < count; i++)
{
- struct delvar *dv = xmalloc (sizeof (struct delvar));
- assert (dv);
- struct variable *v = d->vars[i].var;
+ struct variable *v = d->vars[idx + i].var;
+ vars[i] = v;
dict_unset_split_var (d, v, false);
dict_unset_mrset_var (d, v);
+ dict_unset_varset_var (d, v);
if (d->weight == v)
dict_set_weight (d, NULL);
if (d->filter == v)
dict_set_filter (d, NULL);
-
- dv->var = v;
- dv->case_index = var_get_case_index (v);
- ll_push_tail (&list, (struct ll *)dv);
}
dict_clear_vectors (d);
/* Now issue the variable delete callbacks and delete
the variables. The vardict is not valid at this point
- anymore. That is the reason why we stored the
- caseindex before reindexing. */
- for (size_t vi = idx; vi < idx + count; vi++)
+ anymore. */
+ if (d->callbacks && d->callbacks->vars_deleted)
+ d->callbacks->vars_deleted (d, idx, count, d->cb_data);
+ for (size_t i = 0; i < count; i++)
{
- struct delvar *dv = (struct delvar *) ll_pop_head (&list);
- var_clear_vardict (dv->var);
- if (d->callbacks && d->callbacks->var_deleted)
- d->callbacks->var_deleted (d, dv->var, vi, dv->case_index, d->cb_data);
- var_unref (dv->var);
- free (dv);
+ var_clear_vardict (vars[i]);
+ var_unref (vars[i]);
}
+ free (vars);
+
+ invalidate_proto (d);
}
/* Deletes scratch variables from dictionary D. */
dict_delete_var (d, d->vars[i].var);
else
i++;
+
+ invalidate_proto (d);
}
\f
d->n_vars = d->allocated_vars = 0;
invalidate_proto (d);
hmap_clear (&d->name_map);
- d->next_value_idx = 0;
dict_set_split_vars__ (d, NULL, 0, SPLIT_NONE, skip_callbacks);
if (skip_callbacks)
hmap_destroy (&d->name_map);
attrset_destroy (&d->attributes);
dict_clear_mrsets (d);
+ dict_clear_varsets (d);
free (d->encoding);
free (d);
}
}
static struct variable *
-add_var_with_case_index (struct dictionary *d, struct variable *v,
- int case_index)
+add_var (struct dictionary *d, struct variable *v)
{
- struct vardict_info *vardict;
-
- assert (case_index >= d->next_value_idx);
-
/* Update dictionary. */
if (d->n_vars >= d->allocated_vars)
{
}
}
- vardict = &d->vars[d->n_vars++];
- vardict->dict = d;
- vardict->var = v;
+ struct vardict_info *vardict = &d->vars[d->n_vars++];
+ *vardict = (struct vardict_info) {
+ .dict = d,
+ .var = v,
+ };
hmap_insert (&d->name_map, &vardict->name_node,
utf8_hash_case_string (var_get_name (v), 0));
- vardict->case_index = case_index;
var_set_vardict (v, vardict);
if (d->changed) d->changed (d, d->changed_data);
d->callbacks->var_added (d, var_get_dict_index (v), d->cb_data);
invalidate_proto (d);
- d->next_value_idx = case_index + 1;
return v;
}
-static struct variable *
-add_var (struct dictionary *d, struct variable *v)
-{
- return add_var_with_case_index (d, v, d->next_value_idx);
-}
-
/* Creates and returns a new variable in D with the given NAME
and WIDTH. Returns a null pointer if the given NAME would
duplicate that of an existing variable in the dictionary. */
return add_var (d, new_var);
}
-struct variable *
-dict_clone_var_in_place_assert (struct dictionary *d,
- const struct variable *old_var)
-{
- assert (dict_lookup_var (d, var_get_name (old_var)) == NULL);
- return add_var_with_case_index (d, var_clone (old_var),
- var_get_case_index (old_var));
-}
-
/* Returns the variable named NAME in D, or a null pointer if no
variable has that name. */
struct variable *
void
dict_reorder_var (struct dictionary *d, struct variable *v, size_t new_index)
{
- size_t old_index = var_get_dict_index (v);
-
assert (new_index < d->n_vars);
+ size_t old_index = var_get_dict_index (v);
+ if (new_index == old_index)
+ return;
+
unindex_vars (d, MIN (old_index, new_index), MAX (old_index, new_index) + 1);
move_element (d->vars, d->n_vars, sizeof *d->vars, old_index, new_index);
reindex_vars (d, MIN (old_index, new_index), MAX (old_index, new_index) + 1, false);
+
+ if (d->callbacks && d->callbacks->var_moved)
+ d->callbacks->var_moved (d, new_index, old_index, d->cb_data);
}
/* Reorders the variables in D, placing the COUNT variables
}
/* Returns the format to use for weights. */
-const struct fmt_spec *
+struct fmt_spec
dict_get_weight_format (const struct dictionary *d)
{
- return d->weight ? var_get_print_format (d->weight) : &F_8_0;
+ return d->weight ? var_get_print_format (d->weight) : F_8_0;
}
/* Sets the weighting variable of D to V, or turning off
struct dictionary *d = CONST_CAST (struct dictionary *, d_);
if (d->proto == NULL)
{
- size_t i;
-
- d->proto = caseproto_create ();
- d->proto = caseproto_reserve (d->proto, d->n_vars);
- for (i = 0; i < d->n_vars; i++)
- d->proto = caseproto_set_width (d->proto,
- var_get_case_index (d->vars[i].var),
- var_get_width (d->vars[i].var));
+ short int *widths = xnmalloc (d->n_vars, sizeof *widths);
+ for (size_t i = 0; i < d->n_vars; i++)
+ widths[i] = var_get_width (d->vars[i].var);
+ d->proto = caseproto_from_widths (widths, d->n_vars);
}
return d->proto;
}
-/* Returns the case index of the next value to be added to D.
- This value is the number of `union value's that need to be
- allocated to store a case for dictionary D. */
-int
-dict_get_next_value_idx (const struct dictionary *d)
-{
- return d->next_value_idx;
-}
-
-/* Returns the number of bytes needed to store a case for
- dictionary D. */
-size_t
-dict_get_case_size (const struct dictionary *d)
-{
- return sizeof (union value) * dict_get_next_value_idx (d);
-}
-
-/* Reassigns values in dictionary D so that fragmentation is
- eliminated. */
-void
-dict_compact_values (struct dictionary *d)
-{
- size_t i;
-
- d->next_value_idx = 0;
- for (i = 0; i < d->n_vars; i++)
- {
- struct variable *v = d->vars[i].var;
- set_var_case_index (v, d->next_value_idx++);
- }
- invalidate_proto (d);
-}
-
/* Returns the number of values occupied by the variables in
dictionary D. All variables are considered if EXCLUDE_CLASSES
- is 0, or it may contain one or more of (1u << DC_ORDINARY),
- (1u << DC_SYSTEM), or (1u << DC_SCRATCH) to exclude the
- corresponding type of variable.
-
- The return value may be less than the number of values in one
- of dictionary D's cases (as returned by
- dict_get_next_value_idx) even if E is 0, because there may be
- gaps in D's cases due to deleted variables. */
+ is 0, or it may contain one or more of DC_ORDINARY, DC_SYSTEM,
+ or DC_SCRATCH to exclude the corresponding type of variable. */
size_t
dict_count_values (const struct dictionary *d, unsigned int exclude_classes)
{
- assert ((exclude_classes & ~((1u << DC_ORDINARY)
- | (1u << DC_SYSTEM)
- | (1u << DC_SCRATCH))) == 0);
+ assert (!(exclude_classes & ~DC_ALL));
size_t n = 0;
for (size_t i = 0; i < d->n_vars; i++)
{
enum dict_class class = var_get_dict_class (d->vars[i].var);
- if (!(exclude_classes & (1u << class)))
+ if (!(exclude_classes & class))
n++;
}
return n;
}
-/* Returns the case prototype that would result after deleting
- all variables from D that are not in one of the
- EXCLUDE_CLASSES and compacting the dictionary with
- dict_compact().
-
- The caller must unref the returned caseproto when it is no
- longer needed. */
-struct caseproto *
-dict_get_compacted_proto (const struct dictionary *d,
- unsigned int exclude_classes)
-{
- struct caseproto *proto;
- size_t i;
-
- assert ((exclude_classes & ~((1u << DC_ORDINARY)
- | (1u << DC_SYSTEM)
- | (1u << DC_SCRATCH))) == 0);
-
- proto = caseproto_create ();
- for (i = 0; i < d->n_vars; i++)
- {
- struct variable *v = d->vars[i].var;
- if (!(exclude_classes & (1u << var_get_dict_class (v))))
- proto = caseproto_add_width (proto, var_get_width (v));
- }
- return proto;
-}
/* Returns the file label for D, or a null pointer if D is
unlabeled (see cmd_file_label()). */
const char *
}
}
\f
+
+/* Returns the variable set in DICT with index IDX, which must be between 0 and
+ the count returned by dict_get_n_varsets(), exclusive. */
+const struct varset *
+dict_get_varset (const struct dictionary *dict, size_t idx)
+{
+ assert (idx < dict->n_varsets);
+ return dict->varsets[idx];
+}
+
+/* Returns the number of variable sets in DICT. */
+size_t
+dict_get_n_varsets (const struct dictionary *dict)
+{
+ return dict->n_varsets;
+}
+
+/* Looks for a variable set named NAME in DICT. If it finds one, returns its
+ index; otherwise, returns SIZE_MAX. */
+static size_t
+dict_lookup_varset_idx (const struct dictionary *dict, const char *name)
+{
+ for (size_t i = 0; i < dict->n_varsets; i++)
+ if (!utf8_strcasecmp (name, dict->varsets[i]->name))
+ return i;
+
+ return SIZE_MAX;
+}
+
+/* Looks for a multiple response set named NAME in DICT. If it finds one,
+ returns it; otherwise, returns NULL. */
+const struct varset *
+dict_lookup_varset (const struct dictionary *dict, const char *name)
+{
+ size_t idx = dict_lookup_varset_idx (dict, name);
+ return idx != SIZE_MAX ? dict->varsets[idx] : NULL;
+}
+
+/* Adds VARSET to DICT, replacing any existing set with the same name. Returns
+ true if a set was replaced, false if none existed with the specified name.
+
+ Ownership of VARSET is transferred to DICT. */
+bool
+dict_add_varset (struct dictionary *dict, struct varset *varset)
+{
+ size_t idx = dict_lookup_varset_idx (dict, varset->name);
+ if (idx == SIZE_MAX)
+ {
+ dict->varsets = xrealloc (dict->varsets,
+ (dict->n_varsets + 1) * sizeof *dict->varsets);
+ dict->varsets[dict->n_varsets++] = varset;
+ return true;
+ }
+ else
+ {
+ varset_destroy (dict->varsets[idx]);
+ dict->varsets[idx] = varset;
+ return false;
+ }
+}
+
+/* Deletes all variable sets from DICT. */
+void
+dict_clear_varsets (struct dictionary *dict)
+{
+ for (size_t i = 0; i < dict->n_varsets; i++)
+ varset_destroy (dict->varsets[i]);
+ free (dict->varsets);
+ dict->varsets = NULL;
+ dict->n_varsets = 0;
+}
+
+/* Removes VAR, which must be in DICT, from DICT's multiple response sets. */
+static void
+dict_unset_varset_var (struct dictionary *dict, struct variable *var)
+{
+ assert (dict_contains_var (dict, var));
+
+ for (size_t i = 0; i < dict->n_varsets; i++)
+ {
+ struct varset *varset = dict->varsets[i];
+
+ for (size_t j = 0; j < varset->n_vars;)
+ if (varset->vars[j] == var)
+ remove_element (varset->vars, varset->n_vars--,
+ sizeof *varset->vars, j);
+ else
+ j++;
+ }
+}
+\f
/* Returns D's attribute set. The caller may examine or modify
the attribute set, but must not destroy it. Destroying D or
calling dict_set_attributes for D will also destroy D's
}
-\f
-/* Dictionary used to contain "internal variables". */
-static struct dictionary *internal_dict;
-
-/* Create a variable of the specified WIDTH to be used for internal
- calculations only. The variable is assigned case index CASE_IDX. */
-struct variable *
-dict_create_internal_var (int case_idx, int width)
-{
- if (internal_dict == NULL)
- internal_dict = dict_create ("UTF-8");
-
- for (;;)
- {
- static int counter = INT_MAX / 2;
- struct variable *var;
- char name[64];
-
- if (++counter == INT_MAX)
- counter = INT_MAX / 2;
-
- sprintf (name, "$internal%d", counter);
- var = dict_create_var (internal_dict, name, width);
- if (var != NULL)
- {
- set_var_case_index (var, case_idx);
- return var;
- }
- }
-}
-
-/* Destroys VAR, which must have been created with
- dict_create_internal_var(). */
-void
-dict_destroy_internal_var (struct variable *var)
-{
- if (var != NULL)
- {
- dict_delete_var (internal_dict, var);
-
- /* Destroy internal_dict if it has no variables left, just so that
- valgrind --leak-check --show-reachable won't show internal_dict. */
- if (dict_get_n_vars (internal_dict) == 0)
- {
- dict_unref (internal_dict);
- internal_dict = NULL;
- }
- }
-}
\f
int
vardict_get_dict_index (const struct vardict_info *vardict)