/*
 File: ContFramePool.C
 
 Author:
 Date  : 
 
 */

/*--------------------------------------------------------------------------*/
/* 
 POSSIBLE IMPLEMENTATION
 -----------------------

 The class SimpleFramePool in file "simple_frame_pool.H/C" describes an
 incomplete vanilla implementation of a frame pool that allocates 
 *single* frames at a time. Because it does allocate one frame at a time, 
 it does not guarantee that a sequence of frames is allocated contiguously.
 This can cause problems.
 
 The class ContFramePool has the ability to allocate either single frames,
 or sequences of contiguous frames. This affects how we manage the
 free frames. In SimpleFramePool it is sufficient to maintain the free 
 frames.
 In ContFramePool we need to maintain free *sequences* of frames.
 
 This can be done in many ways, ranging from extensions to bitmaps to 
 free-lists of frames etc.
 
 IMPLEMENTATION:
 
 One simple way to manage sequences of free frames is to add a minor
 extension to the bitmap idea of SimpleFramePool: Instead of maintaining
 whether a frame is FREE or ALLOCATED, which requires one bit per frame, 
 we maintain whether the frame is FREE, or ALLOCATED, or HEAD-OF-SEQUENCE.
 The meaning of FREE is the same as in SimpleFramePool. 
 If a frame is marked as HEAD-OF-SEQUENCE, this means that it is allocated
 and that it is the first such frame in a sequence of frames. Allocated
 frames that are not first in a sequence are marked as ALLOCATED.
 
 NOTE: If we use this scheme to allocate only single frames, then all 
 frames are marked as either FREE or HEAD-OF-SEQUENCE.
 
 NOTE: In SimpleFramePool we needed only one bit to store the state of 
 each frame. Now we need two bits. In a first implementation you can choose
 to use one char per frame. This will allow you to check for a given status
 without having to do bit manipulations. Once you get this to work, 
 revisit the implementation and change it to using two bits. You will get 
 an efficiency penalty if you use one char (i.e., 8 bits) per frame when
 two bits do the trick.
 
 DETAILED IMPLEMENTATION:
 
 How can we use the HEAD-OF-SEQUENCE state to implement a contiguous
 allocator? Let's look a the individual functions:
 
 Constructor: Initialize all frames to FREE, except for any frames that you 
 need for the management of the frame pool, if any.
 
 get_frames(_n_frames): Traverse the "bitmap" of states and look for a 
 sequence of at least _n_frames entries that are FREE. If you find one, 
 mark the first one as HEAD-OF-SEQUENCE and the remaining _n_frames-1 as
 ALLOCATED.

 release_frames(_first_frame_no): Check whether the first frame is marked as
 HEAD-OF-SEQUENCE. If not, something went wrong. If it is, mark it as FREE.
 Traverse the subsequent frames until you reach one that is FREE or 
 HEAD-OF-SEQUENCE. Until then, mark the frames that you traverse as FREE.
 
 mark_inaccessible(_base_frame_no, _n_frames): This is no different than
 get_frames, without having to search for the free sequence. You tell the
 allocator exactly which frame to mark as HEAD-OF-SEQUENCE and how many
 frames after that to mark as ALLOCATED.
 
 needed_info_frames(_n_frames): This depends on how many bits you need 
 to store the state of each frame. If you use a char to represent the state
 of a frame, then you need one info frame for each FRAME_SIZE frames.
 
 A WORD ABOUT RELEASE_FRAMES():
 
 When we releae a frame, we only know its frame number. At the time
 of a frame's release, we don't know necessarily which pool it came
 from. Therefore, the function "release_frame" is static, i.e., 
 not associated with a particular frame pool.
 
 This problem is related to the lack of a so-called "placement delete" in
 C++. For a discussion of this see Stroustrup's FAQ:
 http://www.stroustrup.com/bs_faq2.html#placement-delete
 
 */
/*--------------------------------------------------------------------------*/


/*--------------------------------------------------------------------------*/
/* DEFINES */
/*--------------------------------------------------------------------------*/

/* -- (none) -- */

/*--------------------------------------------------------------------------*/
/* INCLUDES */
/*--------------------------------------------------------------------------*/

#include "cont_frame_pool.H"
#include "console.H"
#include "utils.H"
#include "assert.H"

/*--------------------------------------------------------------------------*/
/* DATA STRUCTURES */
/*--------------------------------------------------------------------------*/

/* -- (none) -- */

/*--------------------------------------------------------------------------*/
/* CONSTANTS */
/*--------------------------------------------------------------------------*/

/* -- (none) -- */

/*--------------------------------------------------------------------------*/
/* FORWARDS */
/*--------------------------------------------------------------------------*/

/* -- (none) -- */

/*--------------------------------------------------------------------------*/
/* METHODS FOR CLASS   C o n t F r a m e P o o l */
/*--------------------------------------------------------------------------*/

ContFramePool* ContFramePool::pools = (ContFramePool *) FRAME_SIZE;   // List of frame pools, managed by the class
unsigned int ContFramePool::nPools = 0;    // Number of pools being managed

ContFramePool::ContFramePool(unsigned long _base_frame_no,
                             unsigned long _n_frames,
                             unsigned long _info_frame_no,
                             unsigned long _n_info_frames)
{
    // Bitmap must fit in a single frame!
    // NOTE: In theory, we don't need to make the bitmap any larger,
    // since as stated in the instructions it is already big enough to
    // hold for 128MB of memory with one bit per frame, so should still
    // be enough for 64MB of memory with 2 bits per frame.
    // Assertion changed to match max size of frames allowed.
    assert(_n_frames <= FRAME_SIZE * 4);
    
    base_frame_no = _base_frame_no;
    nframes = _n_frames;
    nFreeFrames = _n_frames;
    info_frame_no = _info_frame_no;
    n_info_frames = _n_info_frames;
   
    // If _info_frame_no is zero then we keep management info in the first
    // frame(s), else we use the provided frame(s) to keep management info
    // NOTE: bitmap needs to be allocated with n_info_frames if specified.
    if(info_frame_no == 0)
    {
        bitmap = (unsigned char *) (base_frame_no * FRAME_SIZE);
    }
    else
    {
        bitmap = (unsigned char *) (info_frame_no * FRAME_SIZE * n_info_frames);
    }
    
    // Number of frames must "fill" the bitmap!
    assert ((nframes % 8 ) == 0);
    
    
    // Everything ok. Proceed to mark all bits in the bitmap
    // NOTE: changed to reflect that I need 2 bits per frame now
    for(int i=0; i*4 < _n_frames; i++)
    {
        bitmap[i] = 0xFF;
    }
    
    // Mark the first frame as being used if it is being used
    // NOTE: need to mark multiple frames if needed.
    if(info_frame_no == 0)
    {
      bitmap[0] = 0x3F;
      nFreeFrames--;
    }
    else
    {
      unsigned int i = info_frame_no / 4;
      unsigned int r = info_frame_no % 4;
      unsigned char mask = 0x80;
      mask = mask >> r*2;
      
      unsigned int c = 0;
      while(c < n_info_frames)
      {
        bitmap[i] = bitmap[i] ^ mask;
        bitmap[i] = bitmap[i] ^ (mask >> 1);
        if(mask == 0x02)
        {
          i++;
          mask = 0x80;
        }
        c++;
        nFreeFrames--;
      }

    }

    pools[nPools] = *this;
    nPools += 1;
    
    Console::puts("Frame Pool initialized\n");
}

unsigned long ContFramePool::get_frames(unsigned int _n_frames)
{
    // Are there enough frames left to allocate?
    assert(nFreeFrames > _n_frames);
   
    // Find a frame that is not being used and return its frame index.
    // Mark that frame as being used in the bitmap.
    // NOTE: Must be updated to find a sequence of contiguous frames
    // that are not being used and return the index of the head.
    unsigned int frame_no = base_frame_no;
    
    // i is being used as the frame_no / 4
    // j is frame_no % 4
    // together, they will give the actual frame_no.
    // c is used as a counter to count a squence of free frames.
    unsigned int i = 0;
    unsigned int j = 0;
    unsigned int c = 0;
    
    while (true)
    {
        unsigned char mask = 0x80 >> (j*2);
        // check every 2 bits for a free frame in the bitmap
        while((mask & bitmap[i]) == 0 || ((mask >> 1) & bitmap[i]) == 0)
        {
          if(mask != 0x02)
          {
            j++;
            mask = mask >> 2;
          }
          else
          {
            i++;
            j = 0;
	    mask = 0x80;
          }
        }
        // if frame is found, start checking for sequence
        unsigned int temp = i;
        c++;
        while(c < _n_frames)
        {
          if(mask != 0x02)
          {
            mask = mask >> 2;
          }
          else
          {
            temp++;
            mask = 0x80;
          }
          if((mask & bitmap[temp]) != 0 && ((mask >> 1) & bitmap[temp]) != 0)
          {
            c++;
          }
          else
          {
            c = 0;
            break;
          }
        }
        if(c == _n_frames)
        {
          nFreeFrames -= _n_frames;
          break;
        }
    }
    
    frame_no += i*4 + j;

    // Update bitmap
    // First: clear most significant bit to mark head of sequence.
    bitmap[i] = bitmap[i] ^ (0x80 >> (j*2));
    // Second: clear both bits for all remaining frames in the sequence.
    c = 1;
    unsigned char mask = 0x80 >> j*2;
    unsigned int temp = i;
    while(c < _n_frames)
    {
      if(mask != 0x02)
      {
        mask = mask >> 2;
      }
      else
      {
        temp++;
        mask = 0x80;
      }

      bitmap[temp] = bitmap[temp] ^ mask;
      bitmap[temp] = bitmap[temp] ^ (mask >> 1);
      c++;
    }

    return (frame_no);
}

void ContFramePool::mark_inaccessible(unsigned long _base_frame_no,
                                      unsigned long _n_frames)
{
    // Mark all frames in the range as being used.
    int i ;
    for(i = _base_frame_no; i < _base_frame_no + _n_frames; i++){
        mark_inaccessible(i);
    }
    nFreeFrames -= _n_frames;
}

void ContFramePool::mark_inaccessible(unsigned long _frame_no)
{
    // Let's first do a range check.
    assert ((_frame_no >= base_frame_no) && (_frame_no < base_frame_no + nframes));
    
    unsigned int bitmap_index = (_frame_no - base_frame_no) / 4;
    unsigned char mask = 0x80 >> ((_frame_no - base_frame_no) % 4) * 2;
    
    // Is the frame being used already?
    assert(((bitmap[bitmap_index] & mask) != 0) && (bitmap[bitmap_index] & (mask >> 1)) != 0);
    
    // Update bitmap
    bitmap[bitmap_index] ^= mask;
    bitmap[bitmap_index] ^= mask >> 1;
    nFreeFrames--;
}

void ContFramePool::release_frames(unsigned long _frame_no)
{
    unsigned int i = 0;
    while(i < nPools)
    {
      if(_frame_no <= pools[i].base_frame_no || _frame_no > (pools[i].base_frame_no + pools[i].nframes))
      {
        i++;
      }
      else
      {
        pools[i].release_frames_here(_frame_no);
        return;
      }
    }
}

void ContFramePool::release_frames_here(unsigned long _first_frame_no)
{
    unsigned char * bitmap = this->bitmap;

    unsigned int bitmap_index = (_first_frame_no - base_frame_no) / 4;
    unsigned char mask = 0x80 >> ((_first_frame_no - base_frame_no) % 4) * 2;

    if(!((bitmap[bitmap_index] & mask) == 0 && (bitmap[bitmap_index] & (mask >> 1)) != 0))
    {
        if((bitmap[bitmap_index] & mask) != 0 && (bitmap[bitmap_index] & (mask >> 1)) != 0)
        {
          Console::puts("Error, Frame being released is not being used\n");
          assert(false);
        }
        Console::puts("Error, Frame being released is not head of sequence\n");
        assert(false);
    }
       
    bitmap[bitmap_index] ^= mask;
    nFreeFrames++;

    if(mask != 0x02)
    {
      mask = mask >> 2;
    }
    else
    {
      mask = 0x80;
      bitmap_index++;
    }

    while(bitmap[bitmap_index] & mask == 0 && (bitmap[bitmap_index] & (mask >> 1)) == 0)
    {
      bitmap[bitmap_index] ^= mask;
      bitmap[bitmap_index] ^= (mask >> 1);
      if(mask != 0x02)
      {
        mask = mask >> 2;
      }
      else
      {
        mask = 0x80;
        bitmap_index++;
      }
      nFreeFrames++;
    }
}

unsigned long ContFramePool::needed_info_frames(unsigned long _n_frames)
{
    return (_n_frames / (FRAME_SIZE * 4)) + (_n_frames % (FRAME_SIZE * 4) > 0 ? 1 : 0);
}
