/* StringThing.c */
/*****************************************************************************/
/*                                                                           */
/*    Out Of Phase:  Digital Music Synthesis on General Purpose Computers    */
/*    Copyright (C) 1994  Thomas R. Lawrence                                 */
/*                                                                           */
/*    This program is free software; you can redistribute it and/or modify   */
/*    it under the terms of the GNU General Public License as published by   */
/*    the Free Software Foundation; either version 2 of the License, or      */
/*    (at your option) any later version.                                    */
/*                                                                           */
/*    This program 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.  See the          */
/*    GNU General Public License for more details.                           */
/*                                                                           */
/*    You should have received a copy of the GNU General Public License      */
/*    along with this program; if not, write to the Free Software            */
/*    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.              */
/*                                                                           */
/*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
/*                                                                           */
/*****************************************************************************/

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

#include "StringThing.h"
#include "Memory.h"


#define INITIALSTRINGTHINGLENGTH (32)


struct StringThingRec
	{
		char*					TheBuffer;
		long					ActualLength;
	};


/* create a new string thing. */
StringThingRec*			NewStringThing(void)
	{
		StringThingRec*		StringThing;

		StringThing = (StringThingRec*)AllocPtrCanFail(sizeof(StringThingRec),
			"StringThingRec");
		if (StringThing == NIL)
			{
			 FailurePoint1:
				return NIL;
			}
		StringThing->TheBuffer = AllocPtrCanFail(INITIALSTRINGTHINGLENGTH,
			"StringThingRec buffer");
		if (StringThing->TheBuffer == NIL)
			{
			 FailurePoint2:
				ReleasePtr(StringThing->TheBuffer);
				goto FailurePoint1;
			}
		StringThing->ActualLength = 0;
		return StringThing;
	}


/* dispose of a string thing */
void								DisposeStringThing(StringThingRec* StringThing)
	{
		CheckPtrExistence(StringThing);
		ReleasePtr(StringThing->TheBuffer);
		ReleasePtr((char*)StringThing);
	}


/* add some data onto the end of the string thing */
MyBoolean						AppendStringThing(StringThingRec* StringThing,
											char* Data, long Length)
	{
		char*							NewBuffer;
		long							CurrentSize;

		CheckPtrExistence(StringThing);
		CurrentSize = PtrSize(StringThing->TheBuffer);
		if (StringThing->ActualLength + Length > CurrentSize)
			{
				long							NewSize;

				if (StringThing->ActualLength + Length > CurrentSize * 2)
					{
						NewSize = StringThing->ActualLength + Length;
					}
				 else
					{
						NewSize = CurrentSize * 2;
					}
				NewBuffer = AllocPtrCanFail(NewSize,"StringThingRec buffer");
				if (NewBuffer == NIL)
					{
						return False;
					}
				CopyData(&(StringThing->TheBuffer[0]),&(NewBuffer[0]),CurrentSize);
				ReleasePtr(StringThing->TheBuffer);
				StringThing->TheBuffer = NewBuffer;
			}
		PRNGCHK(StringThing->TheBuffer,&(StringThing->TheBuffer[StringThing->ActualLength]),
			Length);
		CopyData(&(Data[0]),&(StringThing->TheBuffer[StringThing->ActualLength]),Length);
		StringThing->ActualLength += Length;
		return True;
	}


/* look up a byte in the string thing */
char								LookupInStringThing(StringThingRec* StringThing, long Index)
	{
		CheckPtrExistence(StringThing);
		ERROR((Index < 0) || (Index >= StringThing->ActualLength),PRERR(ForceAbort,
			"LookupInStringThing:  array bounds violation"));
		PRNGCHK(StringThing->TheBuffer,&(StringThing->TheBuffer[Index]),sizeof(char));
		return StringThing->TheBuffer[Index];
	}


/* find out how long the string thing is */
long								GetStringThingLength(StringThingRec* StringThing)
	{
		CheckPtrExistence(StringThing);
		return StringThing->ActualLength;
	}


/* delete a block of data from the string thing */
void								StringThingDeleteStuff(StringThingRec* StringThing,
											long StartIndex, long NumBytes)
	{
		CheckPtrExistence(StringThing);
		ERROR((StartIndex < 0) || (StartIndex + NumBytes >= StringThing->ActualLength)
			|| (NumBytes > StringThing->ActualLength) || (NumBytes < 0),PRERR(ForceAbort,
			"StringThingDeleteStuff:  array bounds violation"));
		MoveData(&(StringThing->TheBuffer[StartIndex + NumBytes]),
			&(StringThing->TheBuffer[StartIndex]),NumBytes);
		StringThing->ActualLength -= NumBytes;
	}


/* return the index of a character in the buffer or -1 if it's not found */
long								StringThingSearchForChar(StringThingRec* StringThing, char Thing)
	{
		long							Limit;
		long							Scan;

		CheckPtrExistence(StringThing);
		Limit = StringThing->ActualLength;
		for (Scan = 0; Scan < Limit; Scan += 1)
			{
				if (Thing == StringThing->TheBuffer[Scan])
					{
						return Scan;
					}
			}
		return -1;
	}


/* get a subrange of the string thing */
char*								StringThingGetSubrange(StringThingRec* StringThing,
											long StartIndex, long NumBytes)
	{
		char*							Buffer;

		CheckPtrExistence(StringThing);
		ERROR((StartIndex < 0) || (StartIndex + NumBytes > StringThing->ActualLength)
			|| (NumBytes > StringThing->ActualLength) || (NumBytes < 0),PRERR(ForceAbort,
			"StringThingGetSubrange:  array bounds violation"));
		Buffer = AllocPtrCanFail(NumBytes,"StringThingGetSubrange");
		if (Buffer == NIL)
			{
				return NIL;
			}
		PRNGCHK(StringThing->TheBuffer,&(StringThing->TheBuffer[StartIndex]),NumBytes);
		PRNGCHK(Buffer,&(Buffer[0]),NumBytes);
		MoveData(&(StringThing->TheBuffer[StartIndex]),&(Buffer[0]),NumBytes);
		return Buffer;
	}
