cleanup
[pspp] / src / data / caseproto.c
index 1a40213a537d295f78241a0c35a0594ba3a6262a..358d59eb3d37cb01dd2a13dc358461c8172d44d6 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2009 Free Software Foundation, Inc.
+   Copyright (C) 2009, 2011 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 <config.h>
 
-#include <data/caseproto.h>
-#include <data/val-type.h>
-#include <data/value.h>
-#include <libpspp/array.h>
-#include <libpspp/assertion.h>
-#include <libpspp/pool.h>
+#include "data/caseproto.h"
 
-#include "minmax.h"
+#include "data/val-type.h"
+#include "data/value.h"
+#include "libpspp/array.h"
+#include "libpspp/assertion.h"
+#include "libpspp/pool.h"
+
+#include "gl/minmax.h"
 
 static struct caseproto *caseproto_unshare (struct caseproto *);
-static bool try_init_long_strings (const struct caseproto *,
+static bool try_init_strings (const struct caseproto *,
                                    size_t first, size_t last, union value[]);
-static void init_long_strings (const struct caseproto *,
+static void init_strings (const struct caseproto *,
                                size_t first, size_t last, union value[]);
-static void destroy_long_strings (const struct caseproto *,
+static void destroy_strings (const struct caseproto *,
                                   size_t first, size_t last, union value[]);
-static size_t count_long_strings (const struct caseproto *,
+static size_t count_strings (const struct caseproto *,
                                   size_t idx, size_t count);
 
-/* Returns the number of bytes to allocate for a struct caseproto
-   with room for N_WIDTHS elements in its widths[] array. */
-static inline size_t
-caseproto_size (size_t n_widths)
-{
-  return (offsetof (struct caseproto, widths)
-          + n_widths * sizeof (((struct caseproto *) NULL)->widths[0]));
-}
-
 /* Creates and returns a case prototype that initially has no
    widths. */
 struct caseproto *
 caseproto_create (void)
 {
-  enum { N_ALLOCATE = 4 };
-  struct caseproto *proto = xmalloc (caseproto_size (N_ALLOCATE));
-  proto->ref_cnt = 1;
-  proto->long_strings = NULL;
-  proto->n_long_strings = 0;
-  proto->n_widths = 0;
-  proto->allocated_widths = N_ALLOCATE;
+  struct caseproto *proto = xmalloc (sizeof *proto);
+  *proto = (struct caseproto) {
+    .ref_cnt = 1,
+  };
+  return proto;
+}
+
+struct caseproto * MALLOC_LIKE
+caseproto_from_widths (short int *widths, size_t n)
+{
+  struct caseproto *proto = xmalloc (sizeof *proto);
+  *proto = (struct caseproto) {
+    .ref_cnt = 1,
+    .n_widths = n,
+    .allocated_widths = n,
+    .widths = widths,
+  };
+  proto->n_strings = count_strings (proto, 0, n);
   return proto;
 }
 
@@ -76,49 +79,35 @@ caseproto_ref_pool (const struct caseproto *proto_, struct pool *pool)
   return proto;
 }
 
-/* Returns a replacement for PROTO that is unshared and has
-   enough room for at least N_WIDTHS widths before additional
-   memory is needed.  */
-struct caseproto *
-caseproto_reserve (struct caseproto *proto, size_t n_widths)
-{
-  proto = caseproto_unshare (proto);
-  if (n_widths > proto->allocated_widths)
-    {
-      proto->allocated_widths *= MAX (proto->allocated_widths * 2, n_widths);
-      proto = xrealloc (proto, caseproto_size (proto->allocated_widths));
-    }
-  return proto;
-}
-
 /* Returns a replacement for PROTO with WIDTH appended.  */
 struct caseproto *
 caseproto_add_width (struct caseproto *proto, int width)
 {
-  assert (width >= -1 && width <= MAX_STRING);
+  assert (width >= 0 && width <= MAX_STRING);
 
-  proto = caseproto_reserve (proto, proto->n_widths + 1);
+  proto = caseproto_unshare (proto);
+  if (proto->n_widths >= proto->allocated_widths)
+    proto->widths = x2nrealloc (proto->widths, &proto->allocated_widths,
+                                sizeof *proto->widths);
   proto->widths[proto->n_widths++] = width;
-  proto->n_long_strings += count_long_strings (proto, proto->n_widths - 1, 1);
+  if (width > 0)
+    proto->n_strings++;
 
   return proto;
 }
 
-/* Returns a replacement for PROTO with the width at index IDX
-   replaced by WIDTH.  IDX may be greater than the current number
-   of widths in PROTO, in which case any gap is filled in by
-   widths of -1. */
+/* Returns a replacement for PROTO with the width at index IDX replaced by
+   WIDTH.  */
 struct caseproto *
 caseproto_set_width (struct caseproto *proto, size_t idx, int width)
 {
-  assert (width >= -1 && width <= MAX_STRING);
+  assert (idx < proto->n_widths);
+  assert (width >= 0 && width <= MAX_STRING);
 
-  proto = caseproto_reserve (proto, idx + 1);
-  while (idx >= proto->n_widths)
-    proto->widths[proto->n_widths++] = -1;
-  proto->n_long_strings -= count_long_strings (proto, idx, 1);
+  proto = caseproto_unshare (proto);
+  proto->n_strings -= count_strings (proto, idx, 1);
   proto->widths[idx] = width;
-  proto->n_long_strings += count_long_strings (proto, idx, 1);
+  proto->n_strings += count_strings (proto, idx, 1);
 
   return proto;
 }
@@ -129,10 +118,14 @@ caseproto_set_width (struct caseproto *proto, size_t idx, int width)
 struct caseproto *
 caseproto_insert_width (struct caseproto *proto, size_t before, int width)
 {
+  assert (width >= 0 && width <= MAX_STRING);
   assert (before <= proto->n_widths);
 
-  proto = caseproto_reserve (proto, proto->n_widths + 1);
-  proto->n_long_strings += value_needs_init (width);
+  proto = caseproto_unshare (proto);
+  if (proto->n_widths >= proto->allocated_widths)
+    proto->widths = x2nrealloc (proto->widths, &proto->allocated_widths,
+                                sizeof *proto->widths);
+  proto->n_strings += value_needs_init (width);
   insert_element (proto->widths, proto->n_widths, sizeof *proto->widths,
                   before);
   proto->widths[before] = width;
@@ -141,35 +134,35 @@ caseproto_insert_width (struct caseproto *proto, size_t before, int width)
   return proto;
 }
 
-/* Returns a replacement for PROTO with CNT widths removed
+/* Returns a replacement for PROTO with N widths removed
    starting at index IDX. */
 struct caseproto *
-caseproto_remove_widths (struct caseproto *proto, size_t idx, size_t cnt)
+caseproto_remove_widths (struct caseproto *proto, size_t idx, size_t n)
 {
-  assert (caseproto_range_is_valid (proto, idx, cnt));
+  assert (caseproto_range_is_valid (proto, idx, n));
 
   proto = caseproto_unshare (proto);
-  proto->n_long_strings -= count_long_strings (proto, idx, cnt);
+  proto->n_strings -= count_strings (proto, idx, n);
   remove_range (proto->widths, proto->n_widths, sizeof *proto->widths,
-                idx, cnt);
-  proto->n_widths -= cnt;
+                idx, n);
+  proto->n_widths -= n;
   return proto;
 }
 
-/* Returns a replacement for PROTO in which the CNT widths
+/* Returns a replacement for PROTO in which the N widths
    starting at index OLD_WIDTH now start at index NEW_WIDTH, with
    other widths shifting out of the way to make room. */
 struct caseproto *
 caseproto_move_widths (struct caseproto *proto,
                        size_t old_start, size_t new_start,
-                       size_t cnt)
+                       size_t n)
 {
-  assert (caseproto_range_is_valid (proto, old_start, cnt));
-  assert (caseproto_range_is_valid (proto, new_start, cnt));
+  assert (caseproto_range_is_valid (proto, old_start, n));
+  assert (caseproto_range_is_valid (proto, new_start, n));
 
   proto = caseproto_unshare (proto);
   move_range (proto->widths, proto->n_widths, sizeof *proto->widths,
-              old_start, new_start, cnt);
+              old_start, new_start, n);
   return proto;
 }
 
@@ -206,9 +199,9 @@ caseproto_is_conformable (const struct caseproto *a, const struct caseproto *b)
    same as the N widths starting at B_START in B, false if any of
    the corresponding widths differ. */
 bool
-caseproto_equal (const struct caseproto *a, size_t a_start,
-                 const struct caseproto *b, size_t b_start,
-                 size_t n)
+caseproto_range_equal (const struct caseproto *a, size_t a_start,
+                       const struct caseproto *b, size_t b_start,
+                       size_t n)
 {
   size_t i;
 
@@ -220,6 +213,15 @@ caseproto_equal (const struct caseproto *a, size_t a_start,
   return true;
 }
 
+/* Returns true if A and B have the same widths, false otherwise. */
+bool
+caseproto_equal (const struct caseproto *a, const struct caseproto *b)
+{
+  return (a == b ? true
+          : a->n_widths != b->n_widths ? false
+          : caseproto_range_equal (a, 0, b, 0, a->n_widths));
+}
+
 /* Returns true if an array of values that is to be used for
    data of the format specified in PROTO needs to be initialized
    by calling caseproto_init_values, false if that step may be
@@ -230,7 +232,7 @@ caseproto_equal (const struct caseproto *a, size_t a_start,
 bool
 caseproto_needs_init_values (const struct caseproto *proto)
 {
-  return proto->n_long_strings > 0;
+  return proto->n_strings > 0;
 }
 
 /* Initializes the values in VALUES as required by PROTO, by
@@ -245,7 +247,7 @@ caseproto_needs_init_values (const struct caseproto *proto)
 void
 caseproto_init_values (const struct caseproto *proto, union value values[])
 {
-  init_long_strings (proto, 0, proto->n_long_strings, values);
+  init_strings (proto, 0, proto->n_strings, values);
 }
 
 /* Like caseproto_init_values, but returns false instead of
@@ -253,7 +255,7 @@ caseproto_init_values (const struct caseproto *proto, union value values[])
 bool
 caseproto_try_init_values (const struct caseproto *proto, union value values[])
 {
-  return try_init_long_strings (proto, 0, proto->n_long_strings, values);
+  return try_init_strings (proto, 0, proto->n_strings, values);
 }
 
 /* Initializes the data in VALUES that are in NEW but not in OLD,
@@ -272,15 +274,15 @@ void
 caseproto_reinit_values (const struct caseproto *old,
                          const struct caseproto *new, union value values[])
 {
-  size_t old_n_long = old->n_long_strings;
-  size_t new_n_long = new->n_long_strings;
+  size_t old_n_long = old->n_strings;
+  size_t new_n_long = new->n_strings;
 
   expensive_assert (caseproto_is_conformable (old, new));
 
   if (new_n_long > old_n_long)
-    init_long_strings (new, old_n_long, new_n_long, values);
+    init_strings (new, old_n_long, new_n_long, values);
   else if (new_n_long < old_n_long)
-    destroy_long_strings (old, new_n_long, old_n_long, values);
+    destroy_strings (old, new_n_long, old_n_long, values);
 }
 
 /* Frees the values in VALUES as required by PROTO, by calling
@@ -292,7 +294,7 @@ caseproto_reinit_values (const struct caseproto *old,
 void
 caseproto_destroy_values (const struct caseproto *proto, union value values[])
 {
-  destroy_long_strings (proto, 0, proto->n_long_strings, values);
+  destroy_strings (proto, 0, proto->n_strings, values);
 }
 
 /* Copies COUNT values, whose widths are given by widths in PROTO
@@ -313,62 +315,71 @@ caseproto_copy (const struct caseproto *proto, size_t idx, size_t count,
 void
 caseproto_free__ (struct caseproto *proto)
 {
-  free (proto->long_strings);
+  free (proto->strings);
+  free (proto->widths);
   free (proto);
 }
 
 void
-caseproto_refresh_long_string_cache__ (const struct caseproto *proto_)
+caseproto_refresh_string_cache__ (const struct caseproto *proto_)
 {
-  struct caseproto *proto = (struct caseproto *) proto_;
+  struct caseproto *proto = CONST_CAST (struct caseproto *, proto_);
   size_t n, i;
 
-  assert (proto->long_strings == NULL);
-  assert (proto->n_long_strings > 0);
+  assert (proto->strings == NULL);
+  assert (proto->n_strings > 0);
 
-  proto->long_strings = xmalloc (proto->n_long_strings
-                                 * sizeof *proto->long_strings);
+  proto->strings = xmalloc (proto->n_strings * sizeof *proto->strings);
   n = 0;
   for (i = 0; i < proto->n_widths; i++)
-    if (proto->widths[i] > MAX_SHORT_STRING)
-      proto->long_strings[n++] = i;
-  assert (n == proto->n_long_strings);
+    if (proto->widths[i] > 0)
+      proto->strings[n++] = i;
+  assert (n == proto->n_strings);
 }
 
+/* Returns a caseproto that can be modified without affecting the contents of
+   any caseproto shared with OLD.
+
+   The returned caseproto has no strings cache.  This is helpful because the
+   caller might be about to invalidate it. */
 static struct caseproto *
 caseproto_unshare (struct caseproto *old)
 {
-  struct caseproto *new;
-  if (old->ref_cnt > 1)
+  assert (old->ref_cnt > 0);
+  if (old->ref_cnt <= 1)
     {
-      new = xmemdup (old, caseproto_size (old->allocated_widths));
-      new->ref_cnt = 1;
-      --old->ref_cnt;
+      free (old->strings);
+      old->strings = NULL;
+      return old;
     }
-  else
-    {
-      new = old;
-      free (new->long_strings);
-    }
-  new->long_strings = NULL;
+
+  struct caseproto *new = xmalloc (sizeof *new);
+  *new = (struct caseproto) {
+    .ref_cnt = 1,
+    .n_strings = old->n_strings,
+    .n_widths = old->n_widths,
+    .allocated_widths = old->allocated_widths,
+    .widths = xmemdup (old->widths, old->allocated_widths * sizeof *old->widths),
+  };
+  --old->ref_cnt;
   return new;
 }
 
 static bool
-try_init_long_strings (const struct caseproto *proto,
+try_init_strings (const struct caseproto *proto,
                        size_t first, size_t last, union value values[])
 {
   size_t i;
 
-  if (last > 0 && proto->long_strings == NULL)
-    caseproto_refresh_long_string_cache__ (proto);
+  if (last > 0 && proto->strings == NULL)
+    caseproto_refresh_string_cache__ (proto);
 
   for (i = first; i < last; i++)
     {
-      size_t idx = proto->long_strings[i];
+      size_t idx = proto->strings[i];
       if (!value_try_init (&values[idx], proto->widths[idx]))
         {
-          destroy_long_strings (proto, first, i, values);
+          destroy_strings (proto, first, i, values);
           return false;
         }
     }
@@ -376,36 +387,36 @@ try_init_long_strings (const struct caseproto *proto,
 }
 
 static void
-init_long_strings (const struct caseproto *proto,
+init_strings (const struct caseproto *proto,
                    size_t first, size_t last, union value values[])
 {
-  if (!try_init_long_strings (proto, first, last, values))
+  if (!try_init_strings (proto, first, last, values))
     xalloc_die ();
 }
 
 static void
-destroy_long_strings (const struct caseproto *proto, size_t first, size_t last,
+destroy_strings (const struct caseproto *proto, size_t first, size_t last,
                       union value values[])
 {
   size_t i;
 
-  if (last > 0 && proto->long_strings == NULL)
-    caseproto_refresh_long_string_cache__ (proto);
+  if (last > 0 && proto->strings == NULL)
+    caseproto_refresh_string_cache__ (proto);
 
   for (i = first; i < last; i++)
     {
-      size_t idx = proto->long_strings[i];
+      size_t idx = proto->strings[i];
       value_destroy (&values[idx], proto->widths[idx]);
     }
 }
 
 static size_t
-count_long_strings (const struct caseproto *proto, size_t idx, size_t count)
+count_strings (const struct caseproto *proto, size_t idx, size_t count)
 {
   size_t n, i;
 
   n = 0;
   for (i = 0; i < count; i++)
-    n += proto->widths[idx + i] > MAX_SHORT_STRING;
+    n += proto->widths[idx + i] > 0;
   return n;
 }