Add linked list library to PSPP.
[pspp-builds.git] / src / data / casefile.c
index 76f1d97a9bae6ec2cb0060eddf8594beb2ad475a..7681a65f9d4e3e33b1c272760ed33ea0bc78e621 100644 (file)
           during the reading phase.  Each casereader has an
           independent position in the casefile.
 
           during the reading phase.  Each casereader has an
           independent position in the casefile.
 
-          Casereaders may only move forward.  They cannot move
-          backward to arbitrary records or seek randomly.
-          Cloning casereaders is possible, but it is not yet
-          implemented.
+          Ordinary casereaders may only move forward.  They
+          cannot move backward to arbitrary records or seek
+          randomly.  Cloning casereaders is possible, but it is
+          not yet implemented.
 
           Use casefile_get_reader() to create a casereader for
           use in phase 2.  This also transitions from phase 1 to
 
           Use casefile_get_reader() to create a casereader for
           use in phase 2.  This also transitions from phase 1 to
           a case from a casereader.  Use casereader_destroy() to
           discard a casereader when it is no longer needed.
 
           a case from a casereader.  Use casereader_destroy() to
           discard a casereader when it is no longer needed.
 
+          "Random" casereaders, which support a seek operation,
+          may also be created.  These should not, generally, be
+          used for statistical procedures, because random access
+          is much slower than sequential access.  They are
+          intended for use by the GUI.
+
        3. Destruction.  This phase is optional.  The casefile is
           also read with casereaders in this phase, but the
           ability to create new casereaders is curtailed.
        3. Destruction.  This phase is optional.  The casefile is
           also read with casereaders in this phase, but the
           ability to create new casereaders is curtailed.
@@ -150,9 +156,12 @@ struct casereader
     struct casefile *cf;                /* Our casefile. */
     unsigned long case_idx;             /* Case number of current case. */
     bool destructive;                   /* Is this a destructive reader? */
     struct casefile *cf;                /* Our casefile. */
     unsigned long case_idx;             /* Case number of current case. */
     bool destructive;                   /* Is this a destructive reader? */
+    bool random;                        /* Is this a random reader? */
 
     /* Disk storage. */
     int fd;                             /* File descriptor. */
 
     /* Disk storage. */
     int fd;                             /* File descriptor. */
+    off_t file_ofs;                     /* Current position in fd. */
+    off_t buffer_ofs;                   /* File offset of buffer start. */
     union value *buffer;                /* I/O buffer. */
     size_t buffer_pos;                  /* Offset of buffer position. */
     struct ccase c;                     /* Current case. */
     union value *buffer;                /* I/O buffer. */
     size_t buffer_pos;                  /* Offset of buffer position. */
     struct ccase c;                     /* Current case. */
@@ -174,10 +183,11 @@ static size_t case_bytes;
 static void register_atexit (void);
 static void exit_handler (void);
 
 static void register_atexit (void);
 static void exit_handler (void);
 
-static void reader_open_file (struct casereader *reader);
-static void write_case_to_disk (struct casefile *cf, const struct ccase *c);
-static void flush_buffer (struct casefile *cf);
-static bool fill_buffer (struct casereader *reader);
+static void reader_open_file (struct casereader *);
+static void write_case_to_disk (struct casefile *, const struct ccase *);
+static void flush_buffer (struct casefile *);
+static void seek_and_fill_buffer (struct casereader *);
+static bool fill_buffer (struct casereader *);
 
 static void io_error (struct casefile *, const char *, ...)
      PRINTF_FORMAT (2, 3);
 
 static void io_error (struct casefile *, const char *, ...)
      PRINTF_FORMAT (2, 3);
@@ -423,8 +433,6 @@ flush_buffer (struct casefile *cf)
                        cf->buffer_size * sizeof *cf->buffer))
         io_error (cf, _("Error writing temporary file: %s."),
                   strerror (errno));
                        cf->buffer_size * sizeof *cf->buffer))
         io_error (cf, _("Error writing temporary file: %s."),
                   strerror (errno));
-
-
       cf->buffer_used = 0;
     }
 }
       cf->buffer_used = 0;
     }
 }
@@ -519,6 +527,7 @@ casefile_get_reader (const struct casefile *cf_)
   reader->cf = cf;
   reader->case_idx = 0;
   reader->destructive = 0;
   reader->cf = cf;
   reader->case_idx = 0;
   reader->destructive = 0;
+  reader->random = false;
   reader->fd = -1;
   reader->buffer = NULL;
   reader->buffer_pos = 0;
   reader->fd = -1;
   reader->buffer = NULL;
   reader->buffer_pos = 0;
@@ -530,6 +539,23 @@ casefile_get_reader (const struct casefile *cf_)
   return reader;
 }
 
   return reader;
 }
 
+/* Creates and returns a random casereader for CF.  A random
+   casereader can be used to randomly read the cases in a
+   casefile. */
+struct casereader *
+casefile_get_random_reader (const struct casefile *cf) 
+{
+  struct casefile  *mutable_casefile = (struct casefile*) cf;
+  struct casereader *reader;
+
+  enum { WRITE, READ } mode = cf->mode ;
+  reader = casefile_get_reader (cf);
+  reader->random = true;
+  mutable_casefile->mode = mode;
+  
+  return reader;
+}
+
 /* Creates and returns a destructive casereader for CF.  Like a
    normal casereader, a destructive casereader sequentially reads
    the cases in a casefile.  Unlike a normal casereader, a
 /* Creates and returns a destructive casereader for CF.  Like a
    normal casereader, a destructive casereader sequentially reads
    the cases in a casefile.  Unlike a normal casereader, a
@@ -556,8 +582,6 @@ static void
 reader_open_file (struct casereader *reader) 
 {
   struct casefile *cf = reader->cf;
 reader_open_file (struct casereader *reader) 
 {
   struct casefile *cf = reader->cf;
-  off_t file_ofs;
-
   if (!cf->ok || reader->case_idx >= cf->case_cnt)
     return;
 
   if (!cf->ok || reader->case_idx >= cf->case_cnt)
     return;
 
@@ -585,24 +609,42 @@ reader_open_file (struct casereader *reader)
       memset (reader->buffer, 0, cf->buffer_size * sizeof *cf->buffer); 
     }
 
       memset (reader->buffer, 0, cf->buffer_size * sizeof *cf->buffer); 
     }
 
+  case_create (&reader->c, cf->value_cnt);
+
+  reader->buffer_ofs = -1;
+  reader->file_ofs = -1;
+  seek_and_fill_buffer (reader);
+}
+
+/* Seeks the backing file for READER to the proper position and
+   refreshes the buffer contents. */
+static void
+seek_and_fill_buffer (struct casereader *reader) 
+{
+  struct casefile *cf = reader->cf;
+  off_t new_ofs;
+
   if (cf->value_cnt != 0) 
     {
       size_t buffer_case_cnt = cf->buffer_size / cf->value_cnt;
   if (cf->value_cnt != 0) 
     {
       size_t buffer_case_cnt = cf->buffer_size / cf->value_cnt;
-      file_ofs = ((off_t) reader->case_idx / buffer_case_cnt
+      new_ofs = ((off_t) reader->case_idx / buffer_case_cnt
                   * cf->buffer_size * sizeof *cf->buffer);
       reader->buffer_pos = (reader->case_idx % buffer_case_cnt
                             * cf->value_cnt);
     }
   else 
                   * cf->buffer_size * sizeof *cf->buffer);
       reader->buffer_pos = (reader->case_idx % buffer_case_cnt
                             * cf->value_cnt);
     }
   else 
-    file_ofs = 0;
-  if (lseek (reader->fd, file_ofs, SEEK_SET) != file_ofs)
-    io_error (cf, _("%s: Seeking temporary file: %s."),
-              cf->file_name, strerror (errno));
+    new_ofs = 0;
+  if (new_ofs != reader->file_ofs) 
+    {
+      if (lseek (reader->fd, new_ofs, SEEK_SET) != new_ofs)
+        io_error (cf, _("%s: Seeking temporary file: %s."),
+                  cf->file_name, strerror (errno));
+      else
+        reader->file_ofs = new_ofs;
+    }
 
 
-  if (cf->case_cnt > 0 && cf->value_cnt > 0)
+  if (cf->case_cnt > 0 && cf->value_cnt > 0 && reader->buffer_ofs != new_ofs)
     fill_buffer (reader);
     fill_buffer (reader);
-
-  case_create (&reader->c, cf->value_cnt);
 }
 
 /* Fills READER's buffer by reading a block from disk. */
 }
 
 /* Fills READER's buffer by reading a block from disk. */
@@ -618,7 +660,12 @@ fill_buffer (struct casereader *reader)
                   reader->cf->file_name, strerror (errno));
       else if (bytes != reader->cf->buffer_size * sizeof *reader->buffer) 
         io_error (reader->cf, _("%s: Temporary file ended unexpectedly."),
                   reader->cf->file_name, strerror (errno));
       else if (bytes != reader->cf->buffer_size * sizeof *reader->buffer) 
         io_error (reader->cf, _("%s: Temporary file ended unexpectedly."),
-                  reader->cf->file_name); 
+                  reader->cf->file_name);
+      else 
+        {
+          reader->buffer_ofs = reader->file_ofs;
+          reader->file_ofs += bytes; 
+        }
     }
   return reader->cf->ok;
 }
     }
   return reader->cf->ok;
 }
@@ -696,6 +743,21 @@ casereader_read_xfer (struct casereader *reader, struct ccase *c)
     }
 }
 
     }
 }
 
+/* Sets the next case to be read by READER to CASE_IDX,
+   which must be less than the number of cases in the casefile.
+   Allowed only for random readers. */
+void
+casereader_seek (struct casereader *reader, unsigned long case_idx) 
+{
+  assert (reader != NULL);
+  assert (reader->random);
+  assert (case_idx < reader->cf->case_cnt);
+
+  reader->case_idx = case_idx;
+  if (reader->cf->storage == DISK)
+    seek_and_fill_buffer (reader);
+}
+
 /* Destroys READER. */
 void
 casereader_destroy (struct casereader *reader)
 /* Destroys READER. */
 void
 casereader_destroy (struct casereader *reader)