aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/tads/tads2/memory_cache_swap.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/glk/tads/tads2/memory_cache_swap.cpp')
-rw-r--r--engines/glk/tads/tads2/memory_cache_swap.cpp300
1 files changed, 300 insertions, 0 deletions
diff --git a/engines/glk/tads/tads2/memory_cache_swap.cpp b/engines/glk/tads/tads2/memory_cache_swap.cpp
index 114a7258e9..8d78892963 100644
--- a/engines/glk/tads/tads2/memory_cache_swap.cpp
+++ b/engines/glk/tads/tads2/memory_cache_swap.cpp
@@ -21,11 +21,311 @@
*/
#include "glk/tads/tads2/memory_cache_swap.h"
+#include "glk/tads/tads2/memory_cache.h"
+#include "glk/tads/tads2/memory_cache_heap.h"
+#include "glk/tads/tads2/error.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
+
+/* initialize swapper: allocate memory for swap page table */
+void mcsini(mcscxdef *ctx, mcmcx1def *gmemctx, ulong maxsiz,
+ osfildef *fp, char *swapfilename, errcxdef *errctx)
+{
+ uchar *p;
+
+ ctx->mcscxtab = (mcsdsdef **)0; /* anticipate failure */
+
+ /* allocate space from the low-level heap for page table and one page */
+ p = mchalo(errctx, ((MCSPAGETAB * sizeof(mcsdsdef *))
+ + (MCSPAGECNT * sizeof(mcsdsdef))), "mcsini");
+
+ /* set up the context with pointers to this chunk */
+ ctx->mcscxtab = (mcsdsdef **)p;
+ memset(p, 0, (size_t)(MCSPAGETAB * sizeof(mcsdsdef *)));
+ p += MCSPAGETAB * sizeof(mcsdsdef *);
+ ctx->mcscxtab[0] = (mcsdsdef *)p;
+
+ /* set up the rest of the context */
+ ctx->mcscxtop = (ulong)0;
+ ctx->mcscxmax = maxsiz;
+ ctx->mcscxmsg = 0;
+ ctx->mcscxfp = fp;
+ ctx->mcscxerr = errctx;
+ ctx->mcscxmem = gmemctx;
+
+ /*
+ * store the swap filename - make a copy so that the caller doesn't
+ * have to retain the original copy (in case it's on the stack)
+ */
+ if (swapfilename != 0)
+ {
+ ctx->mcscxfname = (char *)mchalo(errctx,
+ (strlen(swapfilename)+1),
+ "mcsini");
+ strcpy(ctx->mcscxfname, swapfilename);
+ }
+ else
+ ctx->mcscxfname = 0;
+}
+
+/* close the swapper */
+void mcsclose(mcscxdef *ctx)
+{
+ if (ctx->mcscxtab) mchfre(ctx->mcscxtab);
+}
+
+/*
+ * Attempt to compact the swap file when it grows too big. The segment
+ * descriptors are always allocated in increasing seek location within
+ * the swap file. To compress the file, make each descriptor's
+ * allocated size equal its used size for each in-use segment, and leave
+ * free segments at their allocated sizes.
+ */
+static void mcscompact(mcscxdef *ctx)
+{
+ char buf[512];
+ ulong max;
+ mcsseg cur_in;
+ mcsseg cur_out;
+ mcsdsdef *desc_in;
+ mcsdsdef *desc_out;
+ uint siz;
+ uint rdsiz;
+ ulong ptr_in;
+ ulong ptr_out;
+
+ max = 0; /* start at offset zero within file */
+ for (cur_in = cur_out = 0 ; cur_in < ctx->mcscxmsg ; ++cur_in)
+ {
+ desc_in = mcsdsc(ctx, cur_in);
+
+ /*
+ * If the present descriptor's address is wrong, and the swap
+ * segment is in use, move the swap segment. If it's not in
+ * use, we don't need to move it, because we're going to throw
+ * away the segment entirely.
+ */
+ if (desc_in->mcsdsptr != max
+ && (desc_in->mcsdsflg & MCSDSFINUSE))
+ {
+ /* ptr_in is the old location, ptr_out is the new location */
+ ptr_in = desc_in->mcsdsptr;
+ ptr_out = max;
+
+ /* copy through our buffer */
+ for (siz = desc_in->mcsdsosz ; siz ; siz -= rdsiz)
+ {
+ /* size is whole buffer, or last piece if smaller */
+ rdsiz = (siz > sizeof(buf) ? sizeof(buf) : siz);
+
+ /* seek to old location and get the piece */
+ osfseek(ctx->mcscxfp, ptr_in, OSFSK_SET);
+ (void)osfrb(ctx->mcscxfp, buf, (size_t)rdsiz);
+
+ /* seek to new location and write the piece */
+ osfseek(ctx->mcscxfp, ptr_out, OSFSK_SET);
+ (void)osfwb(ctx->mcscxfp, buf, (size_t)rdsiz);
+
+ /* adjust the pointers by the size copied */
+ ptr_in += rdsiz;
+ ptr_out += rdsiz;
+ }
+ }
+
+ /* adjust object descriptor to reflect new location */
+ desc_in->mcsdsptr = max;
+
+ /*
+ * Make current object's size exact if it's in use. If it's
+ * not in use, delete the segment altogether.
+ */
+ if (desc_in->mcsdsflg & MCSDSFINUSE)
+ {
+ desc_in->mcsdssiz = desc_in->mcsdsosz;
+ max += desc_in->mcsdssiz;
+
+ /* copy descriptor to correct position to close any holes */
+ if (cur_out != cur_in)
+ {
+ desc_out = mcsdsc(ctx, cur_out);
+ OSCPYSTRUCT(*desc_out, *desc_in);
+
+ /* we need to renumber the corresponding object as well */
+ mcmcsw(ctx->mcscxmem, (mcmon)desc_in->mcsdsobj,
+ cur_out, cur_in);
+ }
+
+ /* we actually wrote this one, so move output pointer */
+ ++cur_out;
+ }
+ else
+ {
+ /*
+ * We need to renumber the corresponding object so that it
+ * knows there is no swap segment for it any more.
+ */
+ mcmcsw(ctx->mcscxmem, (mcmon)desc_in->mcsdsobj,
+ MCSSEGINV, cur_in);
+ }
+ }
+
+ /*
+ * Adjust the top of the file for our new size, and add the savings
+ * into the available space counter. Also, adjust the total handle
+ * count to reflect any descriptors that we've deleted.
+ */
+ ctx->mcscxmax += (ctx->mcscxtop - max);
+ ctx->mcscxtop = max;
+ ctx->mcscxmsg = cur_out;
+}
+
+/* swap an object out to the swap file */
+mcsseg mcsout(mcscxdef *ctx, uint objid, uchar *ptr, ushort siz,
+ mcsseg oldseg, int dirty)
+{
+ mcsdsdef *desc;
+ mcsdsdef **pagep;
+ uint i;
+ uint j;
+ mcsseg min;
+ mcsseg cur;
+ ushort minsiz = 0;
+
+ IF_DEBUG(printf("<< mcsout: objid=%d, ptr=%lx, siz=%u, oldseg=%u >>\n",
+ objid, (unsigned long)ptr, siz, oldseg));
+
+ /* see if old segment can be reused */
+ if (oldseg != MCSSEGINV)
+ {
+ desc = mcsdsc(ctx, oldseg);
+ if (!(desc->mcsdsflg & MCSDSFINUSE) /* if old seg is not in use */
+ && desc->mcsdsobj == objid /* and it has same object */
+ && desc->mcsdssiz >= siz /* and it's still big enough */
+ && !dirty) /* and the object in memory hasn't been changed */
+ {
+ /* we can reuse the old segment without rewriting it */
+ desc->mcsdsflg |= MCSDSFINUSE; /* mark segment as in use */
+ return(oldseg);
+ }
+ }
+
+ /* look for the smallest unused segment big enough for this object */
+ for (cur = 0, min = MCSSEGINV, i = 0, pagep = ctx->mcscxtab
+ ; cur < ctx->mcscxmsg && i < MCSPAGETAB && *pagep ; ++pagep, ++i)
+ {
+ for (j = 0, desc = *pagep ; cur < ctx->mcscxmsg && j < MCSPAGECNT
+ ; ++desc, ++j, ++cur)
+ {
+ if (!(desc->mcsdsflg & MCSDSFINUSE)
+ && desc->mcsdssiz >= siz
+ && (min == MCSSEGINV || desc->mcsdssiz < minsiz))
+ {
+ min = cur;
+ minsiz = desc->mcsdssiz;
+ if (minsiz == siz) break; /* exact match - we're done */
+ }
+ }
+ /* quit if we found an exact match */
+ if (min != MCSSEGINV && minsiz == siz) break;
+ }
+
+ /* if we found nothing, allocate a new segment if possible */
+ if (min == MCSSEGINV)
+ {
+ if (siz > ctx->mcscxmax)
+ {
+ /* swap file is too big; compact it and try again */
+ mcscompact(ctx);
+ if (siz > ctx->mcscxmax)
+ errsig(ctx->mcscxerr, ERR_SWAPBIG);
+ }
+
+ min = ctx->mcscxmsg;
+ if ((min >> 8) >= MCSPAGETAB) /* exceeded pages in page table? */
+ errsig(ctx->mcscxerr, ERR_SWAPPG);
+
+ if (!ctx->mcscxtab[min >> 8]) /* haven't allocate page yet? */
+ {
+ ctx->mcscxtab[min >> 8] =
+ (mcsdsdef *)mchalo(ctx->mcscxerr,
+ (MCSPAGECNT * sizeof(mcsdsdef)),
+ "mcsout");
+ }
+
+ /* set up new descriptor */
+ desc = mcsdsc(ctx, min);
+ desc->mcsdsptr = ctx->mcscxtop;
+ desc->mcsdssiz = siz;
+ desc->mcsdsobj = objid;
+
+ /* write out the segment */
+ mcswrt(ctx, desc, ptr, siz);
+ desc->mcsdsflg = MCSDSFINUSE;
+
+ /* update context information to account for new segment */
+ ctx->mcscxtop += siz; /* add to top seek offset in file */
+ ctx->mcscxmax -= siz; /* take size out of quota */
+ ctx->mcscxmsg++; /* increment last segment allocated */
+
+ return(min);
+ }
+ else
+ {
+ desc = mcsdsc(ctx, min);
+ desc->mcsdsobj = objid;
+ mcswrt(ctx, desc, ptr, siz);
+ desc->mcsdsflg |= MCSDSFINUSE;
+
+ return(min);
+ }
+}
+
+void mcsin(mcscxdef *ctx, mcsseg seg, uchar *ptr, ushort siz)
+{
+ mcsdsdef *desc = mcsdsc(ctx, seg);
+
+ IF_DEBUG(printf("<< mcsin: seg=%u, ptr=%lx, siz=%d, objid=%u >>\n",
+ seg, (unsigned long)ptr, siz, desc->mcsdsobj));
+
+ assert(seg < ctx->mcscxmsg);
+
+ /* can only swap in as much as we wrote */
+ if (desc->mcsdsosz < siz) siz = desc->mcsdsosz;
+
+ /* seek to and read the segment */
+ if (osfseek(ctx->mcscxfp, desc->mcsdsptr, OSFSK_SET))
+ errsig(ctx->mcscxerr, ERR_FSEEK);
+ if (osfrb(ctx->mcscxfp, ptr, (size_t)siz))
+ errsig(ctx->mcscxerr, ERR_FREAD);
+
+ desc->mcsdsflg &= ~MCSDSFINUSE; /* segment no longer in use */
+}
+
+void mcswrt(mcscxdef *ctx, mcsdsdef *desc, uchar *buf, ushort bufl)
+{
+ int tries;
+
+ desc->mcsdsosz = bufl;
+
+ for (tries = 0 ; tries < 2 ; ++tries)
+ {
+ /* attempt to write the object to the swap file */
+ if (osfseek(ctx->mcscxfp, desc->mcsdsptr, OSFSK_SET))
+ errsig(ctx->mcscxerr, ERR_FSEEK);
+ if (!osfwb(ctx->mcscxfp, buf, (size_t)bufl))
+ return;
+
+ /* couldn't write it; compact the swap file */
+ mcscompact(ctx);
+ }
+
+ /* couldn't write to swap file, even after compacting it */
+ errsig(ctx->mcscxerr, ERR_FWRITE);
+}
+
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk