/* page.c

   pseudo-page routines
*/

#include "header.h"
#include "pageHeader.h"
#include "prototypes.h"
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/* Functions ...................................................................*/

/* Extract a particular page from the freeList */
void getPage(PageInfo *pi)
{
  LIST_CUT(pi);
  gcInfo.nPagesInUse++;
}

/* Assume preferredPi points to a valid page in a valid heapblock or is NULL */
PageInfo *reservePage(Word header, PageInfo *preferredPi)
{
  PageInfo *pi;
  
  if((preferredPi != NULL) && 
     (preferredPi->header & PAGE_RESERVED) == 0)
    {
      /* prefferedPi has not been reserved. */
      assert(!LIST_EMPTY(&gcInfo.freeList));
      pi = preferredPi;
    }
  else
    {
      if(LIST_EMPTY(&gcInfo.freeList))
	{
	  allocateHeapBlock();
	}
      
      assert(!LIST_EMPTY(&gcInfo.freeList));
      
      pi = gcInfo.freeList.next;
    }

  LIST_CUT(pi);
  pi->header = PAGE_RESERVED | header;

  gcInfo.nPagesInUse++;
  
  return pi;
}

PageInfo *reserveNPages(unsigned int n, Word header)
{
 static int counter=0;
 int i;
 PageInfo *piStart = NULL, *piEnd = NULL;
 PageInfo *pi;

 counter++;

 pi = gcInfo.freeList.next;
 i = 0; /* i tracks the maximum number of contiguous free pages found so far. */
 while(pi!=&gcInfo.freeList && i < n)
   {
     /* Find the largest contiguous interval around the head of the free list */
     piStart = pi;
     piEnd = pi;
     i = 1; /* i tracks the number of free pages found so far. */
     assert(pi->header == PAGE_FREE);

     while((i<n) && 
	   (piStart > (PageInfo *)BLOCK(piStart)) &&
	   ((piStart-1)->header == PAGE_FREE))
       {
	     piStart--;
	     i++;
       }

     while((i<n) &&
	   (piEnd < ((PageInfo *)BLOCK(piEnd) + PAGES_PER_BLOCK-1)) &&
	   ((piEnd+1)->header == PAGE_FREE))
       {
	 piEnd++;
	 i++;
       }

     pi = pi->next;
   }


 if(i<n)
   {
     /* If i<n then we have finished looking through the whole freeList and
	didn't find enough contigouos pages.  So we allocate a new heapBlock. */
     pi = gcInfo.freeList.prev;

     allocateHeapBlock();

     piStart = pi->next;
     assert(piStart->next == piStart+1);
     assert(piStart->prev = pi);

     assert(!LIST_EMPTY(&gcInfo.freeList));
     
     piEnd = piStart+n-1;
     
     if(piEnd >= (PageInfo *)BLOCK(piStart) + PAGES_PER_BLOCK)
       {
	 fprintf(stderr,"Cannot allocate objects larger than 1 heapblock.\n");
	 exit(1);
       }
   }

 /* Here piStart to piEnd inclusive are the n elements to cut out of the
    freeList. */
 assert(piStart != NULL);
 assert(piEnd != NULL);
 assert(piEnd - piStart == n-1);
 assert(piStart->header == PAGE_FREE);
 assert(piEnd->header == PAGE_FREE);

 for(pi=piStart; pi <= piEnd; pi++)
   {
     LIST_CUT(pi);

     assert(pi->header == PAGE_FREE);
     pi->header = PAGE_RESERVED | header;

     if(pi!=piEnd)
       {
	 pi->next = pi+1;
       }
     if(pi!=piStart)
       {
	 pi->prev = pi-1;
       }
     gcInfo.nPagesInUse++;
   }

 piStart->prev = NULL;
 piEnd->next = NULL;

 return piStart;
}

/* We attach newly freed pages to the head of the freeList in the hope that they
   will get reused as soon as possible.  This may turn out not to be such a hot
   strategy. */
void freePage(PageInfo *pi)
{

  assert(pi->header | PAGE_RESERVED); /* The page must have been reserved. */

  gcInfo.nPagesInUse--;
  
  pi->header = PAGE_FREE;

  LIST_INSERT_BEGIN(&gcInfo.freeList,pi);

}

void freeListOfPages(PageInfo *head)
{
  PageInfo *pi;

  pi = head->next;
  while(pi!=head)
    {
      assert(pi->header!=PAGE_FREE);
      gcInfo.nPagesInUse--;
      pi->header = PAGE_FREE;
      pi=pi->next;
    }

  LIST_MERGE(&gcInfo.freeList,head);

}

PageInfo *internalPageRequest()
{
  PageInfo *pi;

  STATS(gcInfo.nSystemPages++);

#ifdef PAGE_BLACKLISTING
  if(LIST_EMPTY(&gcInfo.blackList))
    {
      pi = reservePage(PAGE_SYSTEM,NULL);
      return pi;
    }
  else
    {
      pi = gcInfo.blackList.next;
      LIST_CUT(pi);
      pi->header |= PAGE_SYSTEM | PAGE_RESERVED;
    }
#else
  pi = reservePage(PAGE_SYSTEM,NULL);
#endif

  return pi;
}

void internalPageFree(PageInfo *pi)
{
  STATS(gcInfo.nSystemPages--);
#ifdef PAGE_BLACKLISTING
  if(PAGE_IS_BLACKLISTED(pi->header))
    {
      pi->header = PAGE_BLACKLISTED | PAGE_RESERVED;
      LIST_INSERT_BEGIN(&gcInfo.blackList,pi);
    }
  else
    {
      freePage(pi);
    }
#else
  freePage(pi);
#endif

}

/* "Zero" all untraversed objects on a pinned page, and untraverse the traversed
   objects (if they are copyable, otherwise just make them pinned).  By "Zero"
   we mean put in a header word for a dummy pointer-free array. */
void fixupPage(PageInfo *pi)
{
  Word *pHeader;
  Word header;
  Word *pageEnd;
  unsigned int size;
  unsigned int headerWords;
  unsigned int status;

  Word *junkStart;
  int junkSize;
  unsigned int junkIs;

  pageEnd = PI_TO_PAGE(pi) + PAGE_SIZE;
  pHeader = PI_TO_PAGE(pi) + PAGE_OFFSET_HEADER(pi->header);

  junkSize = 0;
  junkStart = pHeader;
  junkIs = 0;

  while(pHeader<pageEnd)
    {
      header = *pHeader;

      if(header == GC_EMPTY)
	{
	  if(junkIs)
	    {
	      junkSize++;
	    }
	  pHeader++;
	  continue;
	}

    label1:
      status = GC_EXTRACT_STATUS(header);
      if(status == GC_MOVED)
	{
	  header = INDIRECT_HEADER(header);
	  assert(!GC_END_LARGE(header));
	}
      else
	{
	  if(GC_END_LARGE(header))
	    {
	      header = *(pHeader + GC_SIZE_LARGE(header) + 1);
	      goto label1;
	    }
	  
	  if(status==GC_TRAVERSED)
	    {
	      if(junkIs)
		{
		  if(junkSize == 0 ) 
		    {
		      *junkStart = GC_EMPTY;
		    }
		  else
		    {
		      *junkStart = GC_CREATE_JUNK(junkSize);
		      STATS(gcInfo.nWastedSpace+=(junkSize+1)); 
		      /* +1 for the header word */
		  
		      if(junkSize > COPYABLE_OBJECT) 
			GC_PIN(*junkStart);		  
		    }
		  junkIs = 0;
		}
	      
	      headerWords = GC_HEADER_WORDS(header);
	      size = GC_OBJECT_SIZE(header)+headerWords;
	      if(size<=COPYABLE_OBJECT) 
		GC_CLEAR(*(pHeader+headerWords-1));
	      else
		{
		  STATS(unsigned int residue = size % PAGE_SIZE; \
			unsigned int size2 = size/PAGE_SIZE; \
			size2 += (residue ? 1 : 0); \
			gcInfo.nLargeObjectPages+=size2; \
			gcInfo.nWastedSpace += PAGE_SIZE - residue; \
			);
		  GC_PIN(*(pHeader+headerWords-1));
		}
	      
	      pHeader += size;
	      continue;
	    }	 
	}
  
      if(GC_SMALL(header))
	size = GC_SIZE_SMALL(header)+1;
      else
	size = GC_SIZE_LARGE(header) + GC_HEADER_WORDS(header);


      if(!junkIs)
	{
	  junkStart = pHeader;
	  junkSize = size - 1;
	  assert(junkSize>=0);

	  junkIs = 1;
	}
      else
	{
	  junkSize +=size;
	}

      pHeader += size;
    }
  
  if(junkIs)/* Last objects on page are junk also need to be "zeroed" */
    {
      /* Must assume junkSize is incorrect, but junkStart should be correct. */
      junkSize = pageEnd - junkStart - 1;
      assert(junkSize>=0);

      if(junkSize == 0 ) 
	{
	  *junkStart = GC_EMPTY;
	}
      else
	{
	  *junkStart = GC_CREATE_JUNK(junkSize);
	  STATS(gcInfo.nWastedSpace+=(junkSize+1)); 
	  /* +1 for the header word */
	  
	  if(junkSize > COPYABLE_OBJECT) 
	    GC_PIN(*junkStart);		  
	}
      junkIs = 0;
    }

}


void pinPagesObjects(PageInfo *pi)
{
  Word *pHeader;
  Word header;
  Word indirectHeader;
  Word *pageEnd;
  unsigned int status;

  assert(pi!=NULL);
  assert((pi->header & PAGE_USER) != 0);

  pHeader = PI_TO_PAGE(pi) + PAGE_OFFSET_HEADER(pi->header);
  pageEnd = PI_TO_PAGE(pi) + PAGE_SIZE;

  while(pHeader<pageEnd)
    {
      header = *pHeader;

      if(header==GC_EMPTY)
	{
	  pHeader++;
	  continue;
	}

      status = GC_EXTRACT_STATUS(header);
      if(status!=GC_MOVED)
	{
	  STATS(if(GC_EXTRACT_STATUS(header) == GC_NORMAL)
		gcInfo.nPinnedObjects++);
	  if(status==GC_NORMAL) GC_PIN(*pHeader);

	  pHeader += GC_OBJECT_SIZE(header)+1;
	}
      else
	{
	  indirectHeader = INDIRECT_HEADER(header);
	  pHeader += GC_OBJECT_SIZE(indirectHeader)+1;
	}

    }
}

#ifdef PAGE_BITMASKS
/* Updates the bitmask of pi.  And marks pi's header word as updated.  */
void updatePageBitmask(PageInfo *pi)
{
 Word *header;
 Word *pageEnd;
 Word *ptr, *ptrEnd;
 Word *bitmask;
 unsigned int currentBit;
 unsigned int currentWord;
 unsigned int currentPos;
 unsigned int size;

 assert(0); /* This is probably screwed up now.  If we ever decide to use it I
	       will fix it. */
 bitmask = pi->bitmask;

 /* Clear the bitmask */
 for(ptr=bitmask, ptrEnd = ptr+PAGE_BITMASK_SIZE;ptr<ptrEnd;ptr++)
   {
     *ptr=0;
   }
 
 currentPos = PAGE_OFFSET_HEADER(pi->header);
 header = PI_TO_PAGE(pi) + currentPos;
 pageEnd = PI_TO_PAGE(pi) + PAGE_SIZE;

 while(header<pageEnd)
   {
     /* Although headerOfPointer can be called when there are forwarded pointers
	on the page, there should be none when updatePageBitmask is called. */
     assert(!GC_FORWARDED(*header));

     /* Set the currentBit to 1, since this is a true headerWord. */
     currentWord = BITMASK_WORD_OFFSET(currentPos);
     currentBit = BITMASK_BIT_OFFSET(currentPos);
     *(bitmask + currentWord) |= (0x1 << (currentBit));

     size = GC_OBJECT_SIZE(*header);

     if(GC_END_LARGE(*header))
	{
	  ptrEnd = header+size+1;
	  if(ptrEnd > pageEnd) ptrEnd = pageEnd;

	  header++;
	  currentPos++;
	  while(header<ptrEnd)
	    {
	      currentWord = BITMASK_WORD_OFFSET(currentPos);
	      currentBit = BITMASK_BIT_OFFSET(currentPos);
	      *(bitmask + currentWord) |= (0x1 << (currentBit));
	      header++;
	      currentPos++;
	    }
	}
     else
       {
	 header+=size+1;
	 currentPos+=size+1;
       }
   }
 
 pi->header |= PAGE_BITMASK_UPDATED;

}
#endif


/* Scans a page and calls processObject on all the objects found. None of the
   objects should be forwarded. */
void scanPage(PageInfo *pi)
{
 Word *header;
 Word *pageEnd;
 Word size;

 header = PI_TO_PAGE(pi) + PAGE_OFFSET_HEADER(pi->header);
 pageEnd = PI_TO_PAGE(pi) + PAGE_SIZE;

 while(header<pageEnd)
   {
     if(*header == GC_EMPTY)
       {
	 header++;
	 continue;
       }

     assert(GC_EXTRACT_STATUS(*header) != GC_MOVED);

     size = GC_OBJECT_SIZE(*header);

     if(!GC_END_LARGE(*header))
       {
	 processObject(header+1, gcInfo.explicitQueue);
       }

     header+=size+1;
   }

}

/* This is used for debugging purposes only!!  With gdb you can inspect the
   results of calling these functions.  */
PageInfo *pageToPI(Word *p)
{
 return (PageInfo *)PAGE_TO_PI(p);
}

Word *piToPage(PageInfo *pi)
{
 return PI_TO_PAGE(pi);
}

/* EOF: page.c */

/* (c) Frederick Smith, Greg Morrisett.
 *     October 1998, all rights reserved.
 *
 *
 *              Copyright 1990-1993 Digital Equipment Corporation
 *                         All Rights Reserved
 *
 * Permission to use, copy, and modify this software and its documentation is
 * hereby granted only under the following terms and conditions.  Both the
 * above copyright notice and this permission notice must appear in all copies
 * of the software, derivative works or modified versions, and any portions
 * thereof, and both notices must appear in supporting documentation.
 *
 * Users of this software agree to the terms and conditions set forth herein,
 * and hereby grant back to Digital a non-exclusive, unrestricted, royalty-free
 * right and license under any changes, enhancements or extensions made to the
 * core functions of the software, including but not limited to those affording
 * compatibility with other hardware or software environments, but excluding
 * applications which incorporate this software.  Users further agree to use
 * their best efforts to return to Digital any such changes, enhancements or
 * extensions that they make and inform Digital of noteworthy uses of this
 * software.  Correspondence should be provided to Digital at:
 * 
 *                       Director of Licensing
 *                       Western Research Laboratory
 *                       Digital Equipment Corporation
 *                       250 University Avenue
 *                       Palo Alto, California  94301  
 * 
 * This software may be distributed (but not offered for sale or transferred
 * for compensation) to third parties, provided such third parties agree to
 * abide by the terms and conditions of this notice.  
 * 
 * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
 * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 */
