From 93d4e1865e8e3e2c62bddad822869eedf2e457e0 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Wed, 7 Jun 2006 01:48:47 +0000 Subject: [PATCH] Add random access to casefiles, for use in GUI. --- src/data/ChangeLog | 12 +++++ src/data/casefile.c | 84 ++++++++++++++++++++++++------ src/data/casefile.h | 3 ++ src/language/tests/ChangeLog | 5 ++ src/language/tests/casefile-test.c | 21 ++++++++ 5 files changed, 108 insertions(+), 17 deletions(-) diff --git a/src/data/ChangeLog b/src/data/ChangeLog index 329c688b..aec7b0c8 100644 --- a/src/data/ChangeLog +++ b/src/data/ChangeLog @@ -1,3 +1,15 @@ +Tue Jun 6 18:46:26 2006 Ben Pfaff + + 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 * settings.c: Added call to i18n{done, init}. diff --git a/src/data/casefile.c b/src/data/casefile.c index 76f1d97a..9e31d431 100644 --- a/src/data/casefile.c +++ b/src/data/casefile.c @@ -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) diff --git a/src/data/casefile.h b/src/data/casefile.h index e60566e6..ab60e192 100644 --- a/src/data/casefile.h +++ b/src/data/casefile.h @@ -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 */ diff --git a/src/language/tests/ChangeLog b/src/language/tests/ChangeLog index 4a96aeb9..80c27c0d 100644 --- a/src/language/tests/ChangeLog +++ b/src/language/tests/ChangeLog @@ -1,3 +1,8 @@ +Tue Jun 6 18:48:00 2006 Ben Pfaff + + * casefile-test.c: (test_casefile) Test the new casereader_seek() + function. + Thu Mar 2 08:40:33 WST 2006 John Darrington * Moved files from src directory diff --git a/src/language/tests/casefile-test.c b/src/language/tests/casefile-test.c index 10722dcb..432cf388 100644 --- a/src/language/tests/casefile-test.c +++ b/src/language/tests/casefile-test.c @@ -21,11 +21,14 @@ #include #include +#include #include #include #include #include +#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); -- 2.30.2