X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fdata%2Fvalue-labels.c;h=c8061f7bd5d7fee54c47f5e65aa739b442f10dce;hb=5c3291dc396b795696e94f47780308fd7ace6fc4;hp=dc18a707f6b9c4274aefe3b9fad5f0ef2ed14f3f;hpb=2322678e8fddbbf158b01b2720db2636404bba3b;p=pspp-builds.git diff --git a/src/data/value-labels.c b/src/data/value-labels.c index dc18a707..c8061f7b 100644 --- a/src/data/value-labels.c +++ b/src/data/value-labels.c @@ -1,519 +1,358 @@ -/* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. - Written by Ben Pfaff . +/* PSPP - a program for statistical analysis. + Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc. - This program 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 2 of the - License, or (at your option) any later version. + This program 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. - This program 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. + This program 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. */ + along with this program. If not, see . */ #include + #include "value-labels.h" -#include "message.h" + #include -#include "alloc.h" -#include "compiler.h" -#include "hash.h" -#include "str.h" -static hsh_compare_func compare_int_val_lab; -static hsh_hash_func hash_int_val_lab; -static hsh_free_func free_int_val_lab; +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xalloc.h" -struct atom; static struct atom *atom_create (const char *string); static void atom_destroy (struct atom *); -static char *atom_to_string (const struct atom *); +static const char *atom_to_string (const struct atom *); -/* A set of value labels. */ -struct val_labs - { - int width; /* 0=numeric, otherwise string width. */ - struct hsh_table *labels; /* Hash table of `struct int_val_lab's. */ - }; +/* Returns the label in VL. The caller must not modify or free + the returned value. */ +const char * +val_lab_get_label (const struct val_lab *vl) +{ + return atom_to_string (vl->label); +} /* Creates and returns a new, empty set of value labels with the - given WIDTH, which must designate a numeric (0) or short - string (1...MAX_SHORT_STRING inclusive) width. */ + given WIDTH. */ struct val_labs * -val_labs_create (int width) +val_labs_create (int width) { - struct val_labs *vls; - - assert (width >= 0); - - vls = xmalloc (sizeof *vls); + struct val_labs *vls = xmalloc (sizeof *vls); vls->width = width; - vls->labels = NULL; + hmap_init (&vls->labels); return vls; } /* Creates and returns a new set of value labels identical to - VLS. */ + VLS. Returns a null pointer if VLS is null. */ struct val_labs * -val_labs_copy (const struct val_labs *vls) +val_labs_clone (const struct val_labs *vls) { struct val_labs *copy; - struct val_labs_iterator *i; - struct val_lab *vl; + struct val_lab *label; - assert (vls != NULL); + if (vls == NULL) + return NULL; copy = val_labs_create (vls->width); - for (vl = val_labs_first (vls, &i); vl != NULL; - vl = val_labs_next (vls, &i)) - val_labs_add (copy, vl->value, vl->label); + HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels) + val_labs_add (copy, &label->value, atom_to_string (label->label)); return copy; } -/* Changes the width of VLS to NEW_WIDTH. If VLS is numeric, - NEW_WIDTH must be 0, otherwise it must be within the range - 1...MAX_SHORT_STRING inclusive. */ -void -val_labs_set_width (struct val_labs *vls, int new_width) +/* Determines whether VLS's width can be changed to NEW_WIDTH, + using the rules checked by value_is_resizable. */ +bool +val_labs_can_set_width (const struct val_labs *vls, int new_width) { - assert (vls != NULL); - assert ((vls->width == 0) == (new_width == 0)); + struct val_lab *label; + + HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels) + if (!value_is_resizable (&label->value, vls->width, new_width)) + return false; + + return true; +} +/* Changes the width of VLS to NEW_WIDTH. The original and new + width must be both numeric or both string. */ +void +val_labs_set_width (struct val_labs *vls, int new_width) +{ + assert (val_labs_can_set_width (vls, new_width)); + if (value_needs_resize (vls->width, new_width)) + { + struct val_lab *label; + HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels) + value_resize (&label->value, vls->width, new_width); + } vls->width = new_width; } /* Destroys VLS. */ void -val_labs_destroy (struct val_labs *vls) +val_labs_destroy (struct val_labs *vls) { - if (vls != NULL) + if (vls != NULL) { - if (vls->labels != NULL) - hsh_destroy (vls->labels); + val_labs_clear (vls); + hmap_destroy (&vls->labels); free (vls); } } /* Removes all the value labels from VLS. */ void -val_labs_clear (struct val_labs *vls) +val_labs_clear (struct val_labs *vls) { - assert (vls != NULL); + struct val_lab *label, *next; - hsh_destroy (vls->labels); - vls->labels = NULL; + HMAP_FOR_EACH_SAFE (label, next, struct val_lab, node, &vls->labels) + { + hmap_delete (&vls->labels, &label->node); + value_destroy (&label->value, vls->width); + atom_destroy (label->label); + free (label); + } } -/* Returns the number of value labels in VLS. */ +/* Returns the number of value labels in VLS. + Returns 0 if VLS is null. */ size_t -val_labs_count (const struct val_labs *vls) +val_labs_count (const struct val_labs *vls) { - assert (vls != NULL); - - if (vls->labels == NULL) - return 0; - else - return hsh_count (vls->labels); + return vls == NULL ? 0 : hmap_count (&vls->labels); } -/* One value label in internal format. */ -struct int_val_lab - { - union value value; /* The value being labeled. */ - struct atom *label; /* A ref-counted string. */ - }; - -/* Creates and returns an int_val_lab based on VALUE and - LABEL. */ -static struct int_val_lab * -create_int_val_lab (struct val_labs *vls, union value value, const char *label) +static void +do_add_val_lab (struct val_labs *vls, const union value *value, + const char *label) { - struct int_val_lab *ivl; - - assert (label != NULL); - assert (vls->width <= MAX_SHORT_STRING); - - ivl = xmalloc (sizeof *ivl); - ivl->value = value; - if (vls->width > 0) - memset (ivl->value.s + vls->width, ' ', MAX_SHORT_STRING - vls->width); - ivl->label = atom_create (label); - - return ivl; + struct val_lab *lab = xmalloc (sizeof *lab); + value_init (&lab->value, vls->width); + value_copy (&lab->value, value, vls->width); + lab->label = atom_create (label); + hmap_insert (&vls->labels, &lab->node, value_hash (value, vls->width, 0)); } /* If VLS does not already contain a value label for VALUE, adds - LABEL for it and returns nonzero. Otherwise, returns zero. - Behavior is undefined if VLS's width is greater than - MAX_SHORT_STRING. */ -int -val_labs_add (struct val_labs *vls, union value value, const char *label) + LABEL for it and returns true. Otherwise, returns false. */ +bool +val_labs_add (struct val_labs *vls, const union value *value, + const char *label) { - struct int_val_lab *ivl; - void **vlpp; - - assert (vls != NULL); - assert (vls->width <= MAX_SHORT_STRING); - assert (label != NULL); - - if (vls->labels == NULL) - vls->labels = hsh_create (8, compare_int_val_lab, hash_int_val_lab, - free_int_val_lab, vls); - - ivl = create_int_val_lab (vls, value, label); - vlpp = hsh_probe (vls->labels, ivl); - if (*vlpp == NULL) - { - *vlpp = ivl; - return 1; - } - else + const struct val_lab *lab = val_labs_lookup (vls, value); + if (lab == NULL) { - free_int_val_lab (ivl, vls); - return 0; + do_add_val_lab (vls, value, label); + return true; } + else + return false; } -/* Sets LABEL as the value label for VALUE in VLS. Returns zero - if there wasn't already a value label for VALUE, or nonzero if - there was. Behavior is undefined if VLS's width is greater - than MAX_SHORT_STRING. */ -int -val_labs_replace (struct val_labs *vls, union value value, const char *label) +/* Sets LABEL as the value label for VALUE in VLS, replacing any + existing label for VALUE. */ +void +val_labs_replace (struct val_labs *vls, const union value *value, + const char *label) { - struct int_val_lab *ivl; - - assert (vls != NULL); - assert (vls->width <= MAX_SHORT_STRING); - assert (label != NULL); - - if (vls->labels == NULL) - { - val_labs_add (vls, value, label); - return 0; - } - - ivl = hsh_replace (vls->labels, create_int_val_lab (vls, value, label)); - if (ivl == NULL) - return 0; - else + struct val_lab *vl = (struct val_lab *) val_labs_lookup (vls, value); + if (vl != NULL) { - free_int_val_lab (ivl, vls); - return 1; + atom_destroy (vl->label); + vl->label = atom_create (label); } + else + do_add_val_lab (vls, value, label); } -/* Removes any value label for VALUE within VLS. Returns nonzero - if a value label was removed. Behavior is undefined if VLS's - width is greater than MAX_SHORT_STRING. */ -int -val_labs_remove (struct val_labs *vls, union value value) +/* Removes LABEL from VLS. */ +void +val_labs_remove (struct val_labs *vls, const struct val_lab *label_) { - assert (vls != NULL); - assert (vls->width <= MAX_SHORT_STRING); - - if (vls->labels != NULL) - { - struct int_val_lab *ivl = create_int_val_lab (vls, value, ""); - int deleted = hsh_delete (vls->labels, ivl); - free (ivl); - return deleted; - } - else - return 0; + struct val_lab *label = (struct val_lab *) label_; + hmap_delete (&vls->labels, &label->node); + value_destroy (&label->value, vls->width); + atom_destroy (label->label); + free (label); } /* Searches VLS for a value label for VALUE. If successful, - returns the label; otherwise, returns a null pointer. If - VLS's width is greater than MAX_SHORT_STRING, always returns a - null pointer. */ -char * -val_labs_find (const struct val_labs *vls, union value value) + returns the string used as the label; otherwise, returns a + null pointer. Returns a null pointer if VLS is null. */ +const char * +val_labs_find (const struct val_labs *vls, const union value *value) { - assert (vls != NULL); - - if (vls->width > MAX_SHORT_STRING) - return NULL; + const struct val_lab *label = val_labs_lookup (vls, value); + return label ? atom_to_string (label->label) : NULL; +} - if (vls->labels != NULL) +/* Searches VLS for a value label for VALUE. If successful, + returns the value label; otherwise, returns a null pointer. + Returns a null pointer if VLS is null. */ +const struct val_lab * +val_labs_lookup (const struct val_labs *vls, const union value *value) +{ + if (vls != NULL) { - struct int_val_lab ivl, *vlp; - - ivl.value = value; - vlp = hsh_find (vls->labels, &ivl); - if (vlp != NULL) - return atom_to_string (vlp->label); + struct val_lab *label; + HMAP_FOR_EACH_WITH_HASH (label, struct val_lab, node, + value_hash (value, vls->width, 0), &vls->labels) + if (value_equal (&label->value, value, vls->width)) + return label; } return NULL; } -/* A value labels iterator. */ -struct val_labs_iterator - { - void **labels; /* The labels, in order. */ - void **lp; /* Current label. */ - struct val_lab vl; /* Structure presented to caller. */ - }; - -/* Sets up *IP for iterating through the value labels in VLS in - no particular order. Returns the first value label or a null - pointer if VLS is empty. If the return value is non-null, - then val_labs_next() may be used to continue iterating or - val_labs_done() to free up the iterator. Otherwise, neither - function may be called for *IP. */ -struct val_lab * -val_labs_first (const struct val_labs *vls, struct val_labs_iterator **ip) +/* Returns the first value label in VLS, in arbitrary order, or a + null pointer if VLS is empty or if VLS is a null pointer. If + the return value is non-null, then val_labs_next() may be used + to continue iterating. */ +const struct val_lab * +val_labs_first (const struct val_labs *vls) { - struct val_labs_iterator *i; - - assert (vls != NULL); - assert (ip != NULL); - - if (vls->labels == NULL || vls->width > MAX_SHORT_STRING) - return NULL; - - i = *ip = xmalloc (sizeof *i); - i->labels = hsh_data_copy (vls->labels); - i->lp = i->labels; - return val_labs_next (vls, ip); -} - -/* Sets up *IP for iterating through the value labels in VLS in - sorted order of values. Returns the first value label or a - null pointer if VLS is empty. If the return value is - non-null, then val_labs_next() may be used to continue - iterating or val_labs_done() to free up the iterator. - Otherwise, neither function may be called for *IP. */ -struct val_lab * -val_labs_first_sorted (const struct val_labs *vls, - struct val_labs_iterator **ip) -{ - struct val_labs_iterator *i; - - assert (vls != NULL); - assert (ip != NULL); - - if (vls->labels == NULL || vls->width > MAX_SHORT_STRING) - return NULL; - - i = *ip = xmalloc (sizeof *i); - i->lp = i->labels = hsh_sort_copy (vls->labels); - return val_labs_next (vls, ip); + return vls ? HMAP_FIRST (struct val_lab, node, &vls->labels) : NULL; } /* Returns the next value label in an iteration begun by - val_labs_first() or val_labs_first_sorted(). If the return - value is non-null, then val_labs_next() may be used to - continue iterating or val_labs_done() to free up the iterator. - Otherwise, neither function may be called for *IP. */ -struct val_lab * -val_labs_next (const struct val_labs *vls, struct val_labs_iterator **ip) + val_labs_first(). If the return value is non-null, then + val_labs_next() may be used to continue iterating. */ +const struct val_lab * +val_labs_next (const struct val_labs *vls, const struct val_lab *label) { - struct val_labs_iterator *i; - struct int_val_lab *ivl; - - assert (vls != NULL); - assert (vls->width <= MAX_SHORT_STRING); - assert (ip != NULL); - assert (*ip != NULL); - - i = *ip; - ivl = *i->lp++; - if (ivl != NULL) - { - i->vl.value = ivl->value; - i->vl.label = atom_to_string (ivl->label); - return &i->vl; - } - else - { - free (i->labels); - free (i); - *ip = NULL; - return NULL; - } + return HMAP_NEXT (label, struct val_lab, node, &vls->labels); } -/* Discards the state for an incomplete iteration begun by - val_labs_first() or val_labs_first_sorted(). */ -void -val_labs_done (struct val_labs_iterator **ip) -{ - struct val_labs_iterator *i; - - assert (ip != NULL); - assert (*ip != NULL); - - i = *ip; - free (i->labels); - free (i); - *ip = NULL; -} - -/* Compares two value labels and returns a strcmp()-type result. */ -int -compare_int_val_lab (const void *a_, const void *b_, void *vls_) +static int +compare_labels_by_value_3way (const void *a_, const void *b_, const void *vls_) { - const struct int_val_lab *a = a_; - const struct int_val_lab *b = b_; + const struct val_lab *const *a = a_; + const struct val_lab *const *b = b_; const struct val_labs *vls = vls_; - - if (vls->width == 0) - return a->value.f < b->value.f ? -1 : a->value.f > b->value.f; - else - return memcmp (a->value.s, b->value.s, vls->width); + return value_compare_3way (&(*a)->value, &(*b)->value, vls->width); } -/* Hash a value label. */ -unsigned -hash_int_val_lab (const void *vl_, void *vls_) +/* Allocates and returns an array of pointers to value labels + that is sorted in increasing order by value. The array has + val_labs_count(VLS) elements. The caller is responsible for + freeing the array. */ +const struct val_lab ** +val_labs_sorted (const struct val_labs *vls) { - const struct int_val_lab *vl = vl_; - const struct val_labs *vls = vls_; - - if (vls->width == 0) - return hsh_hash_double (vl->value.f); + if (vls != NULL) + { + const struct val_lab *label; + const struct val_lab **labels; + size_t i; + + labels = xmalloc (val_labs_count (vls) * sizeof *labels); + i = 0; + HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels) + labels[i++] = label; + assert (i == val_labs_count (vls)); + sort (labels, val_labs_count (vls), sizeof *labels, + compare_labels_by_value_3way, vls); + return labels; + } else - return hsh_hash_bytes (vl->value.s, sizeof vl->value.s); -} - -/* Free a value label. */ -void -free_int_val_lab (void *vl_, void *vls_ UNUSED) -{ - struct int_val_lab *vl = vl_; - - atom_destroy (vl->label); - free (vl); + return NULL; } -/* Atoms. */ +/* Atoms: reference-counted constant strings. */ /* An atom. */ -struct atom +struct atom { + struct hmap_node node; /* Hash map node. */ char *string; /* String value. */ unsigned ref_count; /* Number of references. */ }; -static hsh_compare_func compare_atoms; -static hsh_hash_func hash_atom; -static hsh_free_func free_atom; - /* Hash table of atoms. */ -static struct hsh_table *atoms; +static struct hmap atoms = HMAP_INITIALIZER (atoms); + +static void free_atom (struct atom *atom); +static void free_all_atoms (void); /* Creates and returns an atom for STRING. */ static struct atom * -atom_create (const char *string) +atom_create (const char *string) { - struct atom a; - void **app; - + static bool initialized; + struct atom *atom; + size_t hash; + assert (string != NULL); - - if (atoms == NULL) - atoms = hsh_create (8, compare_atoms, hash_atom, free_atom, NULL); - a.string = (char *) string; - app = hsh_probe (atoms, &a); - if (*app != NULL) + if (!initialized) { - struct atom *ap = *app; - ap->ref_count++; - return ap; - } - else - { - struct atom *ap = xmalloc (sizeof *ap); - ap->string = xstrdup (string); - ap->ref_count = 1; - *app = ap; - return ap; + initialized = true; + atexit (free_all_atoms); } + + hash = hash_string (string, 0); + HMAP_FOR_EACH_WITH_HASH (atom, struct atom, node, hash, &atoms) + if (!strcmp (atom->string, string)) + { + atom->ref_count++; + return atom; + } + + atom = xmalloc (sizeof *atom); + atom->string = xstrdup (string); + atom->ref_count = 1; + hmap_insert (&atoms, &atom->node, hash); + return atom; } /* Destroys ATOM. */ -static void +static void atom_destroy (struct atom *atom) { - if (atom != NULL) + if (atom != NULL) { assert (atom->ref_count > 0); atom->ref_count--; - if (atom->ref_count == 0) - hsh_force_delete (atoms, atom); + if (atom->ref_count == 0) + { + hmap_delete (&atoms, &atom->node); + free_atom (atom); + } } } /* Returns the string associated with ATOM. */ -static char * -atom_to_string (const struct atom *atom) +static const char * +atom_to_string (const struct atom *atom) { - assert (atom != NULL); - return atom->string; } -/* A hsh_compare_func that compares A and B. */ -static int -compare_atoms (const void *a_, const void *b_, void *aux UNUSED) -{ - const struct atom *a = a_; - const struct atom *b = b_; - - return strcmp (a->string, b->string); -} - -/* A hsh_hash_func that hashes ATOM. */ -static unsigned -hash_atom (const void *atom_, void *aux UNUSED) -{ - const struct atom *atom = atom_; - - return hsh_hash_string (atom->string); -} - -/* A hsh_free_func that destroys ATOM. */ static void -free_atom (void *atom_, void *aux UNUSED) +free_atom (struct atom *atom) { - struct atom *atom = atom_; - free (atom->string); free (atom); } - -/* Get a string representing the value. - That is, if it has a label, then return that label, - otherwise, if the value is alpha, then return the string for it, - else format it and return the formatted string -*/ -const char * -value_to_string (const union value *val, const struct variable *var) +static void +free_all_atoms (void) { - char *s; - - assert (val != NULL); - assert (var != NULL); + struct atom *atom, *next; - s = val_labs_find (var->val_labs, *val); - if (s == NULL) - { - static char buf[256]; - if (var->width != 0) - str_copy_buf_trunc (buf, sizeof buf, val->s, var->width); - else - snprintf(buf, 100, "%g", val->f); - s = buf; - } - - return s; + HMAP_FOR_EACH_SAFE (atom, next, struct atom, node, &atoms) + free_atom (atom); }