return NULL;
}
+/* Destroys a SORT CASES program. */
void
destroy_sort_cases_pgm (struct sort_cases_pgm *scp)
{
int
sort_cases (struct sort_cases_pgm *scp, int separate)
{
+ scp->case_size = sizeof (union value) * compaction_nval;
+
/* Not sure this is necessary but it's good to be safe. */
if (separate && case_source_is_class (vfm_source, &sort_source_class))
procedure (NULL, NULL, NULL, NULL);
return 0;
}
\f
+/* Results of an internal sort. */
struct internal_sort
{
struct case_list **results;
return NULL;
}
+/* Destroys an internal sort result. */
static void
destroy_internal_sort (struct internal_sort *isrt)
{
return a->case_cnt > b->case_cnt ? -1 : a->case_cnt <b->case_cnt;
}
+/* Results of an external sort. */
struct external_sort
{
struct sort_cases_pgm *scp; /* SORT CASES info. */
struct initial_run *initial_runs; /* Array of initial runs. */
size_t run_cnt, run_cap; /* Number of runs, allocated capacity. */
char *temp_dir; /* Temporary file directory name. */
+ char *temp_name; /* Name of a temporary file. */
int next_file_idx; /* Lowest unused file index. */
- size_t case_size; /* Number of bytes in case. */
};
/* Prototypes for helper functions. */
-static void sort_sink_write (struct case_sink *, struct ccase *);
+static void sort_sink_write (struct case_sink *, const struct ccase *);
static int write_initial_runs (struct external_sort *, int separate);
static int init_external_sort (struct external_sort *);
static int merge (struct external_sort *);
xsrt = xmalloc (sizeof *xsrt);
xsrt->scp = scp;
- xsrt->case_size = sizeof (union value) * compaction_nval;
if (!init_external_sort (xsrt))
goto done;
if (!write_initial_runs (xsrt, separate))
for (i = 0; i < xsrt->run_cnt; i++)
remove_temp_file (xsrt, xsrt->initial_runs[i].file_idx);
rmdir_temp_dir (xsrt);
+ free (xsrt->temp_dir);
+ free (xsrt->temp_name);
free (xsrt->initial_runs);
free (xsrt);
}
/* Temporary directory. */
xsrt->temp_dir = make_temp_dir ();
+ xsrt->temp_name = NULL;
if (xsrt->temp_dir == NULL)
return 0;
+ xsrt->temp_name = xmalloc (strlen (xsrt->temp_dir) + 64);
return 1;
}
-
+/* Returns nonzero if we should return an error even though the
+ operation succeeded. Useful for testing. */
static int
simulate_error (void)
{
}
}
-#define TEMP_FILE_NAME_SIZE (L_tmpnam + 32)
-static void
-get_temp_file_name (struct external_sort *xsrt, int file_idx,
- char filename[TEMP_FILE_NAME_SIZE])
+/* Returns the name of temporary file number FILE_IDX for XSRT.
+ The name is written into a static buffer, so be careful. */
+static char *
+get_temp_file_name (struct external_sort *xsrt, int file_idx)
{
assert (xsrt->temp_dir != NULL);
- sprintf (filename, "%s%c%04d", xsrt->temp_dir, DIR_SEPARATOR, file_idx);
+ sprintf (xsrt->temp_name, "%s%c%04d",
+ xsrt->temp_dir, DIR_SEPARATOR, file_idx);
+ return xsrt->temp_name;
}
+/* Opens temporary file numbered FILE_IDX for XSRT with mode MODE
+ and returns the FILE *. */
static FILE *
open_temp_file (struct external_sort *xsrt, int file_idx, const char *mode)
{
- char temp_file[TEMP_FILE_NAME_SIZE];
+ char *temp_file;
FILE *file;
- get_temp_file_name (xsrt, file_idx, temp_file);
+ temp_file = get_temp_file_name (xsrt, file_idx);
file = fopen (temp_file, mode);
if (simulate_error () || file == NULL)
return file;
}
+/* Closes FILE, which is the temporary file numbered FILE_IDX
+ under XSRT. Returns nonzero only if successful. */
static int
close_temp_file (struct external_sort *xsrt, int file_idx, FILE *file)
{
if (file != NULL)
{
- char temp_file[TEMP_FILE_NAME_SIZE];
- get_temp_file_name (xsrt, file_idx, temp_file);
+ char *temp_file = get_temp_file_name (xsrt, file_idx);
if (simulate_error () || fclose (file) == EOF)
{
msg (SE, _("%s: Error closing temporary file: %s."),
return 1;
}
+/* Delete temporary file numbered FILE_IDX for XSRT. */
static void
remove_temp_file (struct external_sort *xsrt, int file_idx)
{
if (file_idx != -1)
{
- char temp_file[TEMP_FILE_NAME_SIZE];
- get_temp_file_name (xsrt, file_idx, temp_file);
+ char *temp_file = get_temp_file_name (xsrt, file_idx);
if (simulate_error () || remove (temp_file) != 0)
msg (SE, _("%s: Error removing temporary file: %s."),
temp_file, strerror (errno));
}
}
+/* Writes SIZE bytes from buffer DATA into FILE, which is
+ temporary file numbered FILE_IDX from XSRT. */
static int
write_temp_file (struct external_sort *xsrt, int file_idx,
FILE *file, const void *data, size_t size)
return 1;
else
{
- char temp_file[TEMP_FILE_NAME_SIZE];
- get_temp_file_name (xsrt, file_idx, temp_file);
+ char *temp_file = get_temp_file_name (xsrt, file_idx);
msg (SE, _("%s: Error writing temporary file: %s."),
temp_file, strerror (errno));
return 0;
}
}
+/* Reads SIZE bytes into buffer DATA into FILE, which is
+ temporary file numbered FILE_IDX from XSRT. */
static int
read_temp_file (struct external_sort *xsrt, int file_idx,
FILE *file, void *data, size_t size)
return 1;
else
{
- char temp_file[TEMP_FILE_NAME_SIZE];
- get_temp_file_name (xsrt, file_idx, temp_file);
+ char *temp_file = get_temp_file_name (xsrt, file_idx);
if (ferror (file))
msg (SE, _("%s: Error reading temporary file: %s."),
temp_file, strerror (errno));
struct case_list *record; /* Case data. */
};
+/* Represents a set of initial runs during an external sort. */
struct initial_run_state
{
struct external_sort *xsrt;
struct sort_cases_pgm *);
static int compare_record_run_minheap (const void *, const void *, void *);
+/* Writes initial runs for XSRT, sending them to a separate file
+ if SEPARATE is nonzero. */
static int
write_initial_runs (struct external_sort *xsrt, int separate)
{
/* Create case sink. */
if (!separate)
{
- if (vfm_sink)
+ if (vfm_sink != NULL && vfm_sink->class->destroy != NULL)
vfm_sink->class->destroy (vfm_sink);
vfm_sink = create_case_sink (&sort_sink_class, irs);
xsrt->scp->ref_cnt++;
/* Add a single case to an initial run. */
static void
-sort_sink_write (struct case_sink *sink, struct ccase *c)
+sort_sink_write (struct case_sink *sink, const struct ccase *c)
{
struct initial_run_state *irs = sink->aux;
struct record_run *new_record_run;
output_record (irs);
}
+/* Destroys the initial run state represented by IRS. */
static void
destroy_initial_run_state (struct initial_run_state *irs)
{
return 0;
}
+/* Compares record-run tuples A and B on run number first, then
+ on the current record according to SCP. */
static int
compare_record_run (const struct record_run *a,
const struct record_run *b,
return compare_record (a->record->c.data, b->record->c.data, scp);
}
+/* Compares record-run tuples A and B on run number first, then
+ on the current record according to SCP, but in descending
+ order. */
static int
compare_record_run_minheap (const void *a, const void *b, void *scp)
{
irs->output_file = NULL;
}
+/* Writes a record to the current initial run. */
static void
output_record (struct initial_run_state *irs)
{
irs->last_output = record_run->record;
}
+/* Gets a case from the free list in IRS. It is an error to call
+ this function if the free list is empty. */
static struct case_list *
grab_case (struct initial_run_state *irs)
{
return c;
}
+/* Returns C to the free list in IRS. */
static void
release_case (struct initial_run_state *irs, struct case_list *c)
{
\f
/* Merging. */
+/* State of merging initial runs. */
struct merge_state
{
struct external_sort *xsrt; /* External sort state. */
return 1;
}
\f
-static void
-sort_sink_destroy (struct case_sink *sink UNUSED)
-{
- assert (0);
-}
-
static struct case_source *
sort_sink_make_source (struct case_sink *sink)
{
struct initial_run_state *irs = sink->aux;
- return create_case_source (&sort_source_class, irs->xsrt->scp);
+ return create_case_source (&sort_source_class, default_dict,
+ irs->xsrt->scp);
}
const struct case_sink_class sort_sink_class =
"SORT CASES",
NULL,
sort_sink_write,
- sort_sink_destroy,
+ NULL,
sort_sink_make_source,
};
\f
+struct sort_source_aux
+ {
+ struct sort_cases_pgm *scp;
+ struct ccase *dst;
+ write_case_func *write_case;
+ write_case_data wc_data;
+ };
+
+/* Passes C to the write_case function. */
+static int
+sort_source_read_helper (const struct ccase *src, void *aux_)
+{
+ struct sort_source_aux *aux = aux_;
+
+ memcpy (aux->dst, src, aux->scp->case_size);
+ return aux->write_case (aux->wc_data);
+}
+
/* Reads all the records from the source stream and passes them
to write_case(). */
static void
sort_source_read (struct case_source *source,
+ struct ccase *c,
write_case_func *write_case, write_case_data wc_data)
{
struct sort_cases_pgm *scp = source->aux;
+ struct sort_source_aux aux;
+
+ aux.scp = scp;
+ aux.dst = c;
+ aux.write_case = write_case;
+ aux.wc_data = wc_data;
- read_sort_output (scp, write_case, wc_data);
+ read_sort_output (scp, sort_source_read_helper, &aux);
}
-void read_internal_sort_output (struct internal_sort *isrt,
- write_case_func *write_case,
- write_case_data wc_data);
-void read_external_sort_output (struct external_sort *xsrt,
- write_case_func *write_case,
- write_case_data wc_data);
+static void read_internal_sort_output (struct internal_sort *isrt,
+ read_sort_output_func *, void *aux);
+static void read_external_sort_output (struct external_sort *xsrt,
+ read_sort_output_func *, void *aux);
/* Reads all the records from the output stream and passes them to the
function provided, which must have an interface identical to
write_case(). */
void
read_sort_output (struct sort_cases_pgm *scp,
- write_case_func *write_case, write_case_data wc_data)
+ read_sort_output_func *output_func, void *aux)
{
assert ((scp->isrt != NULL) + (scp->xsrt != NULL) <= 1);
if (scp->isrt != NULL)
- read_internal_sort_output (scp->isrt, write_case, wc_data);
+ read_internal_sort_output (scp->isrt, output_func, aux);
else if (scp->xsrt != NULL)
- read_external_sort_output (scp->xsrt, write_case, wc_data);
+ read_external_sort_output (scp->xsrt, output_func, aux);
else
{
/* No results. Probably an external sort that failed. */
}
}
-void
+static void
read_internal_sort_output (struct internal_sort *isrt,
- write_case_func *write_case,
- write_case_data wc_data)
+ read_sort_output_func *output_func,
+ void *aux)
{
- struct ccase *save_temp_case = temp_case;
struct case_list **p;
- for (p = isrt->results; *p; p++)
- {
- temp_case = &(*p)->c;
- write_case (wc_data);
- }
+ for (p = isrt->results; *p; p++)
+ if (!output_func (&(*p)->c, aux))
+ break;
free (isrt->results);
-
- temp_case = save_temp_case;
}
-void
+static void
read_external_sort_output (struct external_sort *xsrt,
- write_case_func *write_case,
- write_case_data wc_data)
+ read_sort_output_func *output_func, void *aux)
{
FILE *file;
int file_idx;
size_t i;
+ struct ccase *c;
assert (xsrt->run_cnt == 1);
file_idx = xsrt->initial_runs[0].file_idx;
return;
}
+ c = xmalloc (xsrt->scp->case_size);
for (i = 0; i < xsrt->initial_runs[0].case_cnt; i++)
{
- if (!read_temp_file (xsrt, file_idx, file,
- temp_case, xsrt->case_size))
+ if (!read_temp_file (xsrt, file_idx, file, c, xsrt->scp->case_size))
{
err_failure ();
break;
}
- if (!write_case (wc_data))
+ if (!output_func (c, aux))
break;
}
+ free (c);
}
static void