/*
 * Copyright 2010  OpenSourceCodeStewardshipFoundation
 *
 * Licensed under BSD
 */



#include <stdio.h>
#include <malloc.h>

#include "DynArray.h"

//== declarations
#define giveInfoFor( array )   &(((PrivDynArrayInfo*)array)[-1])
//==

/*This updates the contents of the array variable, by side-effect.  There can
 * only ever be one variable that holds a pointer to the dyn array, and all
 * accesses must use that variable.  An add can cause the contents of that
 * variable to change!  (Meaning the position of the array has moved)
 */
void *
makePrivDynArray2( void ***addrOfArrayVar, int32 numElemsToAllocate, int32 sizeOfElem )
 { PrivDynArrayInfo *info;
   int32 bytesInArray  = numElemsToAllocate * sizeOfElem;
   
   info = PR_int__malloc( sizeof(PrivDynArrayInfo) + bytesInArray );
   info->addrOfPtrToArray = addrOfArrayVar;
   info->sizeOfArray      = numElemsToAllocate;
   info->sizeOfElem       = sizeOfElem;
   info->numInArray       = 0;
   return &(info[1]); //skip over prolog -- info is prolog
 }


/*A dynamic array is same as any other array, but add a DynArrayInfo next
 * to it.  Accesses and updates of array indexes are done normally, it's
 * only when add a new element into array that use the extra info.
 * An add can cause the pointer to the normal array to change..  so must
 * be protected to single VP at a time.
 *
 *Only need to use this Fn when need a new index, higher than any previous
 */
int32
addToDynArray2( void *value, void **array )
 { int32 numInArray, sizeOfArray;
   PrivDynArrayInfo *info = giveInfoFor(array); //go backward to prolog
   
   numInArray  = info->numInArray;
   sizeOfArray = info->sizeOfArray;

   if( numInArray >= sizeOfArray )
    {
      array = increaseSizeOfDynArrayTo2( array, sizeOfArray * 2 );
      info  = giveInfoFor( array );
    }

   array[ numInArray ] = value;
   info->numInArray++;

   return numInArray; //index of last elem is one less than num in array
 }


/*Sets num in array to exactly value give 
 *Use this when know how many things going to add in -- then can do this
 * once and use as normal array afterwards.  If later add another chunk,
 * do this again.  Note, this makes new size be just big enough to hold
 * highest index, so will do a linear number of copies if use only this.
 *To cut down on number of copies, can use the increaseSizeTo Fn to
 * exponentially increase size..
 */
void
makeNumInArrayBe2( void **array, int32 numInArray )
 { PrivDynArrayInfo *info = giveInfoFor( array );
   
   if( info->sizeOfArray <= numInArray )
    {
      array = increaseSizeOfDynArrayTo2( array, numInArray );
      info = giveInfoFor( array );
    }
   info->numInArray = numInArray; //num is highest index plus 1
 }

/*Allows highest index to remain higher than index give*/
void
makeHighestDynArrayIndexBeAtLeast2( void **array, int32 index)
 { PrivDynArrayInfo *info = giveInfoFor( array );
 
   if( index < info->numInArray ) return; //num added diff than size
   else makeNumInArrayBe2( array, index );
 }


/*Only use this if certain new size is bigger than current size
 */
void **
increaseSizeOfDynArrayTo2( void **oldArray, int32 newSize )
 { int32 oldsizeOfArray, i, numBytesToCopy;
   void **newArray;
   PrivDynArrayInfo  *oldInfo = giveInfoFor( oldArray );
   PrivDynArrayInfo  *newInfo;

   oldsizeOfArray   = oldInfo->sizeOfArray;
   if( newSize <= oldsizeOfArray ) return;

   newInfo = PR_int__malloc( newSize * oldInfo->sizeOfElem + sizeof(PrivDynArrayInfo) );
   newArray = &(newInfo[1]);
   
   numBytesToCopy = oldInfo->numInArray * oldInfo->sizeOfElem + sizeof(PrivDynArrayInfo);
   memcpy( newInfo, oldInfo, numBytesToCopy ); //copies info + array contents
   
   *(newInfo->addrOfPtrToArray) = newArray; //change contents of array var
   newInfo->sizeOfArray = newSize;

   PR_int__free( oldInfo );
   
   return newArray;
 }


/* Frees the array, plus the info
 */
void
freeDynArrayDeep2( void *array, FreeFnPtr freeFnPtr )
 { PrivDynArrayInfo  *info = giveInfoFor( array );
 
   forAllInDynArrayDo2( array, freeFnPtr );
   PR_int__free( info );
 }

/* Only frees the info
 */
void
freeDynArrayFlat2( void *array )
 { PrivDynArrayInfo  *info = giveInfoFor( array );
 
   PR_int__free( info );
 }


/*The function has a fixed prototype: takes a void * returns void
 * So, the function has to internally cast void * to whatever data struc..
 */
void
forAllInDynArrayDo2( void *array, DynArrayFnPtr fnPtr )
 { PrivDynArrayInfo  *info = giveInfoFor( array );
   int32 idx, sizeOfElem;
   void *addrOfElem;
   
   sizeOfElem = info->sizeOfElem;
   for( idx = 0; idx < info->numInArray; idx++ )
    { addrOfElem = ((int8 *)array) + idx * sizeOfElem;
      (*fnPtr)( addrOfElem );
    }
 }

