pivot-table: Reduce size of struct pivot_value from 96 bytes to 80.
[pspp] / src / output / table.c
index b1707a84d875dfb633f87c8736ad70eb507fa0ca..bf22d2da5af8809de3f2b5d3e7b51e8d6aab70c0 100644 (file)
 #include "libpspp/compiler.h"
 #include "libpspp/pool.h"
 #include "libpspp/str.h"
-#include "output/table-item.h"
+#include "output/output-item.h"
+#include "output/pivot-table.h"
 #include "output/table.h"
-#include "output/text-item.h"
 
 #include "gl/xalloc.h"
 
+/* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
+#define H TABLE_HORZ
+#define V TABLE_VERT
+
 /* Increases TABLE's reference count, indicating that it has an additional
    owner.  An table that is shared among multiple owners must not be
    modified. */
@@ -67,10 +71,10 @@ table_is_shared (const struct table *table)
   return table->ref_cnt > 1;
 }
 \f
-struct area_style *
-area_style_clone (struct pool *pool, const struct area_style *old)
+struct table_area_style *
+table_area_style_clone (struct pool *pool, const struct table_area_style *old)
 {
-  struct area_style *new = pool_malloc (pool, sizeof *new);
+  struct table_area_style *new = pool_malloc (pool, sizeof *new);
   *new = *old;
   if (new->font_style.typeface)
     new->font_style.typeface = pool_strdup (pool, new->font_style.typeface);
@@ -78,7 +82,7 @@ area_style_clone (struct pool *pool, const struct area_style *old)
 }
 
 void
-area_style_free (struct area_style *style)
+table_area_style_free (struct table_area_style *style)
 {
   if (style)
     {
@@ -86,105 +90,6 @@ area_style_free (struct area_style *style)
       free (style);
     }
 }
-
-void
-table_cell_format_footnote_markers (const struct table_cell *cell,
-                                    struct string *s)
-{
-  for (size_t i = 0; i < cell->n_footnotes; i++)
-    {
-      if (i)
-        ds_put_byte (s, ',');
-      ds_put_cstr (s, cell->footnotes[i]->marker);
-    }
-}
-
-static const struct footnote **
-add_footnotes (const struct footnote **refs, size_t n_refs,
-               const struct footnote **footnotes, size_t *allocated, size_t *n)
-{
-  for (size_t i = 0; i < n_refs; i++)
-    {
-      const struct footnote *f = refs[i];
-      if (f->idx >= *allocated)
-        {
-          size_t new_allocated = (f->idx + 1) * 2;
-          footnotes = xrealloc (footnotes, new_allocated * sizeof *footnotes);
-          while (*allocated < new_allocated)
-            footnotes[(*allocated)++] = NULL;
-        }
-      footnotes[f->idx] = f;
-      if (f->idx >= *n)
-        *n = f->idx + 1;
-    }
-  return footnotes;
-}
-
-size_t
-table_collect_footnotes (const struct table_item *item,
-                         const struct footnote ***footnotesp)
-{
-  const struct footnote **footnotes = NULL;
-  size_t allocated = 0;
-  size_t n = 0;
-
-  struct table *t = item->table;
-  for (int y = 0; y < table_nr (t); y++)
-    {
-      struct table_cell cell;
-      for (int x = 0; x < table_nc (t); x = cell.d[TABLE_HORZ][1])
-        {
-          table_get_cell (t, x, y, &cell);
-
-          if (x == cell.d[TABLE_HORZ][0] && y == cell.d[TABLE_VERT][0])
-            footnotes = add_footnotes (cell.footnotes, cell.n_footnotes,
-                                       footnotes, &allocated, &n);
-        }
-    }
-
-  const struct table_item_text *title = table_item_get_title (item);
-  if (title)
-    footnotes = add_footnotes (title->footnotes, title->n_footnotes,
-                               footnotes, &allocated, &n);
-
-  const struct table_item_layers *layers = table_item_get_layers (item);
-  if (layers)
-    {
-      for (size_t i = 0; i < layers->n_layers; i++)
-        footnotes = add_footnotes (layers->layers[i].footnotes,
-                                   layers->layers[i].n_footnotes,
-                                   footnotes, &allocated, &n);
-    }
-
-  const struct table_item_text *caption = table_item_get_caption (item);
-  if (caption)
-    footnotes = add_footnotes (caption->footnotes, caption->n_footnotes,
-                               footnotes, &allocated, &n);
-
-  size_t n_nonnull = 0;
-  for (size_t i = 0; i < n; i++)
-    if (footnotes[i])
-      footnotes[n_nonnull++] = footnotes[i];
-
-  *footnotesp = footnotes;
-  return n_nonnull;
-}
-\f
-/* Returns a table that contains a single cell, whose contents are the
-   left-aligned TEXT.  */
-struct table *
-table_from_string (const char *text)
-{
-  struct table *t = table_create (1, 1, 0, 0, 0, 0);
-  t->styles[0] = xmalloc (sizeof *t->styles[0]);
-  *t->styles[0] = (struct area_style) {
-    AREA_STYLE_INITIALIZER__,
-    .cell_style.halign = TABLE_HALIGN_LEFT,
-    .cell_style.valign = TABLE_VALIGN_TOP
-  };
-  table_text (t, 0, 0, 0 << TAB_STYLE_SHIFT, text);
-  return t;
-}
 \f
 const char *
 table_halign_to_string (enum table_halign halign)
@@ -250,15 +155,15 @@ font_style_uninit (struct font_style *font)
 }
 
 void
-area_style_copy (struct pool *container,
-                 struct area_style *dst, const struct area_style *src)
+table_area_style_copy (struct pool *container, struct table_area_style *dst,
+                       const struct table_area_style *src)
 {
   font_style_copy (container, &dst->font_style, &src->font_style);
   dst->cell_style = src->cell_style;
 }
 
 void
-area_style_uninit (struct area_style *area)
+table_area_style_uninit (struct table_area_style *area)
 {
   if (area)
     font_style_uninit (&area->font_style);
@@ -312,6 +217,22 @@ font_style_dump (const struct font_style *f)
     fputs (" underline", stdout);
 }
 
+bool
+font_style_equal (const struct font_style *a, const struct font_style *b)
+{
+  return (a->bold == b->bold
+          && a->italic == b->italic
+          && a->underline == b->underline
+          && a->markup == b->markup
+          && cell_color_equal (&a->fg[0], &b->fg[0])
+          && cell_color_equal (&a->fg[1], &b->fg[1])
+          && cell_color_equal (&a->bg[0], &b->bg[0])
+          && cell_color_equal (&a->bg[1], &b->bg[1])
+          && !strcmp (a->typeface ? a->typeface : "",
+                      b->typeface ? b->typeface : "")
+          && a->size == b->size);
+}
+
 void
 cell_style_dump (const struct cell_style *c)
 {
@@ -374,27 +295,27 @@ table_vline (struct table *t, int style, int x, int y1, int y2)
 {
   if (debugging)
     {
-      if (x < 0 || x > table_nc (t)
-          || y1 < 0 || y1 >= table_nr (t)
-          || y2 < 0 || y2 >= table_nr (t))
+      if (x < 0 || x > t->n[H]
+          || y1 < 0 || y1 >= t->n[V]
+          || y2 < 0 || y2 >= t->n[V])
         {
           printf ("bad vline: x=%d y=(%d,%d) in table size (%d,%d)\n",
-                  x, y1, y2, table_nc (t), table_nr (t));
+                  x, y1, y2, t->n[H], t->n[V]);
           return;
         }
     }
 
   assert (x >= 0);
-  assert (x <= table_nc (t));
+  assert (x <= t->n[H]);
   assert (y1 >= 0);
   assert (y2 >= y1);
-  assert (y2 <= table_nr (t));
+  assert (y2 <= t->n[V]);
 
   if (style != -1)
     {
       int y;
       for (y = y1; y <= y2; y++)
-        t->rv[x + (table_nc (t) + 1) * y] = style;
+        t->rv[x + (t->n[H] + 1) * y] = style;
     }
 }
 
@@ -405,27 +326,27 @@ table_hline (struct table *t, int style, int x1, int x2, int y)
 {
   if (debugging)
     {
-      if (y < 0 || y > table_nr (t)
-          || x1 < 0 || x1 >= table_nc (t)
-          || x2 < 0 || x2 >= table_nc (t))
+      if (y < 0 || y > t->n[V]
+          || x1 < 0 || x1 >= t->n[H]
+          || x2 < 0 || x2 >= t->n[H])
         {
           printf ("bad hline: x=(%d,%d) y=%d in table size (%d,%d)\n",
-                  x1, x2, y, table_nc (t), table_nr (t));
+                  x1, x2, y, t->n[H], t->n[V]);
           return;
         }
     }
 
   assert (y >= 0);
-  assert (y <= table_nr (t));
+  assert (y <= t->n[V]);
   assert (x2 >= x1);
   assert (x1 >= 0);
-  assert (x2 < table_nc (t));
+  assert (x2 < t->n[H]);
 
   if (style != -1)
     {
       int x;
       for (x = x1; x <= x2; x++)
-        t->rh[x + table_nc (t) * y] = style;
+        t->rh[x + t->n[H] * y] = style;
     }
 }
 
@@ -441,13 +362,13 @@ table_box (struct table *t, int f_h, int f_v, int i_h, int i_v,
 {
   if (debugging)
     {
-      if (x1 < 0 || x1 >= table_nc (t)
-          || x2 < 0 || x2 >= table_nc (t)
-          || y1 < 0 || y1 >= table_nr (t)
-          || y2 < 0 || y2 >= table_nr (t))
+      if (x1 < 0 || x1 >= t->n[H]
+          || x2 < 0 || x2 >= t->n[H]
+          || y1 < 0 || y1 >= t->n[V]
+          || y2 < 0 || y2 >= t->n[V])
         {
           printf ("bad box: (%d,%d)-(%d,%d) in table size (%d,%d)\n",
-                  x1, y1, x2, y2, table_nc (t), table_nr (t));
+                  x1, y1, x2, y2, t->n[H], t->n[V]);
           NOT_REACHED ();
         }
     }
@@ -456,16 +377,16 @@ table_box (struct table *t, int f_h, int f_v, int i_h, int i_v,
   assert (y2 >= y1);
   assert (x1 >= 0);
   assert (y1 >= 0);
-  assert (x2 < table_nc (t));
-  assert (y2 < table_nr (t));
+  assert (x2 < t->n[H]);
+  assert (y2 < t->n[V]);
 
   if (f_h != -1)
     {
       int x;
       for (x = x1; x <= x2; x++)
         {
-          t->rh[x + table_nc (t) * y1] = f_h;
-          t->rh[x + table_nc (t) * (y2 + 1)] = f_h;
+          t->rh[x + t->n[H] * y1] = f_h;
+          t->rh[x + t->n[H] * (y2 + 1)] = f_h;
         }
     }
   if (f_v != -1)
@@ -473,8 +394,8 @@ table_box (struct table *t, int f_h, int f_v, int i_h, int i_v,
       int y;
       for (y = y1; y <= y2; y++)
         {
-          t->rv[x1 + (table_nc (t) + 1) * y] = f_v;
-          t->rv[(x2 + 1) + (table_nc (t) + 1) * y] = f_v;
+          t->rv[x1 + (t->n[H] + 1) * y] = f_v;
+          t->rv[(x2 + 1) + (t->n[H] + 1) * y] = f_v;
         }
     }
 
@@ -487,7 +408,7 @@ table_box (struct table *t, int f_h, int f_v, int i_h, int i_v,
           int x;
 
           for (x = x1; x <= x2; x++)
-            t->rh[x + table_nc (t) * y] = i_h;
+            t->rh[x + t->n[H] * y] = i_h;
         }
     }
   if (i_v != -1)
@@ -499,264 +420,124 @@ table_box (struct table *t, int f_h, int f_v, int i_h, int i_v,
           int y;
 
           for (y = y1; y <= y2; y++)
-            t->rv[x + (table_nc (t) + 1) * y] = i_v;
+            t->rv[x + (t->n[H] + 1) * y] = i_v;
         }
     }
 }
 \f
 /* Cells. */
 
-static void
-do_table_text (struct table *table, int c, int r, unsigned opt, char *text)
-{
-  assert (c >= 0);
-  assert (r >= 0);
-  assert (c < table_nc (table));
-  assert (r < table_nr (table));
-
-  if (debugging)
-    {
-      if (c < 0 || r < 0 || c >= table_nc (table) || r >= table_nr (table))
-        {
-          printf ("table_text(): bad cell (%d,%d) in table size (%d,%d)\n",
-                  c, r, table_nc (table), table_nr (table));
-          return;
-        }
-    }
-
-  table->cc[c + r * table_nc (table)] = text;
-  table->ct[c + r * table_nc (table)] = opt;
-}
-
-/* Sets cell (C,R) in TABLE, with options OPT, to have text value
-   TEXT. */
+/* Fill TABLE cells (X1,X2)-(Y1,Y2), inclusive, with VALUE and OPT. */
 void
-table_text (struct table *table, int c, int r, unsigned opt,
-          const char *text)
-{
-  do_table_text (table, c, r, opt, pool_strdup (table->container, text));
-}
-
-/* Sets cell (C,R) in TABLE, with options OPT, to have text value
-   FORMAT, which is formatted as if passed to printf. */
-void
-table_text_format (struct table *table, int c, int r, unsigned opt,
-                   const char *format, ...)
-{
-  va_list args;
-
-  va_start (args, format);
-  do_table_text (table, c, r, opt,
-                 pool_vasprintf (table->container, format, args));
-  va_end (args);
-}
-
-static struct table_cell *
-add_joined_cell (struct table *table, int x1, int y1, int x2, int y2,
-                 unsigned opt)
+table_put (struct table *table, int x1, int y1, int x2, int y2,
+           unsigned opt, const struct pivot_value *value)
 {
-  assert (x1 >= 0);
-  assert (y1 >= 0);
-  assert (y2 >= y1);
-  assert (x2 >= x1);
-  assert (y2 < table_nr (table));
-  assert (x2 < table_nc (table));
+  assert (0 <= x1 && x1 <= x2 && x2 < table->n[H]);
+  assert (0 <= y1 && y1 <= y2 && y2 < table->n[V]);
 
-  if (debugging)
-    {
-      if (x1 < 0 || x1 >= table_nc (table)
-          || y1 < 0 || y1 >= table_nr (table)
-          || x2 < x1 || x2 >= table_nc (table)
-          || y2 < y1 || y2 >= table_nr (table))
-        {
-          printf ("table_joint_text(): bad cell "
-                  "(%d,%d)-(%d,%d) in table size (%d,%d)\n",
-                  x1, y1, x2, y2, table_nc (table), table_nr (table));
-          return NULL;
-        }
-    }
-
-  table_box (table, -1, -1, TABLE_STROKE_NONE, TABLE_STROKE_NONE,
-             x1, y1, x2, y2);
-
-  struct table_cell *cell = pool_alloc (table->container, sizeof *cell);
-  cell->d[TABLE_HORZ][0] = x1;
-  cell->d[TABLE_VERT][0] = y1;
-  cell->d[TABLE_HORZ][1] = ++x2;
-  cell->d[TABLE_VERT][1] = ++y2;
-  cell->options = opt;
-  cell->footnotes = NULL;
-  cell->n_footnotes = 0;
-  cell->style = NULL;
-
-  void **cc = &table->cc[x1 + y1 * table_nc (table)];
-  unsigned short *ct = &table->ct[x1 + y1 * table_nc (table)];
-  const int ofs = table_nc (table) - (x2 - x1);
-  for (int y = y1; y < y2; y++)
+  if (x1 == x2 && y1 == y2)
     {
-      for (int x = x1; x < x2; x++)
-        {
-          *cc++ = cell;
-          *ct++ = opt | TAB_JOIN;
-        }
-
-      cc += ofs;
-      ct += ofs;
+      table->cc[x1 + y1 * table->n[H]] = CONST_CAST (struct pivot_value *, value);
+      table->ct[x1 + y1 * table->n[H]] = opt;
     }
-
-  return cell;
-}
-
-/* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
-   options OPT to have text value TEXT. */
-void
-table_joint_text (struct table *table, int x1, int y1, int x2, int y2,
-                  unsigned opt, const char *text)
-{
-  char *s = pool_strdup (table->container, text);
-  if (x1 == x2 && y1 == y2)
-    do_table_text (table, x1, y1, opt, s);
-  else
-    add_joined_cell (table, x1, y1, x2, y2, opt)->text = s;
-}
-
-static struct table_cell *
-get_joined_cell (struct table *table, int x, int y)
-{
-  int index = x + y * table_nc (table);
-  unsigned short opt = table->ct[index];
-  struct table_cell *cell;
-
-  if (opt & TAB_JOIN)
-    cell = table->cc[index];
   else
     {
-      char *text = table->cc[index];
-
-      cell = add_joined_cell (table, x, y, x, y, table->ct[index]);
-      cell->text = text ? text : pool_strdup (table->container, "");
-    }
-  return cell;
-}
+      table_box (table, -1, -1, TABLE_STROKE_NONE, TABLE_STROKE_NONE,
+                 x1, y1, x2, y2);
 
-/* Create a footnote in TABLE with MARKER (e.g. "a") as its marker and CONTENT
-   as its content.  The footnote will be styled as STYLE, which is mandatory.
-   IDX must uniquely identify the footnote within TABLE.
+      struct table_cell *cell = pool_alloc (table->container, sizeof *cell);
+      *cell = (struct table_cell) {
+        .d = { [H] = { x1, x2 + 1 }, [V] = { y1, y2 + 1 } },
+        .options = opt,
+        .value = value,
+      };
 
-   Returns the new footnote.  The return value is the only way to get to the
-   footnote later, so it is important for the caller to remember it. */
-struct footnote *
-table_create_footnote (struct table *table, size_t idx, const char *content,
-                       const char *marker, struct area_style *style)
-{
-  assert (style);
-
-  struct footnote *f = pool_alloc (table->container, sizeof *f);
-  f->idx = idx;
-  f->content = pool_strdup (table->container, content);
-  f->marker = pool_strdup (table->container, marker);
-  f->style = style;
-  return f;
+      for (int y = y1; y <= y2; y++)
+        {
+          size_t ofs = x1 + y * table->n[H];
+          void **cc = &table->cc[ofs];
+          unsigned short *ct = &table->ct[ofs];
+          for (int x = x1; x <= x2; x++)
+            {
+              *cc++ = cell;
+              *ct++ = opt | TAB_JOIN;
+            }
+        }
+    }
 }
 
-/* Attaches a reference to footnote F to the cell at column X, row Y in
-   TABLE. */
-void
-table_add_footnote (struct table *table, int x, int y,
-                    const struct footnote *f)
+static void
+free_value (void *value_)
 {
-  assert (f->style);
-
-  struct table_cell *cell = get_joined_cell (table, x, y);
-
-  cell->footnotes = pool_realloc (
-    table->container, cell->footnotes,
-    (cell->n_footnotes + 1) * sizeof *cell->footnotes);
-
-  cell->footnotes[cell->n_footnotes++] = f;
+  struct pivot_value *value = value_;
+  pivot_value_destroy (value);
 }
 
-/* Overrides the style for column X, row Y in TABLE with STYLE.
-   Does not make a copy of STYLE, so it should either be allocated from
-   TABLE->container or have a lifetime that will outlive TABLE. */
 void
-table_add_style (struct table *table, int x, int y,
-                 const struct area_style *style)
+table_put_owned (struct table *table, int x1, int y1, int x2, int y2,
+                 unsigned opt, struct pivot_value *value)
 {
-  get_joined_cell (table, x, y)->style = style;
+  table_put (table, x1, y1, x2, y2, opt, value);
+  pool_register (table->container, free_value, value);
 }
 
 /* Returns true if column C, row R has no contents, otherwise false. */
 bool
 table_cell_is_empty (const struct table *table, int c, int r)
 {
-  return table->cc[c + r * table_nc (table)] == NULL;
-}
-\f
-/* Editing. */
-
-/* Writes STRING to the output.  OPTIONS may be any valid combination of TAB_*
-   bits.
-
-   This function is obsolete.  Please do not add new uses of it.  Instead, use
-   a text_item (see output/text-item.h). */
-void
-table_output_text (int options UNUSED, const char *string)
-{
-  text_item_submit (text_item_create (TEXT_ITEM_LOG, string));
-}
-
-/* Same as table_output_text(), but FORMAT is passed through printf-like
-   formatting before output. */
-void
-table_output_text_format (int options, const char *format, ...)
-{
-  va_list args;
-  char *text;
-
-  va_start (args, format);
-  text = xvasprintf (format, args);
-  va_end (args);
-
-  table_output_text (options, text);
-
-  free (text);
+  return table->cc[c + r * table->n[H]] == NULL;
 }
 \f
 /* Initializes CELL with the contents of the table cell at column X and row Y
-   within TABLE.  When CELL is no longer needed, the caller is responsible for
-   freeing it by calling table_cell_free(CELL).
-
-   The caller must ensure that CELL is destroyed before TABLE is unref'ed. */
+   within TABLE. */
 void
 table_get_cell (const struct table *t, int x, int y, struct table_cell *cell)
 {
   assert (x >= 0 && x < t->n[TABLE_HORZ]);
   assert (y >= 0 && y < t->n[TABLE_VERT]);
 
-  int index = x + y * table_nc (t);
+  int index = x + y * t->n[H];
   unsigned short opt = t->ct[index];
   const void *cc = t->cc[index];
 
-  const struct area_style *style
+  struct table_area_style *style
     = t->styles[(opt & TAB_STYLE_MASK) >> TAB_STYLE_SHIFT];
+
+  static const struct pivot_value empty_value = {
+    .text = {
+      .type = PIVOT_VALUE_TEXT,
+      .local = (char *) "",
+      .c = (char *) "",
+      .id = (char *) "",
+      .user_provided = true,
+    },
+  };
+
   if (opt & TAB_JOIN)
     {
       const struct table_cell *jc = cc;
       *cell = *jc;
-      if (!cell->style)
-        cell->style = style;
+      if (!cell->value)
+        cell->value = &empty_value;
+      if (!cell->font_style)
+        cell->font_style = &style->font_style;
+      if (!cell->cell_style)
+        cell->cell_style = &style->cell_style;
     }
   else
-    *cell = (struct table_cell) {
-      .d = { [TABLE_HORZ] = { x, x + 1 },
-             [TABLE_VERT] = { y, y + 1 } },
-      .options = opt,
-      .text = CONST_CAST (char *, cc ? cc : ""),
-      .style = style,
-    };
-
-  assert (cell->style);
+    {
+      const struct pivot_value *v = cc ? cc : &empty_value;
+      *cell = (struct table_cell) {
+        .d = { [H] = { x, x + 1 }, [V] = { y, y + 1 } },
+        .options = opt,
+        .value = v,
+        .font_style = v->font_style ? v->font_style : &style->font_style,
+        .cell_style = v->cell_style ? v->cell_style : &style->cell_style,
+      };
+    }
+
+  assert (cell->font_style);
+  assert (cell->cell_style);
 }
 
 /* Returns one of the TAL_* enumeration constants (declared in output/table.h)
@@ -804,8 +585,8 @@ table_get_rule (const struct table *table, enum table_axis axis, int x, int y,
   assert (y >= 0 && y < table->n[TABLE_VERT] + (axis == TABLE_VERT));
 
   uint8_t raw = (axis == TABLE_VERT
-                 ? table->rh[x + table_nc (table) * y]
-                 : table->rv[x + (table_nc (table) + 1) * y]);
+                 ? table->rh[x + table->n[H] * y]
+                 : table->rv[x + (table->n[H] + 1) * y]);
   struct cell_color *p = table->rule_colors[(raw & TAB_RULE_STYLE_MASK)
                                             >> TAB_RULE_STYLE_SHIFT];
   *color = p ? *p : (struct cell_color) CELL_COLOR_BLACK;