/*
 *  Copyright 2009 OpenSourceCodeStewardshipFoundation.org
 *  Licensed under GNU General Public License version 2
 *
 * NOTE: this version of SRSW correct as of April 25, 2010
 *
 * Author: seanhalle@yahoo.com
 */


#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>

#include "PrivateQueue.h"



//===========================================================================

/*This kind of queue is private to a single core at a time -- has no
 * synchronizations 
 */

PrivQueueStruc* makePrivQ()
 {
   PrivQueueStruc* retQ;
   retQ = (PrivQueueStruc *) malloc( sizeof( PrivQueueStruc ) );

   retQ->startOfData = malloc( 1024 * sizeof(void *) );
   
   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 = malloc( newSize * sizeof(void *) );
   memcpy(Q->startOfData, oldStartOfData, oldSize * sizeof(void *));
   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;
 }


/*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;
 }
