/* Array.c */
/*****************************************************************************/
/*                                                                           */
/*    System Dependency Library for Building Portable Software               */
/*    Macintosh Version                                                      */
/*    Written by Thomas R. Lawrence, 1993 - 1994.                            */
/*                                                                           */
/*    This file is Public Domain; it may be used for any purpose whatsoever  */
/*    without restriction.                                                   */
/*                                                                           */
/*    This package is distributed in the hope that it will be useful,        */
/*    but WITHOUT ANY WARRANTY; without even the implied warranty of         */
/*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                   */
/*                                                                           */
/*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
/*                                                                           */
/*****************************************************************************/

#include "MiscInfo.h"
#include "Debug.h"
#include "Audit.h"
#include "Definitions.h"

#include "Array.h"
#include "Memory.h"


/* Abstract data structure for maintaining a linear array of elements.  It provides */
/* indexed lookup, insertion, and deletion capabilities on the array, and attempts */
/* to provide efficient storage management */


/* when array runs out of free space at end, it is extended by ARRAYDELTACOUNT slots. */
/* when there is more than ARRAYMAXFREECOUNT free space at the end, it is reduced */
/* by ARRAYDELTACOUNT.  Therefore, ARRAYMAXFREECOUNT should be greater than */
/* ARRAYDELTACOUNT to prevent resizing the array constantly. */
#define ARRAYDELTACOUNT (32)
#define ARRAYMAXFREECOUNT (64)

struct ArrayRec
	{
		long					RealElementCount; /* actual number of elements in array */
		long					ListElementCount; /* number of slots in List */
		void**				List;
	};


/* create a new array.  returns NIL if it couldn't allocate any memory. */
ArrayRec*			NewArray(void)
	{
		return NewArrayReserveSpace(ARRAYDELTACOUNT);
	}


/* create a new array with preallocated space for a bunch of elements. */
ArrayRec*			NewArrayReserveSpace(long NumElements)
	{
		ArrayRec*		Array;
		void**			List;

		ERROR(NumElements < 0,PRERR(ForceAbort,"NewArrayReserveSpace:  bad reserve space"));
		Array = (ArrayRec*)AllocPtrCanFail(sizeof(ArrayRec),"ArrayRec");
		if (Array == NIL)
			{
			 FailurePoint1:
				return NIL;
			}
		List = (void**)AllocPtrCanFail(sizeof(void*) * NumElements,"ArrayList");
		if (List == NIL)
			{
			 FailurePoint2:
				ReleasePtr((char*)Array);
				goto FailurePoint1;
			}
		Array->List = List;
		Array->RealElementCount = 0;
		Array->ListElementCount = NumElements;
		return Array;
	}


/* dispose of an array.  any pointers contained in the array are NOT disposed. */
void					DisposeArray(ArrayRec* Array)
	{
		CheckPtrExistence(Array);
		ReleasePtr((char*)Array->List);
		ReleasePtr((char*)Array);
	}


/* get the number of elements currently stored in the array */
long					ArrayGetLength(ArrayRec* Array)
	{
		CheckPtrExistence(Array);
		return Array->RealElementCount;
	}


/* get an indexed element from the array */
void*					ArrayGetElement(ArrayRec* Array, long Where)
	{
		CheckPtrExistence(Array);
		ERROR((Where < 0) || (Where >= ArrayGetLength(Array)),
			PRERR(ForceAbort,"ArrayGetElement:  Index out of range"));
		return (Array->List)[Where];
	}


/* change the value of an indexed element in the array */
void					ArraySetElement(ArrayRec* Array, void* Element, long Where)
	{
		CheckPtrExistence(Array);
		ERROR((Where < 0) || (Where >= ArrayGetLength(Array)),
			PRERR(ForceAbort,"ArraySetElement:  Index out of range"));
		(Array->List)[Where] = Element;
	}


/* insert an element into the array.  Where is the index of the element to insert */
/* BEFORE.  if Where == number of elements in the array, the element is appended. */
/* return True if successful */
MyBoolean			ArrayInsertElement(ArrayRec* Array, void* Element, long Where)
	{
		CheckPtrExistence(Array);
		ERROR((Where < 0) || (Where >/*NB*/ ArrayGetLength(Array)),
			PRERR(ForceAbort,"ArrayInsertElement:  Index out of range"));
		ERROR(Array->RealElementCount > Array->ListElementCount,
			PRERR(ForceAbort,"ArrayInsertElement:  [pre] real count > list block count"));
		if (Array->RealElementCount == Array->ListElementCount)
			{
				long				NewSize;
				void**			NewList;

				/* no free space at the end of the array, so we have to extend it */
				NewSize = Array->RealElementCount + ARRAYDELTACOUNT;
				NewList = (void**)ResizePtr((char*)(Array->List),NewSize * sizeof(void*));
				if (NewList == NIL)
					{
						return False;
					}
				Array->List = NewList;
				Array->ListElementCount = NewSize;
			}
		PRNGCHK(Array->List,&(Array->List[Where]),
			(Array->RealElementCount - Where) * sizeof(void*));
		PRNGCHK(Array->List,&(Array->List[Where + 1]),
			(Array->RealElementCount - Where) * sizeof(void*));
		MoveData((char*)&(Array->List[Where]),
			(char*)&(Array->List[Where + 1]),
			(Array->RealElementCount - Where) * sizeof(void*));
		(Array->List)[Where] = Element;
		Array->RealElementCount += 1;
		ERROR(Array->RealElementCount > Array->ListElementCount,
			PRERR(ForceAbort,"ArrayInsertElement:  [post] real count > list block count"));
		return True;
	}


/* append an element to the end of the array.  returns True if successful */
MyBoolean			ArrayAppendElement(ArrayRec* Array, void* Element)
	{
		CheckPtrExistence(Array);
		return ArrayInsertElement(Array,Element,Array->RealElementCount);
	}


/* delete an element from the array and shift any elements after it down one. */
void					ArrayDeleteElement(ArrayRec* Array, long Where)
	{
		CheckPtrExistence(Array);
		ERROR((Where < 0) || (Where >= ArrayGetLength(Array)),
			PRERR(ForceAbort,"ArrayDeleteElement:  Index out of range"));
		PRNGCHK(Array->List,&(Array->List[Where + 1]),
			(Array->RealElementCount - Where - 1) * sizeof(void*));
		PRNGCHK(Array->List,&(Array->List[Where]),
			(Array->RealElementCount - Where - 1) * sizeof(void*));
		MoveData((char*)&(Array->List[Where + 1]),(char*)&(Array->List[Where]),
			(Array->RealElementCount - Where - 1) * sizeof(void*));
		Array->RealElementCount -= 1;
		if (Array->ListElementCount - Array->RealElementCount >= ARRAYMAXFREECOUNT)
			{
				long				NewSize;
				void**			NewBlock;

				/* if this fails, well... too bad */
				NewSize = Array->ListElementCount - ARRAYDELTACOUNT;
				ERROR(NewSize < 0,PRERR(ForceAbort,
					"ArrayDeleteElement:  reducing array to negative size"));
				NewBlock = (void**)ResizePtr((char*)(Array->List),NewSize * sizeof(void*));
				if (NewBlock != NIL)
					{
						Array->List = NewBlock;
						Array->ListElementCount = NewSize;
					}
			}
	}


/* search the array to find the position of the specified element.  if the */
/* element can't be found in the array, it returns -1. */
long					ArrayFindElement(ArrayRec* Array, void* Element)
	{
		long					Scan;

		CheckPtrExistence(Array);
		for (Scan = 0; Scan < Array->RealElementCount; Scan += 1)
			{
				if ((Array->List)[Scan] == Element)
					{
						return Scan;
					}
			}
		return -1;
	}


/* return a duplicate of the array */
ArrayRec*			DuplicateArray(ArrayRec* Original)
	{
		ArrayRec*		Copy;
		long				ListSize;

		CheckPtrExistence(Original);
		Copy = (ArrayRec*)AllocPtrCanFail(sizeof(ArrayRec),"ArrayRecDup");
		if (Copy == NIL)
			{
			 FailurePoint1:
				return NIL;
			}
		ListSize = PtrSize((char*)Original->List);
		Copy->List = (void**)AllocPtrCanFail(ListSize,"ArrayListDup");
		if (Copy->List == NIL)
			{
			 FailurePoint2:
				ReleasePtr((char*)Copy);
				goto FailurePoint1;
			}
		CopyData((char*)Original->List,(char*)Copy->List,ListSize);
		Copy->RealElementCount = Original->RealElementCount;
		Copy->ListElementCount = Original->ListElementCount;
		return Copy;
	}
