/*	XGHandle.cpp
 *
 *		This is the code to handle handles. Rather than create a
 *	universal implementation, I actually wrapper the native
 *	handle implementation, with a few optimizations to reduce
 *	calls to the OS
 */

/**********************************************************************

  YAAF is Copyright 1997-1999 by William Woody.

  Redistribution and use in source and binary forms, with or without 
  modification, are permitted provided that the following conditions 
  are met:

  Redistributions of source code must retain the above copyright notice, 
  this list of conditions and the following disclaimer.

  Neither the name of the copyright holder or other contributors may
  be used to promote products derived from this software without
  specific written prior permission.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 
  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
  SOFTWARE.
  
  Contact:   woody@alumni.caltech.edu
  Web page:  http://www.pandawave.com/yaaf/

**********************************************************************/

#include <XConfig.h>			// must be first to optimize compile on MSVC++
#include <XHandle.h>
#include <XError.h>
#include <XDataUtil.h>
#include <stdlib.h>
#include <string.h>

void XGHandle::Validate()
{
#if OPT_MACOS
	if (!fData) {
		Assert( fSize == 0 );
		Assert( fAlloc == 0 );
		Assert( fLock == 0 );
	} else {
		Assert( *fData );
		long realsize = ::GetHandleSize( fData );
		long err = ::MemError();
		Assert( !err );
		Assert( fAlloc == realsize );
		Assert( fSize >= 0 && fSize <= fAlloc );
		long state = ::HGetState( fData );
		err = ::MemError();
		Assert( !err );
		if (state & 0x80) {
			Assert( fLock > 0 );
		} else {
			Assert( fLock == 0 );
		}
	}
#endif
}

#define Validate() ((void)0)

/************************************************************************/
/*																		*/
/*	Construction/Destruction											*/
/*																		*/
/************************************************************************/

/*$	XGHandle::XGHandle
 *
 *		Create me. The #pagesize# value is the number of bytes which I
 *	try to allocate at a time to satisfy handle resize requests. If
 *	ommitted, the #pagesize# value for this handle defaults to 512 bytes.
 *
 *		The #size# value gives the initial size to create this handle.
 *	If ommitted, this creates an empty handle.
 *
 *		The reason why we specify a #pagesize# for a handle is to
 *	minimize the number of times the handle has to be resized in
 *	memory. When a SetSize() call is made which can be satisfied
 *	with the current size of the handle, the memory this handle
 *	consumes is not reallocated. This makes an XGHandle fast at handling
 *	small resize requests.
 */

XGHandle::XGHandle(unsigned long size, unsigned short pagesize)
{
	fSize = 0;
	fPageSize = pagesize;
	fAlloc = 0;
	fLock = 0;
	fData = NULL;
	
	if (fPageSize == 0) fPageSize = 512;			// default size
	
	SetSize(size);
}

/*$	XGHandle::~XGHandle
 *
 *		Delete me. This releases the memory associated with this handle.
 */

XGHandle::~XGHandle()
{
#if OPT_MACOS
	if (fData) ::DisposeHandle(fData);
#endif

#if OPT_WINOS || OPT_XWIN
	if (fData) ::free(fData);
#endif
}

/*$	XGHandle::operator =
 *
 *		Copy operator; this copies the bits across, but does not update
 *	the page size. Instead, the old handle is first resized using it's
 *	current #pagesize# parameter.
 */

XGHandle &XGHandle::operator = (const XGHandle &h)
{
	SetSize(h.GetSize());
	memcpy(**this,*h,h.GetSize());
	return *this;
}

/************************************************************************/
/*																		*/
/*	Memory access														*/
/*																		*/
/************************************************************************/

/*$	XGHandle::operator *
 *
 *		This returns a pointer to the block of memory associated with
 *	this handle object. This is a quick and dirty way to get at the
 *	data in this handle, and will even return a pointer when the handle
 *	is not locked. 
 *
 *		Note that you should only get the pointer to this handle when
 *	the handle is unlocked if you know you are going to be performing
 *	a fast operation that won't move memory. Otherwise, the pointer may
 *	be invalidated right out from underneath you, causing a memory
 *	access error.
 */

void *XGHandle::operator *() const
{
#if OPT_MACOS
	return (void *)(*fData);
#endif

#if OPT_WINOS || OPT_XWIN
	return fData;
#endif
}

/*$	XGHandle::Lock
 *
 *		This locks the handle. Locking a handle guarentees that the
 *	data associated with this handle will not move. You should use 
 *	the XGHandleLock object to lock this handle in an exception-safe 
 *	manner.
 *
 *		Note that attempts to resize this handle will fail if the
 *	handle is locked.
 *
 *		This also maintains a "lock count" associated with this handle.
 *	That makes sure that nested calls which lock this handle will
 *	properly balance without accidently unlocking the handle
 *	prematurely.
 */

void XGHandle::Lock()
{
	Assert( fData );
	Validate();
	
#if OPT_MACOS
	if (0 == fLock) ::HLock(fData);
#endif

	fLock++;
}

/*$	XGHandle::Unlock
 *
 *		Unlock memory. You should use the XGHandleLock object to unlock
 *	this handle in an exception/safe manner.
 */

void XGHandle::Unlock()
{
	Assert( fData );
	Validate();
	
	if (fLock > 0) {
		fLock--;

#if OPT_MACOS
		if (0 == fLock) ::HUnlock(fData);
#endif
	}
}

/************************************************************************/
/*																		*/
/*	Memory access														*/
/*																		*/
/************************************************************************/

/*$	XGHandle::SetSize
 *
 *		Set the new size of this handle. This will work unless it's
 *	time to reallocate the data--in that case, this may fail with
 *	an exception.
 *
 *		If you attemp to call this routine on a locked handle, you
 *	will get an exception error, regardless if the operation would
 *	otherwise not move memory.
 */

void XGHandle::SetSize(unsigned long size)
{
	unsigned long newalloc;
	
	/*
	 *	Don't do it if locked!
	 */
	
	Assert(fLock == 0);
	Validate();
	
	/*
	 *	Get the page size to allocate
	 */
	
	newalloc = size;
	if (newalloc % fPageSize) newalloc += fPageSize - newalloc % fPageSize;
	if (newalloc == 0) newalloc = fPageSize;

	/*
	 *	Now see if I have to reallocate
	 */
	
	if (newalloc != fAlloc) {
		/*
		 *	Special case: is fAlloc zero? If it is, allocate memory.
		 */
		
#if OPT_MACOS
		if (fAlloc == 0) {
			fData = ::NewHandle(newalloc);
			if (fData == NULL) throw XGPostError(KXGOutMemory);
		} else {
			::SetHandleSize(fData,newalloc);
			OSErr err = ::MemError();
			if (err == memFullErr) {
				if (newalloc > fAlloc) throw XGPostError(KXGOutMemory);
			}
		}
#endif
#if OPT_WINOS || OPT_XWIN
		if (fAlloc == 0) {
			fData = ::malloc(newalloc);
			if (fData == NULL) throw XGPostError(KXGOutMemory);
		} else {
			void *newptr;
			newptr = ::realloc(fData,newalloc);
			if (newptr == NULL) {
				if (newalloc > fAlloc) throw XGPostError(KXGOutMemory);
			}
			fData = newptr;
		}
#endif

		fAlloc = newalloc;
	}
	
	/*
	 *	And set the new size of me
	 */
	
	fSize = size;
	
	Validate();
}

/************************************************************************/
/*																		*/
/*	Manipulation Utilities												*/
/*																		*/
/************************************************************************/

/*$	XGHandle::Append
 *
 *		Append a block of memory to the end of this thing. This is the
 *	same as calling handle.Insert(handle.GetSize(),len,ptr);
 *
 *		This operation will fail if the handle is locked.
 */

void XGHandle::Append(unsigned long len, void *ptr)
{
	Insert(GetSize(),len,ptr);
}

/*$	XGHandle::Insert
 *
 *		Insert memory into the spot specified. If the value of #off#
 *	is zero, this will insert the memory block at the start of the
 *	handle. If #off# is greater than or equal to GetSize(), this will
 *	append the memory at the end of this handle.
 *
 *		This operation will fail if the handle is locked.
 */

void XGHandle::Insert(unsigned long off, unsigned long len, void *ptr)
{
	unsigned long oldlen;
	unsigned char *memory;
	
	if (len == 0) return;
	
	/*
	 *	Grow the memory
	 */
	
	oldlen = GetSize();
	if (off > oldlen) off = oldlen;			// at worse, append.
	SetSize(oldlen + len);
	memory = (unsigned char *)(**this);
	
	/*
	 *	Now move the high block of the old chunk of memory up
	 */
	
	if (off < oldlen) {
		::memmove(memory + off + len, memory + off, oldlen - off);
	}
	
	/*
	 *	And move memory block into the space just opened up
	 */
	
	if (ptr) {
		::memmove(memory + off, ptr, len);
	} else {
		::memset(memory + off, 0, len);
	}
}

/*$	XGHandle::Delete
 *
 *		Delete a chunk of memory from the spot specified. This will
 *	delete all of the bytes starting from #off# and running #len#
 *	bytes. The values for #off# and #len# are truncated to make sure
 *	they fit in range.
 *
 *		This operation will fail if the handle is locked.
 */

void XGHandle::Delete(unsigned long off, unsigned long len)
{
	unsigned long oldlen;
	unsigned char *memory;
	
	if (len == 0) return;
	
	Assert(fLock == 0);	// Safer to do this now than discover it too late

	/*
	 *	Move the high block down
	 */
	
	oldlen = GetSize();
	if (off + len < oldlen) {
		memory = (unsigned char *)(**this);
		::memmove(memory + off, memory + off + len, oldlen - off - len);
	}
	
	/*
	 *	Shrink the handle size
	 */
	
	SetSize(oldlen - len);
}

/*$	XGHandle::Replace
 *
 *		Replace a chunk of memory with a new chunk, adjusting the size
 *	of the space where the chunk should go to fit.
 */

void XGHandle::Replace(unsigned long off, unsigned long olen,
										  unsigned long nlen,
										  void *ptr)
{
	if (olen > nlen) {
		Delete(off,olen - nlen);
	} else if (olen < nlen) {
		Insert(off,nlen - olen);
	}
	if (ptr) {
		unsigned char *memory = (unsigned char *)(**this);
		::memmove(memory + off, ptr, nlen);
	}
}

/************************************************************************/
/*																		*/
/*	Memory extraction routines											*/
/*																		*/
/*		One common use for handles is reading in a large datafile into	*/
/*	memory and manipulating it in a portable way. This provides some	*/
/*	support for that.													*/
/*																		*/
/************************************************************************/

/*$	XGHandle::GetShort
 *
 *		Get the short integer in a portable way. This converts the word
 *	object from network byte order to the native order of this platform.
 */

short XGHandle::GetShort(unsigned long off) const
{
	short retval;
	
	::memmove(&retval,((char *)**this + off),sizeof(retval));
	return NSwapWord(retval);
}

/*$	XGHandle::GetLong
 *
 *		Get the long integer in a portable way. This converts the word
 *	object from network byte order to the native order of this platform.
 */

long XGHandle::GetLong(unsigned long off) const
{
	long retval;
	
	::memmove(&retval,((char *)**this + off),sizeof(retval));
	return NSwapLong(retval);
}

/*$	XGHandle::GetMemory
 *
 *		Copy memory from handle into array. This copies the memory from
 *	the location specified into #data#, which should be allocated big
 *	enough to handle this operation.
 */

void XGHandle::GetMemory(unsigned long off, unsigned long len, void *data) const
{
	::memmove((char *)data,((char *)**this + off),len);
}

/*$	XGHandle::SetShort
 *
 *		Set the short integer in a portable way. This stores the word
 *	object from native order of this platform to network order.
 */

void XGHandle::SetShort(unsigned long off, short value)
{
	value = NSwapWord(value);
	::memmove(((char *)**this + off),&value,sizeof(value));
}

/*$	XGHandle::SetLong
 *
 *		Set the long integer in a portable way This stores the word
 *	object from native order of this platform to network order.
 */

void XGHandle::SetLong(unsigned long off, long value)
{
	value = NSwapLong(value);
	::memmove(((char *)**this + off),&value,sizeof(value));
}

/*$	XGHandle::SetMemory
 *
 *		Copy memory from array into handle. The data in
 *	#data# should be the same as the size provided in #len#.
 */

void XGHandle::SetMemory(unsigned long off, unsigned long len, const void *data)
{
	::memmove(((char *)**this + off),(const char *)data,len);
}



void XGHandle::SwapContents( XGHandle &a, XGHandle &b )
{
	unsigned short temp2;
	unsigned long temp4;
	
#if OPT_MACOS
	Handle tempH;
#endif

#if OPT_XWIN || OPT_WINOS
	void * tempH;
#endif
	//a.Validate();
	//b.Validate();
	
	temp2 = a.fLock;
	a.fLock = b.fLock;
	b.fLock = temp2;
	
	temp4 = a.fSize;
	a.fSize = b.fSize;
	b.fSize = temp4;
	
	temp4 = a.fAlloc;
	a.fAlloc = b.fAlloc;
	b.fAlloc = temp4;
	
	tempH = a.fData;
	a.fData = b.fData;
	b.fData = tempH;
	
	//a.Validate();
	//b.Validate();
	
}


