#include "sort.h"
#include "fsort.h"
__RCSID("$NetBSD: msort.c,v 1.31 2016/06/01 02:37:55 kre Exp $");
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <util.h>
#define DELETE (1)
typedef struct mfile {
FILE *fp;
get_func_t get;
RECHEADER *rec;
u_char *end;
} MFILE;
static int cmp(RECHEADER *, RECHEADER *);
static int insert(struct mfile **, struct mfile *, int, int);
static void merge_sort_fstack(FILE *, put_func_t, struct field *);
#define MERGE_FNUM 16
static struct mfile fstack[MERGE_FNUM];
static struct mfile fstack_1[MERGE_FNUM];
static struct mfile fstack_2[MERGE_FNUM];
static int fstack_count, fstack_1_count, fstack_2_count;
void
save_for_merge(FILE *fp, get_func_t get, struct field *ftbl)
{
FILE *mfp, *mfp1, *mfp2;
if (fstack_count == MERGE_FNUM) {
mfp = ftmp();
merge_sort_fstack(mfp, putrec, ftbl);
if (fstack_1_count == MERGE_FNUM) {
mfp1 = ftmp();
memcpy(fstack, fstack_1, sizeof fstack);
merge_sort_fstack(mfp1, putrec, ftbl);
if (fstack_2_count == MERGE_FNUM) {
mfp2 = ftmp();
memcpy(fstack, fstack_2, sizeof fstack);
merge_sort_fstack(mfp2, putrec, ftbl);
fstack_2[0].fp = mfp2;
fstack_2_count = 1;
}
fstack_2[fstack_2_count].fp = mfp1;
fstack_2[fstack_2_count].get = geteasy;
fstack_2_count++;
fstack_1_count = 0;
}
fstack_1[fstack_1_count].fp = mfp;
fstack_1[fstack_1_count].get = geteasy;
fstack_1_count++;
fstack_count = 0;
}
fstack[fstack_count].fp = fp;
fstack[fstack_count++].get = get;
}
void
fmerge(struct filelist *filelist, int nfiles, FILE *outfp, struct field *ftbl)
{
get_func_t get = SINGL_FLD ? makeline : makekey;
FILE *fp;
int i;
for (i = 0; i < nfiles; i++) {
fp = fopen(filelist->names[i], "r");
if (fp == NULL)
err(2, "%s", filelist->names[i]);
save_for_merge(fp, get, ftbl);
}
merge_sort(outfp, putline, ftbl);
}
void
merge_sort(FILE *outfp, put_func_t put, struct field *ftbl)
{
int count = fstack_1_count + fstack_2_count;
FILE *mfp;
int i;
if (count == 0) {
merge_sort_fstack(outfp, put, ftbl);
return;
}
count += fstack_count;
for (;;) {
i = count;
if (i > MERGE_FNUM)
i = MERGE_FNUM;
while (fstack_count > 0)
fstack[--i] = fstack[--fstack_count];
while (i > 0 && fstack_1_count > 0)
fstack[--i] = fstack_1[--fstack_1_count];
while (i > 0)
fstack[--i] = fstack_2[--fstack_2_count];
if (count <= MERGE_FNUM) {
fstack_count = count;
merge_sort_fstack(outfp, put, ftbl);
return;
}
mfp = ftmp();
fstack_count = count > MERGE_FNUM ? MERGE_FNUM : count;
merge_sort_fstack(mfp, putrec, ftbl);
fstack[0].fp = mfp;
fstack[0].get = geteasy;
fstack_count = 1;
count -= MERGE_FNUM - 1;
}
}
static void
merge_sort_fstack(FILE *outfp, put_func_t put, struct field *ftbl)
{
struct mfile *flistb[MERGE_FNUM], **flist = flistb, *cfile;
RECHEADER *new_rec;
u_char *new_end;
void *tmp;
int c, i, nfiles;
size_t sz;
for (nfiles = i = 0; i < fstack_count; i++) {
cfile = &fstack[i];
if (cfile->rec == NULL) {
cfile->rec = allocrec(NULL, DEFLLEN);
cfile->end = (u_char *)cfile->rec + DEFLLEN;
}
rewind(cfile->fp);
for (;;) {
c = cfile->get(cfile->fp, cfile->rec, cfile->end, ftbl);
if (c == EOF)
break;
if (c == BUFFEND) {
sz = (cfile->end - (u_char *)cfile->rec) * 2;
cfile->rec = allocrec(cfile->rec, sz);
cfile->end = (u_char *)cfile->rec + sz;
continue;
}
if (nfiles != 0) {
if (insert(flist, cfile, nfiles, !DELETE))
continue;
} else
flist[0] = cfile;
nfiles++;
break;
}
}
if (nfiles == 0)
return;
new_rec = allocrec(NULL, DEFLLEN);
new_end = (u_char *)new_rec + DEFLLEN;
for (;;) {
cfile = flist[0];
c = cfile->get(cfile->fp, new_rec, new_end, ftbl);
if (c == EOF) {
put(cfile->rec, outfp);
if (--nfiles == 0)
break;
flist++;
continue;
}
if (c == BUFFEND) {
sz = (new_end - (u_char *)new_rec) * 2;
new_rec = allocrec(new_rec, sz);
new_end = (u_char *)new_rec +sz;
continue;
}
tmp = cfile->rec;
cfile->rec = new_rec;
new_rec = tmp;
tmp = cfile->end;
cfile->end = new_end;
new_end = tmp;
c = insert(flist, cfile, nfiles, DELETE);
if (c != 0 || (UNIQUE && cfile == flist[0]
&& cmp(new_rec, cfile->rec) == 0)) {
tmp = cfile->rec;
cfile->rec = new_rec;
new_rec = tmp;
tmp = cfile->end;
cfile->end = new_end;
new_end = tmp;
continue;
}
put(new_rec, outfp);
}
free(new_rec);
}
static int
insert(struct mfile **flist, struct mfile *rec, int ttop, int delete)
{
int mid, top = ttop, bot = 0, cmpv = 1;
for (mid = top / 2; bot + 1 != top; mid = (bot + top) / 2) {
cmpv = cmp(rec->rec, flist[mid]->rec);
if (cmpv == 0 ) {
if (UNIQUE)
return 1;
cmpv = rec < flist[mid] ? -1 : 1;
if (REVERSE)
cmpv = -cmpv;
}
if (cmpv < 0)
top = mid;
else
bot = mid;
}
if (delete) {
if (bot != 0) {
memmove(flist, flist + 1, bot * sizeof(MFILE *));
flist[bot] = rec;
}
return 0;
}
if (bot == 0 && cmpv != 0) {
cmpv = cmp(rec->rec, flist[0]->rec);
if (cmpv == 0 && UNIQUE)
return 1;
if (cmpv < 0)
bot = -1;
}
bot++;
memmove(flist + bot + 1, flist + bot, (ttop - bot) * sizeof(MFILE *));
flist[bot] = rec;
return 0;
}
void
order(struct filelist *filelist, struct field *ftbl, int quiet)
{
get_func_t get = SINGL_FLD ? makeline : makekey;
RECHEADER *crec, *prec, *trec;
u_char *crec_end, *prec_end, *trec_end;
FILE *fp;
int c;
fp = fopen(filelist->names[0], "r");
if (fp == NULL)
err(2, "%s", filelist->names[0]);
crec = malloc(offsetof(RECHEADER, data[DEFLLEN]));
crec_end = crec->data + DEFLLEN;
prec = malloc(offsetof(RECHEADER, data[DEFLLEN]));
prec_end = prec->data + DEFLLEN;
if (get(fp, prec, prec_end, ftbl) != 0)
exit(0);
while (get(fp, crec, crec_end, ftbl) == 0) {
if (0 < (c = cmp(prec, crec))) {
if (quiet)
exit(1);
crec->data[crec->length-1] = 0;
errx(1, "found disorder: %s", crec->data+crec->offset);
}
if (UNIQUE && !c) {
if (quiet)
exit(1);
crec->data[crec->length-1] = 0;
errx(1, "found non-uniqueness: %s",
crec->data+crec->offset);
}
trec = prec;
prec = crec;
crec = trec;
trec_end = prec_end;
prec_end = crec_end;
crec_end = trec_end;
}
exit(0);
}
static int
cmp(RECHEADER *rec1, RECHEADER *rec2)
{
int len;
int r;
len = min(rec1->keylen, rec2->keylen);
r = memcmp(rec1->data, rec2->data, len);
if (r == 0)
r = rec1->keylen - rec2->keylen;
if (REVERSE)
r = -r;
return r;
}