pxd: initial work
[pspp] / src / data / value-labels.c
index 746e20908f8f8bdafa7fa8a6529e7d8450be333c..c1dee054ac8318d9c94dc1387b88e5af46aa9ad9 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2009, 2010, 2011 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
 #include "libpspp/hmap.h"
 #include "libpspp/intern.h"
 #include "libpspp/message.h"
+#include "libpspp/pxd.h"
 #include "libpspp/str.h"
 
 #include "gl/xalloc.h"
 
+static void val_labs_uncache__ (struct val_labs *);
+static void val_lab_uncache__ (struct val_lab *);
+
 /* Creates and returns a new, empty set of value labels with the
    given WIDTH. */
 struct val_labs *
@@ -88,7 +92,11 @@ val_labs_set_width (struct val_labs *vls, int new_width)
       HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels)
         value_resize (&label->value, vls->width, new_width);
     }
-  vls->width = new_width;
+  if (vls->width != new_width)
+    {
+      vls->width = new_width;
+      val_labs_uncache__ (vls);
+    }
 }
 
 /* Destroys VLS. */
@@ -97,12 +105,21 @@ val_labs_destroy (struct val_labs *vls)
 {
   if (vls != NULL)
     {
+      val_labs_uncache__ (vls);
       val_labs_clear (vls);
       hmap_destroy (&vls->labels);
       free (vls);
     }
 }
 
+static void
+val_lab_clear (struct val_lab *lab, int width)
+{
+  val_lab_uncache__ (lab);
+  value_destroy (&lab->value, width);
+  intern_unref (lab->label);
+}
+
 /* Removes all the value labels from VLS. */
 void
 val_labs_clear (struct val_labs *vls)
@@ -111,10 +128,8 @@ val_labs_clear (struct val_labs *vls)
 
   HMAP_FOR_EACH_SAFE (label, next, struct val_lab, node, &vls->labels)
     {
+      val_lab_clear (label, vls->width);
       hmap_delete (&vls->labels, &label->node);
-      value_destroy (&label->value, vls->width);
-      intern_unref (label->label);
-      intern_unref (label->escaped_label);
       free (label);
     }
 }
@@ -162,6 +177,86 @@ set_label (struct val_lab *lab, const char *escaped_label)
     }
 }
 
+static void
+val_labs_uncache__ (struct val_labs *vls UNUSED)
+{
+}
+
+static void
+val_lab_uncache__ (struct val_lab *vl UNUSED)
+{
+}
+
+static struct pxd_object *
+val_lab_save (const struct val_lab *vl, int width, struct pxd *pxd)
+{
+  struct pxd_builder b;
+
+  pxd_builder_init (&b, pxd);
+
+  pxd_builder_put_value (&b, &vl->value, width);
+  pxd_builder_put_interned_string (&b, vl->label);
+
+  return pxd_builder_commit (&b);
+}
+
+struct pxd_object *
+val_labs_save (const struct val_labs *vls, struct pxd *pxd)
+{
+  struct val_lab *label;
+  struct pxd_builder b;
+
+  pxd_builder_init (&b, pxd);
+
+  pxd_builder_put_u32 (&b, vls->width);
+  HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels)
+    pxd_builder_put_link (&b, val_lab_save (label, vls->width, pxd));
+
+  return pxd_builder_commit (&b);
+}
+
+static struct val_lab *
+val_lab_load (struct pxd_object *object, const struct pxd *pxd, int width)
+{
+  struct pxd_parser p;
+  struct val_lab *vl;
+
+  pxd_parser_init (&p, object, pxd);
+
+  vl = xmalloc (sizeof *vl);
+  pxd_parser_get_value (&p, &vl->value, width);
+  vl->label = pxd_parser_get_interned_string (&p);
+
+  pxd_parser_destroy (&p);
+
+  return vl;
+}
+
+struct val_labs *
+val_labs_load (struct pxd_object *object, const struct pxd *pxd)
+{
+  struct val_labs *vls;
+  struct pxd_parser p;
+  unsigned int i;
+  int width;
+
+  pxd_parser_init (&p, object, pxd);
+  width = pxd_parser_get_u32 (&p);
+
+  vls = val_labs_create (width);
+  for (i = 0; i < pxd_object_get_n_links (object); i++)
+    {
+      struct pxd_object *lab_obj = pxd_object_get_link (object, i, pxd);
+      struct val_lab *lab = val_lab_load (lab_obj, pxd, width);
+      hmap_insert (&vls->labels, &lab->node,
+                   value_hash (&lab->value, width, 0));
+    }
+
+  pxd_parser_destroy (&p);
+
+  return vls;
+}
+\f
 static void
 do_add_val_lab (struct val_labs *vls, const union value *value,
                 const char *escaped_label)
@@ -201,6 +296,7 @@ val_labs_replace (struct val_labs *vls, const union value *value,
   struct val_lab *vl = val_labs_lookup (vls, value);
   if (vl != NULL)
     {
+      val_lab_uncache__ (vl);
       intern_unref (vl->label);
       intern_unref (vl->escaped_label);
       set_label (vl, label);
@@ -213,10 +309,8 @@ val_labs_replace (struct val_labs *vls, const union value *value,
 void
 val_labs_remove (struct val_labs *vls, struct val_lab *label)
 {
+  val_lab_clear (label, vls->width);
   hmap_delete (&vls->labels, &label->node);
-  value_destroy (&label->value, vls->width);
-  intern_unref (label->label);
-  intern_unref (label->escaped_label);
   free (label);
 }
 
@@ -256,6 +350,35 @@ 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)
+    {
+      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 value;
+}
 \f
 /* 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
@@ -333,7 +456,13 @@ val_labs_equal (const struct val_labs *a, const struct val_labs *b)
 {
   const struct val_lab *label;
 
-  if (val_labs_count (a) != val_labs_count (b) || a->width != b->width)
+  if (val_labs_count (a) != val_labs_count (b))
+    return false;
+  
+  if (a == NULL || b == NULL)
+    return true;
+
+  if (a->width != b->width)
     return false;
 
   HMAP_FOR_EACH (label, struct val_lab, node, &a->labels)