casefile_destroy(). This function will also destroy any
remaining casereaders. */
+/* FIXME: should we implement compression? */
+
/* In-memory cases are arranged in an array of arrays. The top
level is variable size and the size of each bottom level array
is fixed at the number of cases defined here. */
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