Change "union value" to dynamically allocate long strings.
[pspp-builds.git] / src / data / scratch-writer.c
index 13ccee8c6f0aef698c2542ca495dbf4280bf5677..631305fe9d96c6a668c33473780867930f88d3a4 100644 (file)
-/* PSPP - computes sample statistics.
-   Copyright (C) 2006 Free Software Foundation, Inc.
-   Written by Ben Pfaff <blp@gnu.org>.
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2006, 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 the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
+   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
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
 
-   This program is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   General Public License for more details.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-   02110-1301, USA. */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <config.h>
+
 #include "scratch-writer.h"
+
 #include <stdlib.h>
-#include "case.h"
-#include "casefile.h"
-#include "fastfile.h"
-#include "dictionary.h"
-#include "file-handle-def.h"
-#include "scratch-handle.h"
+
+#include <data/case.h>
+#include <data/case-map.h>
+#include <data/casereader.h>
+#include <data/casewriter-provider.h>
+#include <data/casewriter.h>
+#include <data/dictionary.h>
+#include <data/file-handle-def.h>
+#include <data/scratch-handle.h>
+#include <data/variable.h>
+#include <libpspp/compiler.h>
+#include <libpspp/taint.h>
+
 #include "xalloc.h"
 
+#define N_(msgid) (msgid)
+
 /* A scratch file writer. */
-struct scratch_writer 
+struct scratch_writer
   {
-    struct scratch_handle *handle;      /* Underlying scratch handle. */
     struct file_handle *fh;             /* Underlying file handle. */
-    struct dict_compactor *compactor;   /* Compacts into handle->dictionary. */
+    struct fh_lock *lock;               /* Exclusive access to file handle. */
+    struct dictionary *dict;            /* Dictionary for subwriter. */
+    struct case_map *compactor;         /* Compacts into dictionary. */
+    struct casewriter *subwriter;       /* Data output. */
   };
 
+static const struct casewriter_class scratch_writer_casewriter_class;
+
 /* Opens FH, which must have referent type FH_REF_SCRATCH, and
    returns a scratch_writer for it, or a null pointer on
    failure.  Cases stored in the scratch_writer will be expected
-   to be drawn from DICTIONARY.
-
-   If you use an any_writer instead, then your code can be more
-   flexible without being any harder to write. */
-struct scratch_writer *
+   to be drawn from DICTIONARY. */
+struct casewriter *
 scratch_writer_open (struct file_handle *fh,
-                     const struct dictionary *dictionary) 
+                     const struct dictionary *dictionary)
 {
-  struct scratch_handle *sh;
   struct scratch_writer *writer;
-  struct dictionary *scratch_dict;
-  struct dict_compactor *compactor;
-
-  if (!fh_open (fh, FH_REF_SCRATCH, "scratch file", "we"))
+  struct casewriter *casewriter;
+  struct fh_lock *lock;
+
+  /* Get exclusive write access to handle. */
+  /* TRANSLATORS: this fragment will be interpolated into
+     messages in fh_lock() that identify types of files. */
+  lock = fh_lock (fh, FH_REF_SCRATCH, N_("scratch file"), FH_ACC_WRITE, true);
+  if (lock == NULL)
     return NULL;
 
-  /* Destroy previous contents of handle. */
-  sh = fh_get_scratch_handle (fh);
-  if (sh != NULL) 
-    scratch_handle_destroy (sh);
+  /* Create writer. */
+  writer = xmalloc (sizeof *writer);
+  writer->lock = lock;
+  writer->fh = fh_ref (fh);
 
-  /* Copy the dictionary and compact if needed. */
-  scratch_dict = dict_clone (dictionary);
-  if (dict_compacting_would_shrink (scratch_dict)) 
+  writer->dict = dict_clone (dictionary);
+  dict_delete_scratch_vars (writer->dict);
+  if (dict_count_values (writer->dict, 0)
+      < dict_get_next_value_idx (writer->dict))
     {
-      compactor = dict_make_compactor (scratch_dict);
-      dict_compact_values (scratch_dict);
+      writer->compactor = case_map_to_compact_dict (writer->dict, 0);
+      dict_compact_values (writer->dict);
     }
   else
-    compactor = NULL;
-
-  /* Create new contents. */
-  sh = xmalloc (sizeof *sh);
-  sh->dictionary = scratch_dict;
-  sh->casefile = fastfile_create (dict_get_next_value_idx (sh->dictionary));
-
-  /* Create writer. */
-  writer = xmalloc (sizeof *writer);
-  writer->handle = sh;
-  writer->fh = fh;
-  writer->compactor = compactor;
-
-  fh_set_scratch_handle (fh, sh);
-  return writer;
+    writer->compactor = NULL;
+  writer->subwriter = autopaging_writer_create (dict_get_proto (writer->dict));
+
+  casewriter = casewriter_create (dict_get_proto (writer->dict),
+                                  &scratch_writer_casewriter_class, writer);
+  taint_propagate (casewriter_get_taint (writer->subwriter),
+                   casewriter_get_taint (casewriter));
+  return casewriter;
 }
 
 /* Writes case C to WRITER. */
-bool
-scratch_writer_write_case (struct scratch_writer *writer,
-                           const struct ccase *c) 
+static void
+scratch_writer_casewriter_write (struct casewriter *w UNUSED, void *writer_,
+                                 struct ccase *c)
 {
-  struct scratch_handle *handle = writer->handle;
-  if (writer->compactor) 
-    {
-      struct ccase tmp_case;
-      case_create (&tmp_case, dict_get_next_value_idx (handle->dictionary));
-      dict_compactor_compact (writer->compactor, &tmp_case, c);
-      return casefile_append_xfer (handle->casefile, &tmp_case);
-    }
-  else 
-    return casefile_append (handle->casefile, c);
+  struct scratch_writer *writer = writer_;
+  casewriter_write (writer->subwriter,
+                    case_map_execute (writer->compactor, c));
 }
 
-/* Returns true if an I/O error occurred on WRITER, false otherwise. */
-bool
-scratch_writer_error (const struct scratch_writer *writer) 
+/* Closes WRITER. */
+static void
+scratch_writer_casewriter_destroy (struct casewriter *w UNUSED, void *writer_)
 {
-  return casefile_error (writer->handle->casefile);
-}
+  static unsigned int next_unique_id = 0x12345678;
 
-/* Closes WRITER.
-   Returns true if successful, false if an I/O error occurred on WRITER. */
-bool
-scratch_writer_close (struct scratch_writer *writer) 
-{
-  struct casefile *cf = writer->handle->casefile;
-  bool ok = casefile_error (cf);
-  fh_close (writer->fh, "scratch file", "we");
+  struct scratch_writer *writer = writer_;
+  struct casereader *reader = casewriter_make_reader (writer->subwriter);
+  if (!casereader_error (reader))
+    {
+      /* Destroy previous contents of handle. */
+      struct scratch_handle *sh = fh_get_scratch_handle (writer->fh);
+      if (sh != NULL)
+        scratch_handle_destroy (sh);
+
+      /* Create new contents. */
+      sh = xmalloc (sizeof *sh);
+      sh->unique_id = ++next_unique_id;
+      sh->dictionary = writer->dict;
+      sh->casereader = reader;
+      fh_set_scratch_handle (writer->fh, sh);
+    }
+  else
+    {
+      casereader_destroy (reader);
+      dict_destroy (writer->dict);
+    }
+
+  fh_unlock (writer->lock);
+  fh_unref (writer->fh);
   free (writer);
-  return ok;
 }
+
+static const struct casewriter_class scratch_writer_casewriter_class =
+  {
+    scratch_writer_casewriter_write,
+    scratch_writer_casewriter_destroy,
+    NULL,
+  };