/*	XHandle.h
 *
 *		Most OS systems I support have the concept of a handle,
 *	a block of resizable memory
 */

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

  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/

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

#ifndef PandaWave_XHANDLE
#define PandaWave_XHANDLE

#include <XConfig.h>

/**@name Support--Handle-based memory allocation
 *
 *	These classes encapsulate the concept of a "handle", a chunk of
 *	movable, resizable memory which can store an arbitrary stream of
 *	data. The fundamental object of all of these is the 'XGHandle'
 *	object; this encapsulates a Macintosh handle on the MacOS, and
 *	encapsulates the 'malloc()'/'realloc()' calls on Windows in a
 *	safe manner.
 *
 *	This forms the basic allocation type used by many other routines
 *	and classes in YAAF. In particular, the XGStringList object is
 *	the basis of menu processing for YAAF.
 */
 
//@{

/************************************************************************/
/*																		*/
/*	External Interface													*/
/*																		*/
/************************************************************************/

/*$	XGHandle
 *
 *		This is the wrapper to a block of movable memory. This
 *	encapsulates a chunk of memory, and provides "lock" and
 *	"unlock" semantics, as well as platform-neutral data access
 *	to arbitrary 2-byte and 4-byte wide data.
 */

class XGHandle {
	public:
								XGHandle(unsigned long size = 0, 
										 unsigned short pagesize = 0);
		virtual					~XGHandle();
		
		XGHandle				&operator = (const XGHandle &);
		
		/*
		 *	Memory access
		 */
		
		void					*operator *() const;
		
		/*
		 *	Memory size
		 */
	
		
		/*$	XGHandle::GetSize
		 *
		 *		Returns the size of this handle. This returns the
		 *	number of bytes that is currently allocated for this handle,
		 *	and not the number of bytes this handle consumes in memory.
		 *	(Generally the amount of memory is larger, due to the
		 *	'pagesize' value provided this routine.)
		 */
		
		unsigned long			GetSize() const
									{
										return fSize;
									}
		void					SetSize(unsigned long);
		
		/*
		 *	Memory manipulation utilities
		 */
		
		void					Append(unsigned long len,
									   void *ptr = 0);
		void					Insert(unsigned long off,
									   unsigned long len,
									   void *ptr = 0);
		void					Delete(unsigned long off,
									   unsigned long len);
		void					Replace(unsigned long off,
										unsigned long olen,
										unsigned long nlen,
										void *ptr = 0);
									   
		/*
		 *	Memory extraction routines
		 */
		
		/*$	XGHandle::GetChar
		 *
		 *		Gets the character at the specified byte offset.
		 *	This returns the 8-bit byte at the location specified.
		 */
		
		char					GetChar(unsigned long off) const
									{
										return *((char *)**this + off);
									}
		short					GetShort(unsigned long offset) const;
		long					GetLong(unsigned long offset) const;
		void					GetMemory(unsigned long offset, unsigned long size, void *out) const;
		
		/*$	XGHandle::SetChar
		 *
		 *		Sets the character at the specified byte offset to
		 *	the byte provided. This is the inverse of the XGHandle::GetChar
		 *	routine and is a convenient way to pull bytes out of the
		 *	handle without dereferencing the handle.
		 */
		
		void					SetChar(unsigned long off, char c)
									{
										*((char *)**this + off) = c;
									}
		void					SetShort(unsigned long offset,short);
		void					SetLong(unsigned long offset,long);
		void					SetMemory(unsigned long offset, unsigned long size, const void *in);

		/*
		 *	Lock/unlock semantics (prefered: use the XGHandleLock object)
		 */
				
		void					Lock();
		void					Unlock();
		
		static void				SwapContents( XGHandle &a, XGHandle &b );
		
		void					Validate();
		
	private:
		unsigned short			fPageSize;
		unsigned short			fLock;
		unsigned long			fSize;
		unsigned long			fAlloc;
		
#if OPT_MACOS
		Handle					fData;
#endif

#if OPT_XWIN || OPT_WINOS
		void					*fData;
#endif
};

/*$	XGHandleLock
 *
 *		This is a stack-based class which locks the handle. This
 *	provides an exception-safe mechanism for locking the state of
 *	a handle, and in conjunction with the XGHandle::Lock and
 *	XGHandle::Unlock methods, provides a safe way to handle nested
 *	handle locking and unlocking.
 *
 *		To use this object, you simply declare a stack-based
 *	object with the handle you wish to lock as it's constructor
 *	parameter. When that variable leaves scope, the handle will
 *	be unlocked.
 */

class XGHandleLock {
	public:

		/*$	XGHandleLock::XGHandleLock
		 *
		 *		This constructor stores a pointer to the handle
		 *	provided, and locks the handle. The handle will be
		 *	unlocked when the destructor is invoked, which is
		 *	when this variable goes out of scope.
		 */
		
								XGHandleLock(XGHandle &ptr)
									{
										fPtr = &ptr;
										fPtr->Lock();
									}
		
		/*$	XGHandleLock::~XGHandleLock
		 *
		 *		The destructor unlocks the handle that was provided.
		 *	For a stack-based object, this is normally called during
		 *	stack cleanup.
		 */
		
								~XGHandleLock()
									{
										fPtr->Unlock();
									}
	private:
		XGHandle				*fPtr;
};

/************************************************************************/
/*																		*/
/*	Complex Data Types													*/
/*																		*/
/************************************************************************/

/*$	XGStringList
 *
 *		This manages a list of strings, used by various pieces of the
 *	YAAF code. Not only does this store a string list, but this also
 *	provides a mechanism for storing "extra" bytes. These extra bytes,
 *	no more than a few dozen fixed bytes per string, provide a way
 *	to store extra data, such as an index or a pointer to another
 *	object.
 *
 *		The string list is constructed from a single handle, reducing
 *	memory fragmentation.
 */

class XGStringList : private XGHandle {
	public:
								XGStringList(short extraData = 0);
		virtual					~XGStringList();
		
		XGStringList			&operator = (const XGStringList &);
		
		/*
		 *	String list interface
		 */
		
		/*$	XGStringList::GetNumStrings
		 *
		 *		Returns the number of strings that are stored in
		 *	this XGStringList object.
		 */
		
		short					GetNumStrings() const
									{
										return fNumber;
									}
		
		void					GetString(long,char *) const;
		void					SetString(long,const char *);
		void					GetExtra(long, void *) const;
		void					SetExtra(long, const void *);

		/*$	XGStringList::GetExtraSize
		 *
		 *		Gets the size of the extra field associated with
		 *	each string, in bytes.
		 */
		
		short					GetExtraSize()
									{
										return fExtra;
									}
		void					SetExtraSize(short);
		void					Insert(long,const char *str = 0);
		
		/*$	XGStringList::Append
		 *
		 *		Appends the specified string to the end of this
		 *	string list. This is the equivalent of calling
		 *	stringList.Insert(stringList.GetNumStrings(),str);
		 */
		
		void					Append(const char *str = 0)
									{
										Insert(fNumber,str);
									}
		void					Delete(long);
		void					DeleteAll();
		
	private:
		short					fNumber;
		short					fExtra;
};


//@}

#endif // PandaWave_XHANDLE
