X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fdata%2Fvalue-labels.c;h=1080cdff1d82d207aecd0577cc16ad685c25d1ed;hb=b7479c9a558c9d46c5685724b0a67f8ba2e7956c;hp=c8061f7bd5d7fee54c47f5e65aa739b442f10dce;hpb=3bbb4370239deb29ebbf813d258aef6249e2a431;p=pspp diff --git a/src/data/value-labels.c b/src/data/value-labels.c index c8061f7bd5..1080cdff1d 100644 --- a/src/data/value-labels.c +++ b/src/data/value-labels.c @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2009, 2010, 2011, 2012 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 @@ -16,33 +16,23 @@ #include -#include "value-labels.h" +#include "data/value-labels.h" #include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "data/data-out.h" +#include "data/value.h" +#include "data/variable.h" +#include "libpspp/array.h" +#include "libpspp/cast.h" +#include "libpspp/compiler.h" +#include "libpspp/hash-functions.h" +#include "libpspp/hmap.h" +#include "libpspp/intern.h" +#include "libpspp/message.h" +#include "libpspp/str.h" -#include "xalloc.h" - -static struct atom *atom_create (const char *string); -static void atom_destroy (struct atom *); -static const char *atom_to_string (const struct atom *); - -/* 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); -} +#include "gl/xalloc.h" /* Creates and returns a new, empty set of value labels with the given WIDTH. */ @@ -68,7 +58,7 @@ val_labs_clone (const struct val_labs *vls) copy = val_labs_create (vls->width); HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels) - val_labs_add (copy, &label->value, atom_to_string (label->label)); + val_labs_add (copy, &label->value, label->escaped_label); return copy; } @@ -123,11 +113,19 @@ val_labs_clear (struct val_labs *vls) { hmap_delete (&vls->labels, &label->node); value_destroy (&label->value, vls->width); - atom_destroy (label->label); + intern_unref (label->label); + intern_unref (label->escaped_label); free (label); } } +/* Returns the width of VLS. */ +int +val_labs_get_width (const struct val_labs *vls) +{ + return vls->width; +} + /* Returns the number of value labels in VLS. Returns 0 if VLS is null. */ size_t @@ -136,19 +134,48 @@ val_labs_count (const struct val_labs *vls) return vls == NULL ? 0 : hmap_count (&vls->labels); } +static void +set_label (struct val_lab *lab, const char *escaped_label) +{ + lab->escaped_label = intern_new (escaped_label); + if (strstr (escaped_label, "\\n") == NULL) + lab->label = intern_ref (lab->escaped_label); + else + { + struct string s; + const char *p; + + ds_init_empty (&s); + ds_extend (&s, intern_strlen (lab->escaped_label)); + for (p = escaped_label; *p != '\0'; p++) + { + char c = *p; + if (c == '\\' && p[1] == 'n') + { + c = '\n'; + p++; + } + ds_put_byte (&s, c); + } + lab->label = intern_new (ds_cstr (&s)); + ds_destroy (&s); + } +} + static void do_add_val_lab (struct val_labs *vls, const union value *value, - const char *label) + const char *escaped_label) { 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); + value_clone (&lab->value, value, vls->width); + set_label (lab, escaped_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 true. Otherwise, returns false. */ +/* If VLS does not already contain a value label for VALUE, adds the UTF-8 + encoded LABEL for it and returns true. Otherwise, returns false. + + In LABEL, the two-byte sequence "\\n" is interpreted as a new-line. */ bool val_labs_add (struct val_labs *vls, const union value *value, const char *label) @@ -164,16 +191,19 @@ val_labs_add (struct val_labs *vls, const union value *value, } /* Sets LABEL as the value label for VALUE in VLS, replacing any - existing label for VALUE. */ + existing label for VALUE. + + In LABEL, the two-byte sequence "\\n" is interpreted as a new-line. */ void val_labs_replace (struct val_labs *vls, const union value *value, const char *label) { - struct val_lab *vl = (struct val_lab *) val_labs_lookup (vls, value); + struct val_lab *vl = val_labs_lookup (vls, value); if (vl != NULL) { - atom_destroy (vl->label); - vl->label = atom_create (label); + intern_unref (vl->label); + intern_unref (vl->escaped_label); + set_label (vl, label); } else do_add_val_lab (vls, value, label); @@ -181,40 +211,79 @@ val_labs_replace (struct val_labs *vls, const union value *value, /* Removes LABEL from VLS. */ void -val_labs_remove (struct val_labs *vls, const struct val_lab *label_) +val_labs_remove (struct val_labs *vls, struct val_lab *label) { - struct val_lab *label = (struct val_lab *) label_; hmap_delete (&vls->labels, &label->node); value_destroy (&label->value, vls->width); - atom_destroy (label->label); + intern_unref (label->label); + intern_unref (label->escaped_label); free (label); } -/* Searches VLS for a value label for VALUE. If successful, - returns the string used as the label; otherwise, returns a - null pointer. Returns a null pointer if VLS is null. */ +/* Searches VLS for a value label for VALUE. If successful, returns the string + used as the label, as a UTF-8 encoded string in a format suitable for + output. 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) { const struct val_lab *label = val_labs_lookup (vls, value); - return label ? atom_to_string (label->label) : NULL; + return label ? label->label : 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 * +static struct val_lab * +val_labs_lookup__ (const struct val_labs *vls, const union value *value, + unsigned int hash) +{ + struct val_lab *label; + + HMAP_FOR_EACH_WITH_HASH (label, struct val_lab, node, hash, &vls->labels) + if (value_equal (&label->value, value, vls->width)) + return label; + + return 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. */ +struct val_lab * val_labs_lookup (const struct val_labs *vls, const union value *value) { + return (vls == NULL ? NULL + : val_labs_lookup__ (vls, value, value_hash (value, vls->width, 0))); +} + +/* Searches VLS for a value label whose label is exactly LABEL. If successful, + returns the corresponding value. Otherwise, returns a null pointer. + + Returns a null pointer if VLS is null. + + This function is O(n) in the number of labels in VLS. */ +const union value * +val_labs_find_value (const struct val_labs *vls, const char *label_) +{ + const union value *value = NULL; + if (vls != NULL) { - 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; + const struct val_lab *vl; + const char *label; + + label = intern_new (label_); + HMAP_FOR_EACH (vl, struct val_lab, node, &vls->labels) + if (vl->label == label) + { + value = &vl->value; + break; + } + intern_unref (label); } - return NULL; + + return value; } /* Returns the first value label in VLS, in arbitrary order, or a @@ -270,89 +339,45 @@ val_labs_sorted (const struct val_labs *vls) else return NULL; } - -/* Atoms: reference-counted constant strings. */ - -/* An atom. */ -struct atom - { - struct hmap_node node; /* Hash map node. */ - char *string; /* String value. */ - unsigned ref_count; /* Number of references. */ - }; -/* Hash table of atoms. */ -static struct hmap atoms = HMAP_INITIALIZER (atoms); +/* Returns a hash value that represents all of the labels in VLS, starting from + BASIS. */ +unsigned int +val_labs_hash (const struct val_labs *vls, unsigned int basis) +{ + const struct val_lab *label; + unsigned int hash; -static void free_atom (struct atom *atom); -static void free_all_atoms (void); + hash = hash_int (val_labs_count (vls), basis); + HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels) + hash ^= value_hash (&label->value, vls->width, + hash_string (label->label, basis)); + return hash; +} -/* Creates and returns an atom for STRING. */ -static struct atom * -atom_create (const char *string) +/* Returns true if A and B contain the same values with the same labels, + false if they differ in some way. */ +bool +val_labs_equal (const struct val_labs *a, const struct val_labs *b) { - static bool initialized; - struct atom *atom; - size_t hash; + const struct val_lab *label; - assert (string != NULL); + if (val_labs_count (a) != val_labs_count (b)) + return false; - if (!initialized) - { - initialized = true; - atexit (free_all_atoms); - } + if (a == NULL || b == NULL) + return true; - 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; -} + if (a->width != b->width) + return false; -/* Destroys ATOM. */ -static void -atom_destroy (struct atom *atom) -{ - if (atom != NULL) + HMAP_FOR_EACH (label, struct val_lab, node, &a->labels) { - assert (atom->ref_count > 0); - atom->ref_count--; - if (atom->ref_count == 0) - { - hmap_delete (&atoms, &atom->node); - free_atom (atom); - } + struct val_lab *label2 = val_labs_lookup__ (b, &label->value, + label->node.hash); + if (!label2 || label->label != label2->label) + return false; } -} - -/* Returns the string associated with ATOM. */ -static const char * -atom_to_string (const struct atom *atom) -{ - return atom->string; -} -static void -free_atom (struct atom *atom) -{ - free (atom->string); - free (atom); -} - -static void -free_all_atoms (void) -{ - struct atom *atom, *next; - - HMAP_FOR_EACH_SAFE (atom, next, struct atom, node, &atoms) - free_atom (atom); + return true; }