/*! \file blast-in.c
 *
 * COPYRIGHT (c) 2019 The Fellowship of SML/NJ (http://www.smlnj.org)
 * All rights reserved.
 */

#include <stdio.h>
#include "ml-base.h"
#include "ml-values.h"
#include "ml-state.h"
#include "heap.h"
#include "ml-heap-image.h"
#include "c-globals-tbl.h"
#include "heap-input.h"


/* local routines */
PVT status_t ReadImage (ml_state_t *msp, inbuf_t *bp, ml_val_t *objRef);


/* BlastIn:
 *
 * Build an ML heap object from a sequence of bytes; the fd is the underlying
 * file descriptor (== -1, if blasting from a string), buf is any pre-read
 * bytes of data, and nbytesP points to the number of bytes in buf.
 */
ml_val_t BlastIn (ml_state_t *msp, Byte_t *buf, long len, bool_t *errFlg)
{
    inbuf_t		inBuf;
    ml_image_hdr_t	hdr;
    ml_val_t		obj;

    inBuf.needsSwap	= FALSE;
    inBuf.file		= NULL;
    inBuf.base		= buf;
    inBuf.buf		= buf;
    inBuf.nbytes	= len;

  /* read the object header */
    if (HeapIO_ReadBlock (&inBuf, &hdr, sizeof(hdr)) == FAILURE) {
	*errFlg = TRUE;
	return ML_unit;
    }
    if (hdr.byteOrder != ORDER) {
	if (BIGENDIAN_TO_HOST32(hdr.byteOrder) != ORDER) {
	    *errFlg = TRUE;
	    return ML_unit;
	}
	hdr.magic = BIGENDIAN_TO_HOST32(hdr.magic);
	hdr.kind = BIGENDIAN_TO_HOST32(hdr.kind);
	inBuf.needsSwap = TRUE;
    }
    if (hdr.magic != BLAST_MAGIC) {
	*errFlg = TRUE;
	return ML_unit;
    }

    switch (hdr.kind) {
      case BLAST_IMAGE:
	if (ReadImage(msp, &inBuf, &obj) == FAILURE) {
	    *errFlg = TRUE;
	    return ML_unit;
	}
	break;
      case BLAST_UNBOXED: {
	    ml_blast_hdr_t	bhdr;
	    if (HeapIO_ReadBlock(&inBuf, &bhdr, sizeof(bhdr)) == FAILURE) {
	        *errFlg = TRUE;
	        return ML_unit;
	    }
	    else
		obj = bhdr.rootObj;
	} break;
      default:
	*errFlg = TRUE;
	return ML_unit;
    }

    return obj;

} /* end of BlastIn */


/* ReadImage:
 */
PVT status_t ReadImage (ml_state_t *msp, inbuf_t *bp, ml_val_t *objRef)
{
    ml_blast_hdr_t	blastHdr;
    ml_val_t		*externs;
    heap_arena_hdr_t	*arenaHdrs[NUM_OBJ_KINDS], *arenaHdrsBuf;
    int			arenaHdrsSize, i;
    gen_t		*gen1 = msp->ml_heap->gen[0];

    if ((HeapIO_ReadBlock(bp, &blastHdr, sizeof(blastHdr)) == FAILURE)
    || (blastHdr.numArenas > NUM_ARENAS)
    || (blastHdr.numBOKinds > NUM_BIGOBJ_KINDS))
	return FAILURE;

  /* read the externals table */
    externs = HeapIO_ReadExterns (bp);

  /* read the arena headers. */
    arenaHdrsSize = (blastHdr.numArenas + blastHdr.numBOKinds)
			* sizeof(heap_arena_hdr_t);
    arenaHdrsBuf = (heap_arena_hdr_t *) MALLOC (arenaHdrsSize);
    if (HeapIO_ReadBlock (bp, arenaHdrsBuf, arenaHdrsSize) == FAILURE) {
	FREE (arenaHdrsBuf);
	return FAILURE;
    }
    for (i = 0;  i < NUM_OBJ_KINDS;  i++)
	arenaHdrs[i] = NIL(heap_arena_hdr_t *);
    for (i = 0;  i < blastHdr.numArenas;  i++) {
	heap_arena_hdr_t	*p = &(arenaHdrsBuf[i]);
	arenaHdrs[p->objKind] = p;
    }
    /** DO BIG OBJECT HEADERS TOO **/

  /* check the heap to see if there is enough free space in the 1st generation */
    {
	Addr_t	allocSzB = msp->ml_heap->allocSzB;
	bool_t	needsGC = FALSE;

	for (i = 0;  i < NUM_ARENAS;  i++) {
	    arena_t	*ap = gen1->arena[i];
	    if ((arenaHdrs[i] != NIL(heap_arena_hdr_t *)) && ((! isACTIVE(ap))
	    || (AVAIL_SPACE(ap) < arenaHdrs[i]->info.o.sizeB + allocSzB))) {
		needsGC = TRUE;
		ap->reqSizeB = arenaHdrs[i]->info.o.sizeB;
	    }
	}
	if (needsGC) {
	    if (bp->nbytes > 0) {
	      /* the GC may cause the buffer to move */
		ml_val_t	buffer = PTR_CtoML(bp->base);
		InvokeGCWithRoots (msp, 1, &buffer, NIL(ml_val_t *));
		if (buffer != PTR_CtoML(bp->base)) {
		  /* the buffer moved, so adjust the buffer pointers */
		    Byte_t	*newBase = PTR_MLtoC(Byte_t, buffer);
		    bp->buf = newBase + (bp->buf - bp->base);
		    bp->base = newBase;
		}
	    }
	    else
		InvokeGC (msp, 1);
	}
    }

    /** Read the blasted objects **/
    {
	Addr_t	arenaBase[NUM_ARENAS];

	for (i = 0;  i < NUM_ARENAS;  i++) {
	    if (arenaHdrs[i] != NIL(heap_arena_hdr_t *)) {
	        arena_t	*ap = gen1->arena[i];
	        arenaBase[i] = (Addr_t)(ap->nextw);
	        HeapIO_ReadBlock (bp, (ap->nextw), arenaHdrs[i]->info.o.sizeB);
/*SayDebug ("[%2d] Read [%#x..%#x)\n", i+1, ap->nextw,*/
/*(Addr_t)(ap->nextw)+arenaHdrs[i]->info.o.sizeB);*/
	    }
	}

      /* adjust the pointers */
	for (i = 0;  i < NUM_ARENAS;  i++) {
	    if (arenaHdrs[i] != NIL(heap_arena_hdr_t *)) {
		arena_t	*ap = gen1->arena[i];
		if (i != STRING_INDX) {
		    ml_val_t	*p, *stop;
		    p = ap->nextw;
		    stop = (ml_val_t *)((Addr_t)p + arenaHdrs[i]->info.o.sizeB);
		    while (p < stop) {
		        ml_val_t	w = *p;
		        if (! isUNBOXED(w)) {
			    if (isEXTERNTAG(w)) {
			        w = externs[EXTERNID(w)];
			    }
			    else if (! isDESC(w)) {
/*SayDebug ("adjust (@%#x) %#x --> ", p, w);*/
			        w = PTR_CtoML(arenaBase[HIO_GET_ID(w)] + HIO_GET_OFFSET(w));
/*SayDebug ("%#x\n", w);*/
			    }
		            *p = w;
		        }
		        p++;
		    }
		    ap->nextw	=
		    ap->sweep_nextw	= stop;
	        }
	        else
		    ap->nextw = (ml_val_t *)((Addr_t)(ap->nextw)
				+ arenaHdrs[i]->info.o.sizeB);
	    }
	} /* end of for */

      /* adjust the root object pointer */
	if (isEXTERNTAG(blastHdr.rootObj))
	    *objRef = externs[EXTERNID(blastHdr.rootObj)];
	else
	    *objRef = PTR_CtoML(
		    arenaBase[HIO_GET_ID(blastHdr.rootObj)]
		    + HIO_GET_OFFSET(blastHdr.rootObj));
/*SayDebug ("root = %#x, adjusted = %#x\n", blastHdr.rootObj, *objRef);*/
    }

    FREE (arenaHdrsBuf);
    FREE (externs);

    return SUCCESS;

} /* end of ReadImage */
