Listing 3: MergeFile and PreSort implementations

/*
 *  mfile.c
 */

#include <assert.h>
#include <mem.h>        //memcpy()
#include "mfile.h"

MergeFile::MergeFile( const int wd,
     const size_t blen, const char *mode ) :
    _width(wd), _fn(0), _tmpfile(1), _holdtmps(0),
    _actual_runs(0), _null_runs(0), _eofile(0),
    _items(0), _fmode(mode), _maxitems(blen/wd)
{   _pbuf = new char[blen];
    _resetp();
    _fn = tmpnam(_tmpf);    //temp file name
    Mf = new ffstream( _fn, _fmode );
}

MergeFile::~MergeFile()
{   if( _pbuf )
        delete _pbuf;
    if( _tmpfile )
    {   delete Mf;   //_flushb() redundant
        if( !_holdtmps )
            remove( _fn );    //zap temp file
    }
}

void MergeFile::AttachStream( ffstream * fs )
{   _flushb();      //resets pointers
    if( _tmpfile )   //remove temporary file
    {   delete Mf;
        remove( _fn );
    }
    Mf = fs;         //attach new stream
    _fn = 0;         //filename unknown
    _tmpfile = 0;  //new file is non-temp
}

int MergeFile::RenameOutput( const char *newname )
{   _flushb();
    Mf->close();
    int bad = rename( _fn, newname );   // OK == 0
    if( bad )
        _fn = tmpnam(_tmpf);   // new temp file name
    Mf->open( _fn, _fmode );   // new temp file
    return bad;  // -1 if "newname" already exists
}

PCDATA MergeFile::_getb()  //get item at _gpos ...
{   PCDATA tmp = _gpos;
    _advp( _gpos );
    _items--;
    return tmp;
}

PCDATA MergeFile::Get() //get next buffered item...
{   if( _is_empty() )
        _fillb();
    return _eofile ? PCDATA(0) : _getb();
}

PCDATA MergeFile::Nextg() //return next Get() item
{   if( _is_empty() )
        _fillb();
    return _eofile ? PCDATA(0) : _gpos;
}

PCDATA MergeFile::Put( PCDATA obj )
{   if( obj )     //place obj at _ppos
    {   if( _items >= _maxitems )
            _flushb();
        memcpy( _ppos, obj, _width );
        _advp( _ppos );
        _items++;
        if( _items > 1 )
            _advp( _lpos );
    }
    return obj;
}

void MergeFile::_flushb()    //flush buffer
{   if( !_is_empty() )
    {   size_t n = Mf->write( _gpos, _width, _items );
        assert( n == _items );   //out of disk space?
    }
    _eofile = _items = 0;
    _resetp();
}

void MergeFile::_fillb()   //fill buffer
{
  if( _eofile ) return;
    _items = Mf->read( _pbuf, _width, _maxitems );
    _eofile = !_items;
    _resetp();
}

void MergeFile::ResetIOMode( io_mode mode )
{   _flushb();
    if( mode == in )     //input
        Mf->rewind();   //Get/Nextg calls _fillb()
    else if( _tmpfile && (mode == out) )
    {   Mf->close();    //truncate file for output
        Mf->open( _fn, _fmode );
        _actual_runs = _null_runs = 0;
    }
}

PreSort::PreSort( MergeFile& src,
                  const InternalSort& insort )
{   Sf = &src;
    Ins = &insort;
    _psbuf = PPCDATA( new PDATA[ Sf->_maxitems ] );
}

size_t PreSort::GenerateRun( MergeFile *dest,
                             int& comp        )
{   Sf->_fillb();
    if( EndOfFile() ) return 0;    //empty
    _setsbuf();
    size_t items = Sf->Items();
    for( size_t i = items; i; i-- )
        _putsbuf( Sf->_getb() );   //fill sort array
    assert( Sf->Items() == 0 );    //got 'em all?
    Ins->Sort( _psbuf, items );    //sort 'em
    PCDATA lp = dest->Lastp();     //OK even if empty
    comp = Ins->Compare( &lp, (PPCDATA) _psbuf );
    _setsbuf();
    for( size_t j = items; j; j-- )
        dest->Put( _getsbuf() );  //output to dest
    return items;   //return #items in sorted run
}
//End of File