/*   _____________________________________________________________
    /                                                             \ 
    | Source file:  code for ARRAY CLASSES of expansion library
    \_____________________________________________________________/

    Description:

    Notes:          This source is 100% public domain and might be
                    copied, modified and used by everyone as he likes it.

    Contents:

    Developers: ID: Name:
    =========== --- -----
                JTH Jürgen Thumm

  _____________________________________/VERSION HISTORY\____________________
 /Date_ Version Who What____________________________________________________\ 
 yymmdd ------- --- ---------------------------------------------------------
 940407 0.90    JTH created
 941025             local atst() now also checks for NULL pointers
 941026             MemArray: quick fix for 64k problem with WIN16: index
                              limited to 32k. if(!aadr) test.
 941205             Buffer::size(0) support. ~Array now does size(0)
                    on it's gbuf when last instance is deleted.
*/

#   include "array.h"

//|| Buffer    

Buffer::Buffer(size_t initsize)
{
    isize   = 0;
    imem    = 0;
    adr     = 0;

    if(initsize)    size(initsize);     // if size given, do re-size
}

Buffer::~Buffer()
{
    if(isize && imem)   delete [] imem; imem=0; adr=0;
}

bool    Buffer::testhit()
{
    if(     isize
        &&  imem
        &&  (       imem[BUF_SA_SIZE-1] != 0xFF
                ||  imem[isize]     != 0xFF )   )
    {
        perr("Buffer %lx : damaged from writing - dump follows", this);
        perr(" [first and last %d bytes are private class data]",
            BUF_SA_SIZE);
        explib_root.hexdump(imem, isize + BUF_SA_SIZE * 2);

        return  1;
    }

    return  0;
}

size_t  Buffer::size(size_t newsize)
{
    if(newsize != -1)
    {
        size_t  osize = isize;  // remember old size
        uchar*  omem  = imem;   // and memory

        if(     testhit()
            ||  !newsize
            ||  !(imem = new uchar[newsize + BUF_SA_SIZE * 2])
          )
        {
            isize   = 0;
            adr     = 0;
        }
        else
        {
            isize   = newsize;
            imem[BUF_SA_SIZE-1] = 0xFF;
            imem[isize]         = 0xFF;
            adr     = imem + BUF_SA_SIZE;
        }

        //  if there was old mem

        if(osize && omem)
        {
            if(isize)
                //  copy old data to new mem
                memcpy(adr, omem + BUF_SA_SIZE, min(osize, isize));

            //  free old mem

            delete [] omem;
        }
    }

    return  isize;
}

//|| BaseArray 

//  atst() macro localization:

#define tmp0304941625   atst
#undef  atst
#define atst(x,r)           \
        if( !x ||           \
    (tadr && explib_root.MemTracker.TestAdr(x,__FILE__,__LINE__))   \
          ) return r

BaseArray::BaseArray()  {idx=0; tadr=1;}
BaseArray::~BaseArray() {}

size_t  BaseArray::index(size_t newidx)
{_p (newidx, idx, lsize, (ulong)this);

    if(newidx != -1)
        if(newidx <= lsize)
            idx = newidx;
        else
            idx = -1;   // set to invalid

    return  idx;
}

size_t BaseArray::get(size_t start, void* adr, size_t maxsize)
{_p (start, (ulong)adr, maxsize, (ulong)this);

 size_t done=0;
 uchar* uadr=(uchar*)adr;

    if(index(start) != start)   return 0;

    while(maxsize-- && (!error() && !eod()))
    {
        getchr(*uadr++);    done++;
    }

    return  done;
}

size_t BaseArray::put(size_t start, void* adr, size_t maxsize)
{_p (start, (ulong)adr, maxsize, (ulong)this);

 size_t done=0;
 uchar* uadr=(uchar*)adr;

    if(index(start) != start)   return 0;

    ifn(maxsize)    return  0;

    while(maxsize-- && !error())
    {
        putchr(*uadr++);    done++;
    }

    return  done;
}

size_t BaseArray::size()
{
    return  lsize;
}

//|| MemArray  

MemArray::MemArray()    {   dead=0; aadr=0; asize=0; lsize=0;   }
MemArray::~MemArray()   {   if(aadr) delete [] aadr; aadr=0;    }

bool    MemArray::eod()     {   return (idx >= lsize) ? 1 : 0;  }
rcode   MemArray::error()   {   return OK;  }

rcode   MemArray::getchr(uchar& x)
{
    _p  ((ulong)aadr, idx, (ulong)&x, -1);

#ifdef  WIN16
    idx &= 32767U;  // quick fix for 64k problem
#endif

    if(idx < lsize)
    {
        if(!aadr)   return FAILED;
        atst(&aadr[idx], FAILED);

        x = aadr[idx++];
        return OK;
    }

    return (idx > lsize) ? NOTAVAIL : EOD;
}

rcode   MemArray::putchr(uchar x)
{_p ((ulong)aadr, idx, x, lsize);

#ifdef  WIN16
    idx &= 32767U;  // quick fix for 64k problem
#endif

    if(idx == lsize)
    {
        lsize++;

        while(lsize >= asize)
        {
         uchar* oadr;
         size_t osize;

            oadr     =  aadr;
            osize    =  asize;

            asize   +=  256;
            ifn(aadr =  new uchar[asize])
                {
                    MemErr(104941933);
                    if(oadr) delete [] oadr;
                    return MEMORY;
                }

            if(oadr)
            {
                memcpy(aadr, oadr, osize);
                delete  [] oadr;
            }
        }
    }

    if(idx < lsize)
    {
        if(!aadr)   return FAILED;
        atst(&aadr[idx], FAILED);

        aadr[idx++] = x;
        return OK;
    }

    return  NOTAVAIL;
}

//|| FileArray 

bool    FileArray::eod()    {   return feof(f)   ?  1 :  0; }
rcode   FileArray::error()  {   return ferror(f) ? IO : OK; }

FileArray::FileArray(char* filename)
{_
 long posn;

    dead = 0;

    ifn(f = fopen(filename, "r+b")) // r+b: read & change binary file
    {
        ifn(f = fopen(filename, "w+b")) // create new file

            dead = 1;   // both opening tries failed

        lsize = 0;
    }
    else
    {
        fseek(f, 0, SEEK_END);
        posn = ftell(f);

        if(posn < 0)
            lsize   = 0;
        else
            lsize   = posn;

        fseek(f, 0, SEEK_SET);
    }

    fidx    = 0;
}

FileArray::~FileArray()
{_
    if(f)   fclose(f);  f=0;
}

rcode   FileArray::getchr(uchar& x)
{_p (fidx, idx, (ulong)&x, (ulong)f);

 int i;

    if(fidx != idx)
        if(fseek(f, idx, SEEK_SET))
            return  NOTAVAIL;
        else
            fidx = idx;

    if((i = fgetc(f)) != EOF)
        {   x = (uchar)i;   fidx = ++idx;   return OK;  }

    return  IO;
}
rcode   FileArray::putchr(uchar x)
{_p (fidx, idx, x, (ulong)f);

    if(fidx != idx)
        if(fseek(f, idx, SEEK_SET))
            return  NOTAVAIL;
        else
            fidx = idx;

    if(fputc(x, f) != EOF)
        {   fidx = ++idx;   return  OK; }

    return  IO;
}

//|| Array      

long    Array::instcnt = 0;

Buffer  Array::gbuf(0);
// size stays 0 until first Array instance created
// when all instances are deleted, size is re-set to 0

bool    Array::eod()    {   return ba->eod();   }
rcode   Array::error()  {   return ba->error(); }

/*
char    operator << ( char& l,  Array& r)   {   return(l = r.getb());   }
uchar   operator << ( uchar& l, Array& r)   {   return(l = r.getb());   }
short   operator << (short& l,  Array& r)   {   return(l = r.getw());   }
ushort  operator << (ushort& l, Array& r)   {   return(l = r.getw());   }
*/
//uint  operator << (  uint& l, Array& r)   {   return(l = r.geti());   }
//ulong operator << ( ulong& l, Array& r)   {   return(l = r.getl());   }
//int   operator << (  int& l,  Array& r)   {   return(l = r.geti());   }
//long  operator << ( long& l,  Array& r)   {   return(l = r.getl());   }

Array::Array(char* filename)
{_
    instcnt++;

    dead    = 0;
    tadr    = 1;

    if(filename)
        ba  = new FileArray(filename);
    else
        ba  = new MemArray;

    if(!ba || ba->dead)
        dead = 1;
    else
    {
        // if global buffer size wasn't set yet, do so:

        ifn(gbuf.size())    gbuf.size(1024);
    }
}

Array::~Array()
{_
    if(ba)  delete ba;  ba=0;

    if(--instcnt <= 0)
    {
        instcnt = 0;

        gbuf.size(0);   // free buffer mem
    }
}

//  INDEX selection

size_t  Array::index(size_t newidx)
{_  ifn(ba) {SysErr(704941931); return -1;}

    return  ba->index(newidx);
}

size_t  Array::size()
{
    return  ba->size();
}

Array& Array::operator [] (size_t idx)
{_
    index(idx);
    return  *this;
}

//  MEMORY block access

size_t Array::get(void* x, size_t size)
{_  ifn(ba) {SysErr(804941520); return 0;}

    return  ba->get(ba->index(), x, size);
}

size_t Array::put(void* x, size_t size)
{_  ifn(ba) {SysErr(804941521); return 0;}

    return  ba->put(ba->index(), x, size);
}

//  BYTE access

rcode Array::putb(uchar x)
{_
    return  ba->putchr(x);
}

rcode Array::getb(uchar& x)
{_
    return  ba->getchr(x);
}

//  SHORT (WORD) access

rcode Array::putw(ushort x)
{_
    if(ba->put(ba->index(), &x, sizeof(ushort)) != sizeof(ushort))
        return  FAILED;
    else
        return  OK;
}

rcode Array::getw(ushort& x)
{_
    if(ba->get(ba->index(), &x, sizeof(ushort)) != sizeof(ushort))
        return  FAILED;
    else
        return  OK;
}

/*
rcode Array::puti(uint x)
{_
    scalb.ui = x;
    return  ba->put(ba->index(), &scalb.ui, sizeof(uint));
}

uint Array::geti()
{_
    if(ba->get(ba->index(), &scalb.ui, sizeof(uint)))   return -1;
    return  scalb.ui;
}

//  LONG access

rcode Array::putl(ulong x)
{_
    scalb.ul = x;
    return  ba->put(ba->index(), &scalb.ul, sizeof(ulong));
}

ulong Array::getl()
{_
    if(ba->get(ba->index(), &scalb.ul, sizeof(ulong)))  return -1;
    return  scalb.ul;
}
*/

//|| gets       

char*   Array::gets(char* ostr, size_t maxsize) // get a string
{_p ((ulong)ostr, maxsize, (ulong)this, -1);

 char*  str=ostr;
 uchar  x;

    if(eod())   return  0;

    while(maxsize > 1 && (!error() && !eod()))
    {
        ba->getchr(x);  *str++  = x;    maxsize--;

        if(x=='\n') break;
    }

    *str = 0;

    return  error() ? NULL : ostr;
}

//|| printf    

char*   Array::printf(char *format, ...)
{_
 va_list ap;
 size_t  len;

  va_start(ap, format);
    len = vsprintf(gbuf.adr, format, ap);
    gbuf.testhit();
  va_end(ap);

    if(put(gbuf.adr, len) == len)   // if updated mem

        if(ibuf.size(len) == len)   // and resized ibuf

            memcpy(ibuf.adr, gbuf.adr, len);    // update ibuf

   return   ibuf.adr;
}

//|| ScalPipe 

rcode   ScalarPipe::putb(uchar x)
{
 rcode  rc=OK;

    if( wdi.cnt.b && wdi.dat.b == x )
    {
        // count equal bytes:
        wdi.cnt.b++;

        // counter overflow?
        if(wdi.cnt.b >= SHRT_MAX - 10)  // '10' for tolerance
        {
            rc = flush();
        }
    }
    else
    {
        ifn(rc = flush())
        {
            wdi.cnt.b   = 1;
            wdi.dat.b   = x;
        }
    }

    return  rc;
}

rcode   ScalarPipe::flush()
{
 rcode  rc=OK;

    //  are there unwritten buffer data?

    if(wdi.cnt.b)
    {
        ifn(rc = Array::putw(wdi.cnt.b))
                 Array::putb(wdi.dat.b);

        wdi.cnt.b   = 0;
    }

    return  rc;
}

rcode   ScalarPipe::getb(uchar& x)
{
 rcode  rc=OK;
 ushort us;

    ifn(rdi.cnt.b)
    {
        // fill read data info:

        if(rc = Array::getw(us))    return rc;
        rdi.cnt.b   = us;
        if(rc = Array::getb(rdi.dat.b)) return rc;
    }

    // reconstruct stream from delta infos:

    x   = rdi.dat.b;

    rdi.cnt.b--;

    return  rc;
}

size_t  ScalarPipe::index(size_t newidx)
{
    if(newidx != -1)
    {
        //  if there's unwritten buffer data,
        //  write it before changing index:

        if(flush()) return -1;
    }

    return  Array::index(newidx);
}

//  atst() macro de-localization:

#undef  atst
#define atst    tmp0304941625
#undef  tmp0304941625
