/*	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 - Yet another application framework
 *  Copyright (C) 1997, 1998 William Edward Woody
 *  
 *  This library is free software; you can redistribute it
 *  and/or modify it under the terms of the GNU Library
 *  General Public License as published by the Free Software
 *  Foundation; either version 2 of the License, or any
 *  later version.
 *  
 *  This library is distributed in the hope that it will be
 *  useful, but WITHOUT ANY WARRANTY; without even the implied
 *  warranty of MERCHANTABIILITY or FITNESS FOR A PARTICULAR
 *  PURPOSE. See the GNU Library General Public License for
 *  more details.
 *  
 *  You should have received a copy of the GNU Library General
 *  Public License along with this library; if not, write to the
 *  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 *  Boston, MA 02111-1307, USA.
 *  
 *  To contact the author, either e-mail me at woody@pandawave.com, 
 *	or write to
 *  
 *          William Edward Woody
 *          In Phase Consulting
 *          1545 Ard Eevin Avenue
 *          Glendale, CA 91202
 *
 *	Or visit us on the web at http://www.pandawave.com
 */

#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>

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

// @G"Construction/Destruction"

/*	@F"XGHandle::XGHandle" @I
 *
 *		Create me. The 'pagesize' value is the number of bytes which I
 *	try to allocate at a time to satisfy handle resize requests.
 */

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

/*	@F"XGHandle::~XGHandle" @I
 *
 *		Delete me
 */

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

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

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

// @G"Memory Access"

/*	@F"XGHandle::operator *" @I
 *
 *		This is the quick and dirty way to access memory without
 *	locking the handle. Best to lock the handle instead
 */

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

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

/*	@F"XGHandle::Lock" @I
 *
 *		This locks the handle. You should use the XGHandleLock object
 *	to lock this handle in an exception-safe manner.
 */

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

	fLock++;
}

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

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

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

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

//  @G"Memory size"

/*	@F"XGHandle::SetSize" @I
 *
 *		Set the new size of this handle. This will work unless it's
 *	time to reallocate the data--in that case, this may fail
 */

void XGHandle::SetSize(unsigned long size)
{
	unsigned long newalloc;
	
	/*
	 *	Don't do it if locked!
	 */
	
	Assert(fLock == 0);
	
	/*
	 *	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);
			if (::MemError()) {
				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;
}

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

// @G"Memory manipulation"

/*	@F"XGHandle::Append" @I
 *
 *		Append a block of memory to the end of this thing
 */

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

/*	@F"XGHandle::Insert" @I
 *
 *		Insert memory into the spot specified
 */

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);
	}
}

/*	@F"XGHandle::Delete" @I
 *
 *		Delete a chunk of memory from the spot specified
 */

void XGHandle::Delete(unsigned long off, unsigned long len)
{
	unsigned long oldlen;
	unsigned char *memory;
	
	if (len == 0) return;

	/*
	 *	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);
}

/************************************************************************/
/*																		*/
/*	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.													*/
/*																		*/
/************************************************************************/

//  @G"Memory extraction"

/*	@F"XGHandle::GetShort" @I
 *
 *		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);
}

/*	@F"XGHandle::GetLong" @I
 *
 *		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);
}

/*	@F"XGHandle::GetMemory" @I
 *
 *		Copy memory from handle into array
 */

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

/*	@F"XGHandle::SetShort" @I
 *
 *		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));
}

/*	@F"XGHandle::SetLong" @I
 *
 *		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));
}

/*	@F"XGHandle::SetMemory" @I
 *
 *		Copy memory from array into handle
 */

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

