Add random access to casefiles, for use in GUI.
authorBen Pfaff <blp@gnu.org>
Wed, 7 Jun 2006 01:48:47 +0000 (01:48 +0000)
committerBen Pfaff <blp@gnu.org>
Wed, 7 Jun 2006 01:48:47 +0000 (01:48 +0000)
src/data/ChangeLog
src/data/casefile.c
src/data/casefile.h
src/language/tests/ChangeLog
src/language/tests/casefile-test.c

index 329c688b9d4d0ff1ec3b8bf784a9dca04d444199..aec7b0c8d587df97b26f5e6b4f7dc42755f930ca 100644 (file)
@@ -1,3 +1,15 @@
+Tue Jun  6 18:46:26 2006  Ben Pfaff  <blp@gnu.org>
+
+       Implement random access to casefiles, for use in GUI.
+       
+       * casefile.c: (struct casereader) Add `random', `file_ofs',
+       `buffer_ofs' members.
+       (casefile_get_random_reader) New function.
+       (read_open_file) Break part into new function
+       seek_and_fill_buffer().
+       (fill_buffer) Update buffer_ofs, file_ofs.
+       (casereader_seek) New function.
+
 Tue May 30 19:52:33 WST 2006 John Darrington <john@darrington.wattle.id.au>
 
        * settings.c: Added call to i18n{done, init}.
index 76f1d97a9bae6ec2cb0060eddf8594beb2ad475a..9e31d431ce39410b0953515e79d1de5b12be0311 100644 (file)
@@ -150,9 +150,12 @@ struct casereader
     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. */
+    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. */
@@ -174,10 +177,11 @@ static size_t case_bytes;
 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);
@@ -423,8 +427,6 @@ flush_buffer (struct casefile *cf)
                        cf->buffer_size * sizeof *cf->buffer))
         io_error (cf, _("Error writing temporary file: %s."),
                   strerror (errno));
-
-
       cf->buffer_used = 0;
     }
 }
@@ -519,6 +521,7 @@ casefile_get_reader (const struct casefile *cf_)
   reader->cf = cf;
   reader->case_idx = 0;
   reader->destructive = 0;
+  reader->random = false;
   reader->fd = -1;
   reader->buffer = NULL;
   reader->buffer_pos = 0;
@@ -530,6 +533,17 @@ casefile_get_reader (const struct casefile *cf_)
   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 casereader *reader = casefile_get_reader (cf);
+  reader->random = true;
+  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
@@ -556,8 +570,6 @@ static void
 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;
 
@@ -585,24 +597,42 @@ reader_open_file (struct casereader *reader)
       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;
-      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 
-    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);
-
-  case_create (&reader->c, cf->value_cnt);
 }
 
 /* Fills READER's buffer by reading a block from disk. */
@@ -618,7 +648,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); 
+                  reader->cf->file_name);
+      else 
+        {
+          reader->buffer_ofs = reader->file_ofs;
+          reader->file_ofs += bytes; 
+        }
     }
   return reader->cf->ok;
 }
@@ -696,6 +731,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)
index e60566e6327b7c5ee8b4a6cf0667a15fe5022196..ab60e192798cbea20d0a142cb2ef06727e03c492 100644 (file)
@@ -44,12 +44,15 @@ bool casefile_append_xfer (struct casefile *, struct ccase *);
 void casefile_mode_reader (struct casefile *);
 struct casereader *casefile_get_reader (const struct casefile *);
 struct casereader *casefile_get_destructive_reader (struct casefile *);
+struct casereader *casefile_get_random_reader (const struct casefile *);
 
 const struct casefile *casereader_get_casefile (const struct casereader *);
 bool casereader_read (struct casereader *, struct ccase *);
 bool casereader_read_xfer (struct casereader *, struct ccase *);
 void casereader_destroy (struct casereader *);
 
+void casereader_seek (struct casereader *, unsigned long case_idx);
+
 unsigned long casereader_cnum(const struct casereader *);
 
 #endif /* casefile.h */
index 4a96aeb941a9479876c7386203e97d30fc102f8b..80c27c0d983d9925c82b2e8028fcf435ac635e76 100644 (file)
@@ -1,3 +1,8 @@
+Tue Jun  6 18:48:00 2006  Ben Pfaff  <blp@gnu.org>
+
+       * casefile-test.c: (test_casefile) Test the new casereader_seek()
+       function.
+
 Thu Mar  2 08:40:33 WST 2006 John Darrington <john@darrington.wattle.id.au>
        
        * Moved files from src directory
index 10722dcbaf17152c9f62740e1586a19ab7585e07..432cf388e944f1d9add1345db209c62f510fe038 100644 (file)
 #include <data/casefile.h>
 #include <data/case.h>
 
+#include <gsl/gsl_randist.h>
 #include <gsl/gsl_rng.h>
 #include <stdarg.h>
 #include <language/command.h>
 #include <language/lexer/lexer.h>
 
+#include "xalloc.h"
+
 static void test_casefile (int pattern, size_t value_cnt, size_t case_cnt);
 static void get_random_case (struct ccase *, size_t value_cnt,
                              size_t case_idx);
@@ -133,6 +136,24 @@ test_casefile (int pattern, size_t value_cnt, size_t case_cnt)
     casereader_destroy (r1);
   if (pattern != 2)
     casereader_destroy (r2);
+  if (pattern > 3) 
+    {
+      int *order;
+      r1 = casefile_get_random_reader (cf);
+      order = xmalloc (sizeof *order * case_cnt);
+      for (i = 0; i < case_cnt; i++)
+        order[i] = i;
+      if (case_cnt > 0)
+        gsl_ran_shuffle (rng, order, case_cnt, sizeof *order);
+      for (i = 0; i < case_cnt; i++)
+        {
+          int case_idx = order[i];
+          casereader_seek (r1, case_idx);
+          read_and_verify_random_case (cf, r1, case_idx);
+        }
+      casereader_destroy (r1);
+      free (order);
+    }
   if (pattern > 2) 
     {
       r1 = casefile_get_destructive_reader (cf);