Implement missing values for long string variables.
[pspp-builds.git] / src / data / missing-values.c
index 867e0f719afc4bffc605fd45b895c759f3007d02..c1a7469158ead5204e6da7fa5a4432ad36fc3bbb 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2005 Free Software Foundation, Inc.
+   Copyright (C) 2005, 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
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <config.h>
-#include "missing-values.h"
+#include <data/missing-values.h>
 #include <assert.h>
 #include <stdlib.h>
-#include <libpspp/array.h>
+#include <data/variable.h>
 #include <libpspp/assertion.h>
-#include "variable.h"
 #include <libpspp/str.h>
 
 /* Types of user-missing values.
@@ -36,15 +35,47 @@ enum mv_type
   };
 
 /* Initializes MV as a set of missing values for a variable of
-   the given WIDTH.  Although only numeric variables and short
-   string variables may have missing values, WIDTH may be any
-   valid variable width. */
+   the given WIDTH.  MV should be destroyed with mv_destroy when
+   it is no longer needed. */
 void
 mv_init (struct missing_values *mv, int width)
 {
+  int i;
+
+  assert (width >= 0 && width <= MAX_STRING);
+  mv->type = MVT_NONE;
+  mv->width = width;
+  for (i = 0; i < 3; i++)
+    value_init (&mv->values[i], width);
+}
+
+/* Initializes MV as a set of missing values for a variable of
+   the given WIDTH.  MV will be automatically destroyed along
+   with POOL; it must not be passed to mv_destroy for explicit
+   destruction. */
+void
+mv_init_pool (struct pool *pool, struct missing_values *mv, int width)
+{
+  int i;
+
   assert (width >= 0 && width <= MAX_STRING);
   mv->type = MVT_NONE;
   mv->width = width;
+  for (i = 0; i < 3; i++)
+    value_init_pool (pool, &mv->values[i], width);
+}
+
+/* Frees any storage allocated by mv_init for MV. */
+void
+mv_destroy (struct missing_values *mv)
+{
+  if (mv != NULL)
+    {
+      int i;
+
+      for (i = 0; i < 3; i++)
+        value_destroy (&mv->values[i], mv->width);
+    }
 }
 
 /* Removes any missing values from MV. */
@@ -54,13 +85,32 @@ mv_clear (struct missing_values *mv)
   mv->type = MVT_NONE;
 }
 
-/* Copies SRC to MV. */
+/* Initializes MV as a copy of SRC. */
 void
 mv_copy (struct missing_values *mv, const struct missing_values *src)
 {
-  assert(src);
+  int i;
+
+  mv_init (mv, src->width);
+  mv->type = src->type;
+  for (i = 0; i < 3; i++)
+    value_copy (&mv->values[i], &src->values[i], mv->width);
+}
+
+/* Returns true if VALUE, of the given WIDTH, may be added to a
+   missing value set also of the given WIDTH.  This is normally
+   the case, but string missing values over MV_MAX_STRING bytes
+   long must consist solely of spaces after the first
+   MV_MAX_STRING bytes.  */
+bool
+mv_is_acceptable (const union value *value, int width)
+{
+  int i;
 
-  *mv = *src;
+  for (i = MV_MAX_STRING; i < width; i++)
+    if (value_str (value, width)[i] != ' ')
+      return false;
+  return true;
 }
 
 /* Returns true if MV is an empty set of missing values. */
@@ -80,20 +130,21 @@ mv_get_width (const struct missing_values *mv)
 
 /* Attempts to add individual value V to the set of missing
    values MV.  Returns true if successful, false if MV has no
-   more room for missing values.  (Long string variables never
-   accept missing values.) */
+   more room for missing values or if V is not an acceptable
+   missing value. */
 bool
 mv_add_value (struct missing_values *mv, const union value *v)
 {
-  if (mv->width > MAX_SHORT_STRING)
+  if (!mv_is_acceptable (v, mv->width))
     return false;
+
   switch (mv->type)
     {
     case MVT_NONE:
     case MVT_1:
     case MVT_2:
     case MVT_RANGE:
-      mv->values[mv->type & 3] = *v;
+      value_copy (&mv->values[mv->type & 3], v, mv->width);
       mv->type++;
       return true;
 
@@ -107,13 +158,20 @@ mv_add_value (struct missing_values *mv, const union value *v)
 /* Attempts to add S to the set of string missing values MV.  S
    must contain exactly as many characters as MV's width.
    Returns true if successful, false if MV has no more room for
-   missing values.  (Long string variables never accept missing
-   values.) */
+   missing values or if S is not an acceptable missing value. */
 bool
 mv_add_str (struct missing_values *mv, const char s[])
 {
+  union value v;
+  bool ok;
+
   assert (mv->width > 0);
-  return mv_add_value (mv, (union value *) s);
+  value_init (&v, mv->width);
+  memcpy (value_str_rw (&v, mv->width), s, mv->width);
+  ok = mv_add_value (mv, &v);
+  value_destroy (&v, mv->width);
+
+  return ok;
 }
 
 /* Attempts to add D to the set of numeric missing values MV.
@@ -122,8 +180,16 @@ mv_add_str (struct missing_values *mv, const char s[])
 bool
 mv_add_num (struct missing_values *mv, double d)
 {
+  union value v;
+  bool ok;
+
   assert (mv->width == 0);
-  return mv_add_value (mv, (union value *) &d);
+  value_init (&v, 0);
+  v.f = d;
+  ok = mv_add_value (mv, &v);
+  value_destroy (&v, 0);
+
+  return ok;
 }
 
 /* Attempts to add range [LOW, HIGH] to the set of numeric
@@ -152,7 +218,8 @@ mv_has_value (const struct missing_values *mv)
   return mv_n_values (mv) > 0;
 }
 
-/* Removes one individual value from MV and stores it in *V.
+/* Removes one individual value from MV and stores it in V, which
+   must have been initialized as a value with the same width as MV.
    MV must contain an individual value (as determined by
    mv_has_value()).
 
@@ -165,32 +232,46 @@ mv_has_value (const struct missing_values *mv)
 void
 mv_pop_value (struct missing_values *mv, union value *v)
 {
+  union value tmp;
+
   assert (mv_has_value (mv));
 
-  *v = mv->values[0];
-  remove_element (mv->values, mv->type & 3, sizeof *mv->values, 0);
+  value_copy (v, &mv->values[0], mv->width);
+  tmp = mv->values[0];
+  mv->values[0] = mv->values[1];
+  mv->values[1] = mv->values[2];
+  mv->values[2] = tmp;
   mv->type--;
 }
 
-/* Stores MV's value with index IDX in *V.
+/* Returns MV's discrete value with index IDX.  The caller must
+   not modify or free this value, or access it after MV is
+   modified or freed.
    IDX must be less than the number of discrete values in MV, as
-   reported by mv_n_values(MV). */
-void
-mv_get_value (const struct missing_values *mv, union value *v, int idx)
+   reported by mv_n_values. */
+const union value *
+mv_get_value (const struct missing_values *mv, int idx)
 {
   assert (idx >= 0 && idx < mv_n_values (mv));
-  *v = mv->values[idx];
+  return &mv->values[idx];
 }
 
-void
+/* Replaces MV's discrete value with index IDX by a copy of V,
+   which must have the same width as MV.
+   IDX must be less than the number of discrete values in MV, as
+   reported by mv_n_values. */
+bool
 mv_replace_value (struct missing_values *mv, const union value *v, int idx)
 {
   assert (idx >= 0) ;
   assert (idx < mv_n_values(mv));
 
-  mv->values[idx] = *v;
-}
+  if (!mv_is_acceptable (v, mv->width))
+    return false;
 
+  value_copy (&mv->values[idx], v, mv->width);
+  return true;
+}
 
 /* Returns the number of individual (not part of a range) missing
    values in MV. */
@@ -232,7 +313,6 @@ mv_get_range (const struct missing_values *mv, double *low, double *high)
   *high = mv->values[2].f;
 }
 
-
 /* Returns true if values[IDX] is in use when the `type' member
    is set to TYPE (in struct missing_values),
    false otherwise. */
@@ -262,17 +342,12 @@ using_element (unsigned type, int idx)
 /* Returns true if MV can be resized to the given WIDTH with
    mv_resize(), false otherwise.  Resizing is possible only when
    each value in MV (if any) is resizable from MV's current width
-   to WIDTH, as determined by value_is_resizable.  In addition,
-   resizing must not produce a non-empty set of long string
-   missing values. */
+   to WIDTH, as determined by value_is_resizable. */
 bool
 mv_is_resizable (const struct missing_values *mv, int width)
 {
   int i;
 
-  if (width > MAX_SHORT_STRING && mv->type != MVT_NONE)
-    return false;
-
   for (i = 0; i < 3; i++)
     if (using_element (mv->type, i)
         && !value_is_resizable (&mv->values[i], mv->width, width))
@@ -292,6 +367,11 @@ mv_resize (struct missing_values *mv, int width)
   for (i = 0; i < 3; i++)
     if (using_element (mv->type, i))
       value_resize (&mv->values[i], mv->width, width);
+    else
+      {
+        value_destroy (&mv->values[i], mv->width);
+        value_init (&mv->values[i], width);
+      }
   mv->width = width;
 }
 
@@ -333,14 +413,14 @@ is_str_user_missing (const struct missing_values *mv, const char s[])
     case MVT_NONE:
       return false;
     case MVT_1:
-      return !memcmp (v[0].short_string, s, mv->width);
+      return !memcmp (value_str (&v[0], mv->width), s, mv->width);
     case MVT_2:
-      return (!memcmp (v[0].short_string, s, mv->width)
-              || !memcmp (v[1].short_string, s, mv->width));
+      return (!memcmp (value_str (&v[0], mv->width), s, mv->width)
+              || !memcmp (value_str (&v[1], mv->width), s, mv->width));
     case MVT_3:
-      return (!memcmp (v[0].short_string, s, mv->width)
-              || !memcmp (v[1].short_string, s, mv->width)
-              || !memcmp (v[2].short_string, s, mv->width));
+      return (!memcmp (value_str (&v[0], mv->width), s, mv->width)
+              || !memcmp (value_str (&v[1], mv->width), s, mv->width)
+              || !memcmp (value_str (&v[2], mv->width), s, mv->width));
     case MVT_RANGE:
     case MVT_RANGE_1:
       NOT_REACHED ();
@@ -356,7 +436,7 @@ mv_is_value_missing (const struct missing_values *mv, const union value *v,
 {
   return (mv->width == 0
           ? mv_is_num_missing (mv, v->f, class)
-          : mv_is_str_missing (mv, v->short_string, class));
+          : mv_is_str_missing (mv, value_str (v, mv->width), class));
 }
 
 /* Returns true if D is a missing value in the given CLASS in MV,