enum { MEMORY, DISK } storage; /* Where cases are stored. */
enum { WRITE, READ } mode; /* Is writing or reading allowed? */
struct casereader *readers; /* List of our readers. */
- int being_destroyed; /* Does a destructive reader exist? */
+ bool being_destroyed; /* Does a destructive reader exist? */
bool ok; /* False after I/O error. */
/* Memory storage. */
struct casereader *next, *prev; /* Next, prev in casefile's list. */
struct casefile *cf; /* Our casefile. */
unsigned long case_idx; /* Case number of current case. */
- int destructive; /* Is this a destructive reader? */
+ 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. */
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);
return !cf->ok;
}
-/* Returns nonzero only if casefile CF is stored in memory (instead of on
- disk). */
-int
+/* Returns true only if casefile CF is stored in memory (instead of on
+ disk), false otherwise. */
+bool
casefile_in_core (const struct casefile *cf)
{
assert (cf != NULL);
cf->buffer_size * sizeof *cf->buffer))
io_error (cf, _("Error writing temporary file: %s."),
strerror (errno));
-
-
cf->buffer_used = 0;
}
}
reader->cf = cf;
reader->case_idx = 0;
reader->destructive = 0;
+ reader->random = false;
reader->fd = -1;
reader->buffer = NULL;
reader->buffer_pos = 0;
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
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;
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. */
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;
}
/* Reads a copy of the next case from READER into C.
Caller is responsible for destroying C.
Returns true if successful, false at end of file. */
-int
+bool
casereader_read (struct casereader *reader, struct ccase *c)
{
assert (reader != NULL);
if (!reader->cf->ok || reader->case_idx >= reader->cf->case_cnt)
- return 0;
+ return false;
if (reader->cf->storage == MEMORY)
{
case_clone (c, &reader->cf->cases[block_idx][case_idx]);
reader->case_idx++;
- return 1;
+ return true;
}
else
{
if (reader->buffer_pos + reader->cf->value_cnt > reader->cf->buffer_size)
{
if (!fill_buffer (reader))
- return 0;
+ return false;
reader->buffer_pos = 0;
}
reader->case_idx++;
case_clone (c, &reader->c);
- return 1;
+ return true;
}
}
to the caller. Caller is responsible for destroying C.
Returns true if successful, false at end of file or on I/O
error. */
-int
+bool
casereader_read_xfer (struct casereader *reader, struct ccase *c)
{
assert (reader != NULL);
case_move (c, read_case);
reader->case_idx++;
- return 1;
+ return true;
}
}
+/* 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)
static void
register_atexit (void)
{
- static int registered = 0;
+ static bool registered = false;
if (!registered)
{
- registered = 1;
+ registered = true;
atexit (exit_handler);
}
}
-
-
/* atexit() handler that closes and deletes our temporary
files. */
static void