/*
 *  Copyright 2009 OpenSourceStewardshipFoundation.org
 *  Licensed under GNU General Public License version 2
 *
 * Author: seanhalle@yahoo.com
 */


#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>

#include "PrivateQueue.h"
#include "../vmalloc.h"



//===========================================================================

/*This kind of queue is private to a single core at a time -- has no
 * synchronizations 
 */

PrivQueueStruc* makeVMSPrivQ()
 {
   PrivQueueStruc *retQ;
   retQ = VMS__malloc( sizeof( PrivQueueStruc ) );
   retQ->startOfData = (void*)VMS__malloc( 1024 * sizeof(void *) );
   memset( retQ->startOfData, 0, 1024 );

   retQ->extractPos = &(retQ->startOfData[0]); //side by side == empty
   retQ->insertPos  = &(retQ->startOfData[1]); // so start pos's have to be
   retQ->endOfData  = &(retQ->startOfData[1023]);

   return retQ;
 }


void
enlargePrivQ( PrivQueueStruc *Q )
 { int    oldSize, newSize;
   void **oldStartOfData;

   oldSize           = Q->endOfData - Q->startOfData;
   newSize           = 2 * oldSize;
   oldStartOfData = Q->startOfData;
   Q->startOfData = VMS__malloc( newSize * sizeof(void *) );
   memcpy(Q->startOfData, oldStartOfData, oldSize * sizeof(void *));
   VMS__free(oldStartOfData);
   
   Q->extractPos  = &(Q->startOfData[0]); //side by side == empty
   Q->insertPos   = &(Q->startOfData[1]); // so start pos's have to be
   Q->endOfData   = &(Q->startOfData[newSize - 1]);
 }


/*Returns NULL when queue is empty
 */
void* readPrivQ( PrivQueueStruc* Q )
 { void *out    = 0;
   void **startOfData = Q->startOfData;
   void **endOfData   = Q->endOfData;

   void **insertPos  = Q->insertPos;
   void **extractPos = Q->extractPos;

      //if not empty -- (extract is just below insert when empty)
   if( insertPos - extractPos != 1 &&
       !(extractPos == endOfData && insertPos == startOfData))
    {    //move before read
      if( extractPos == endOfData ) //write new pos exactly once, correctly
       { Q->extractPos = startOfData; //can't overrun then fix it 'cause
       }                              // other thread might read bad pos
      else
       { Q->extractPos++;
       }
      out = *(Q->extractPos);
      return out;
    }
      //Q is empty
   return NULL;
 }

int32
numInPrivQ( PrivQueueStruc *Q )
 { int32 size, numIn;
 
   if( Q->insertPos < Q->extractPos )
    {    //insert has wrapped around so numIn is:
         // insertPos + size - extractPos -- Consider, is empty when
         // extractPos = endOfData and insert = start -- correctly get zero
      size  = Q->endOfData - Q->startOfData;
      numIn = Q->insertPos + size - Q->extractPos;
    }
   else
    {
      numIn =  Q->insertPos -  Q->extractPos -1;//-1 bec empty @ side-by-side
    }
   return numIn;
 }


/*Expands the queue size automatically when it's full
 */
void
writePrivQ( void * in, PrivQueueStruc* Q )
 {
   void **startOfData = Q->startOfData;
   void **endOfData   = Q->endOfData;
   
   void **insertPos  = Q->insertPos;
   void **extractPos = Q->extractPos;

tryAgain:
      //Full? (insert is just below extract when full)
   if( extractPos - insertPos != 1 &&
       !(insertPos == endOfData && extractPos == startOfData))
    { *(Q->insertPos) = in;   //insert before move
      if( insertPos == endOfData ) //write new pos exactly once, correctly
       { Q->insertPos = startOfData;
       }
      else
       { Q->insertPos++;
       }
      return;
    }
      //Q is full
   enlargePrivQ( Q );
   goto tryAgain;
 }


/*Returns false when the queue was full.
 * have option of calling make_larger_PrivQ to make more room, then try again
 */
int writeIfSpacePrivQ( void * in, PrivQueueStruc* Q )
 {
   void **startOfData = Q->startOfData;
   void **endOfData   = Q->endOfData;

   void **insertPos  = Q->insertPos;
   void **extractPos = Q->extractPos;

   if( extractPos - insertPos != 1 &&
       !(insertPos == endOfData && extractPos == startOfData))
    { *(Q->insertPos) = in;   //insert before move
      if( insertPos == endOfData ) //write new pos exactly once, correctly
       { Q->insertPos = startOfData;
       }
      else
       { Q->insertPos++;
       }
      return TRUE;
    }
      //Q is full
   return FALSE;
 }

/*Treats queue as a stack -- no matter contents, if read done right after
 * a push, then the pushed item is what comes out.
 * Expands the queue size automatically when it's full.
 */
void
pushPrivQ( void * in, PrivQueueStruc* Q )
 {
   void **startOfData = Q->startOfData;
   void **endOfData   = Q->endOfData;

   void **insertPos  = Q->insertPos;
   void **extractPos = Q->extractPos;

tryAgain:
      //Full? (insert is just below extract when full)
   if( extractPos - insertPos != 1 &&
       !(insertPos == endOfData && extractPos == startOfData))
    {    //insert -- but go backwards, inserting at read position then
         // move read pos backwards
      *(Q->extractPos) = in;
      if( extractPos == startOfData ) //write new pos exactly once, correctly
       { Q->extractPos = endOfData;   //can't overrun then fix it 'cause
       }                              // other thread might read bad pos
      else
       { Q->extractPos--;
       }
      return;
    }
      //Q is full
   enlargePrivQ( Q );
   goto tryAgain;
 }

void
freePrivQ( PrivQueueStruc *Q )
 {
   VMS__free( Q->startOfData );
   VMS__free( Q );
 }