/***************************************************************************
 *                                                                         *
 *   DYNPOOL.C                                                             *
 *                                                                         *
 *   Copyright (c) 1994-1997 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   These routines support use of dynamic pools of memory areas.  Memory  *
 *   areas within a pool can be reserved and unreserved as needed.  To     *
 *   conserve memory, areas can be swapped to disk as demand increases.    *
 *                                                                         *
 *                                               - C. Robert  10/18/94     *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"

#define FILREV "$Revision: 5 $"

static
INT poolsarr=-1;                   /* dynarr handle to array of pools      */

#define PINCSZ 20                  /* increment size for pool array        */

struct poolinf {                   /* info for each dynamic memory pool    */
     SHORT *areas;                 /*   each is idx into areamem or #define*/
     VOID *areamem;                /*   alcblok()'d array of area memory   */
     UINT areasiz;                 /*   size of each area within pool      */
     SHORT nareas;                 /*   number of areas within pool        */
     SHORT ninmem;                 /*   number of areas in memory at once  */
     ULONG sfoff;                  /*   swap file offset for this pool     */
};

struct ameminf {                   /* structure for area memory            */
     INT owner;                    /*   idx into areas of cur owner, or -1 */
     ULONG lused;                  /*   pseudo-time of last use            */
     CHAR mem[1];                  /*   variable-length memory area        */
};

static
ULONG curptim=0L;                  /* current pseudo-time for stamping     */

                                   /* when ! idx into areamem, area values */
#define JNRSVD -1                  /*   area in use, just now reserved     */
#define ONDISK -2                  /*   area in use, but swapped to disk   */
#define NOTINU -10                 /*   area not currently in use          */

static
CHAR sfile[GCMAXPTH]="";           /* swap file for on-disk memory areas   */
static
FILE *sfp=NULL;                    /* swap file pointer                    */

static VOID seekto(ULONG offset);

VOID
inipool(                           /* initialize dynamic pool API          */
CHAR *swpfil)                      /*   swap file for on-disk memory areas */
{
     stlcpy(sfile,swpfil,GCMAXPTH);
     unlink(sfile);
}

INT                                /*   pool handle                        */
newpool(                           /* create a new pool of memory areas    */
UINT areasiz,                      /*   size of each area within pool      */
INT nareas,                        /*   number of areas within pool        */
INT ninmem)                        /*   number of areas in memory at once  */
{
     struct poolinf newpool;
     INT i;
     struct poolinf *pool;
     struct ameminf *amptr;

     if (poolsarr == -1) {
          poolsarr=newarr(PINCSZ,sizeof(struct poolinf));
     }
     newpool.areas=alcmem(nareas*sizeof(SHORT));
     for (i=0 ; i < nareas ; i++) {
          newpool.areas[i]=NOTINU;
     }
#ifdef GCWINNT
     ninmem=nareas;                // prevent swapfile under Windows NT
#endif                             // GCWINNT
     newpool.areamem=alcblok((USHORT)ninmem,
                             (USHORT)((sizeof(struct ameminf)-1)+areasiz));
     for (i=0 ; i < ninmem ; i++) {
          amptr=ptrblok(newpool.areamem,(USHORT)i);
          amptr->owner=-1;
     }
     newpool.areasiz=areasiz;
     newpool.nareas=nareas;
     newpool.ninmem=ninmem;
     if (nareas > ninmem) {
          if (sfp == NULL && (sfp=fopen(sfile,FOPWRB)) == NULL) {
               catastro("CAN'T OPEN \"%s\" FOR AREA SWAPPING!",sfile);
          }
          newpool.sfoff=0L;
          for (i=0 ; i < ninarr(poolsarr) ; i++) {
               pool=arrelem(poolsarr,i);
               if (pool->nareas > pool->ninmem) {
                    newpool.sfoff+=((LONG)pool->nareas*pool->areasiz);
               }
          }
          if (fseek(sfp,newpool.sfoff,SEEK_SET) != 0) {
               catastro("CAN'T SEEK TO NEW POOL'S SWAP AREA (%s)!",sfile);
          }
          amptr=ptrblok(newpool.areamem,(USHORT)0);
          for (i=0 ; i < nareas ; i++) {
               if (fwrite(amptr->mem,areasiz,1,sfp) != 1) {
                    catastro("CAN'T EXPAND POOL AREA SWAP FILE (%s)!",sfile);
               }
          }
          fclose(sfp);
          if ((sfp=fopen(sfile,FOPRWB)) == NULL) {
               catastro("CAN'T RE-OPEN \"%s\" FOR AREA SWAPPING!",sfile);
          }
     }
     add2arr(poolsarr,&newpool);
     return(ninarr(poolsarr)-1);
}

INT                                /*   handle to area, or -1 if none avail*/
rsvarea(                           /* reserve a memory area within pool    */
INT poolhdl)                       /*   handle to the pool (from newpool())*/
{
     INT i;
     struct poolinf *pool;

     pool=arrelem(poolsarr,poolhdl);
     for (i=0 ; i < pool->nareas ; i++) {
          if (pool->areas[i] == NOTINU) {
               pool->areas[i]=JNRSVD;
               return(i);
          }
     }
     return(-1);
}

VOID *                             /*   returns ptr to reserved mem area   */
areaptr(                           /* get pointer to a reserved mem area   */
INT poolhdl,                       /*   handle to the pool (from newpool())*/
INT areahdl)                       /*   handle to the area (from rsvarea())*/
{
     INT i,lruidx;
     struct poolinf *pool;
     struct ameminf *amptr;
     ULONG lrutim;
     GBOOL swapin;

     pool=arrelem(poolsarr,poolhdl);
     if (pool->areas[areahdl] == NOTINU) {
          catastro("ATTEMPTED TO GET POINTER TO UNRESERVED AREA!");
     }
     curptim++;
     if (pool->areas[areahdl] >= 0) {
          amptr=ptrblok(pool->areamem,(USHORT)pool->areas[areahdl]);
          amptr->lused=curptim;
          return(amptr->mem);
     }
     swapin=(pool->areas[areahdl] == ONDISK);
     lrutim=0L;
     lruidx=0;
     for (i=0 ; i < pool->ninmem ; i++) {
          amptr=ptrblok(pool->areamem,(USHORT)i);
          if (amptr->owner == -1) {
               break;
          }
          if (curptim-amptr->lused > lrutim) {
               lrutim=curptim-amptr->lused;
               lruidx=i;
          }
     }
     if (i == pool->ninmem) {
          amptr=ptrblok(pool->areamem,(USHORT)lruidx);
          seekto(pool->sfoff+(LONG)amptr->owner*pool->areasiz);
          if (fwrite(amptr->mem,pool->areasiz,1,sfp) != 1) {
               catastro("CAN'T SWAP OUT AREA MEMORY!");
          }
          pool->areas[amptr->owner]=ONDISK;
          pool->areas[areahdl]=lruidx;
     }
     else {
          pool->areas[areahdl]=i;
     }
     amptr->owner=areahdl;
     amptr->lused=curptim;
     if (swapin) {
          seekto(pool->sfoff+(LONG)areahdl*pool->areasiz);
          if (fread(amptr->mem,pool->areasiz,1,sfp) != 1) {
               catastro("CAN'T SWAP IN AREA MEMORY!");
          }
     }
     else {
          setmem(amptr->mem,pool->areasiz,0);
     }
     return(amptr->mem);
}

VOID
unrarea(                           /* unreserve a memory area within pool  */
INT poolhdl,                       /*   handle to the pool (from newpool())*/
INT areahdl)                       /*   handle to the area (from rsvarea())*/
{
     struct poolinf *pool;
     struct ameminf *amptr;

     pool=arrelem(poolsarr,poolhdl);
     ASSERT(pool->areas[areahdl] != NOTINU);
     if (pool->areas[areahdl] >= 0) {
          amptr=ptrblok(pool->areamem,(USHORT)pool->areas[areahdl]);
          amptr->owner=-1;
     }
     pool->areas[areahdl]=NOTINU;
}

VOID
clspool(VOID)                      /* close down dynamic pool API          */
{
     if (sfp != NULL) {
          fclose(sfp);
          sfp=NULL;
          unlink(sfile);
     }
}

static VOID
seekto(                            /* seek to offset in area swap file     */
ULONG offset)                      /*   offset to seek to                  */
{
     if (fseek(sfp,offset,SEEK_SET) != 0) {
          catastro("CAN'T SEEK IN AREA SWAP FILE!");
     }
}

