//------------------------------------------------------------------------------
// Desc:	This file contains misc toolkit functions
//
// Tabs:	3
//
//		Copyright (c) 2000-2006 Novell, Inc. All Rights Reserved.
//
//		This program is free software; you can redistribute it and/or
//		modify it under the terms of version 2 of the GNU General Public
//		License as published by the Free Software Foundation.
//
//		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, contact Novell, Inc.
//
//		To contact Novell about this file by physical or electronic mail,
//		you may find current contact information at www.novell.com
//
// $Id: ftkmisc.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $
//------------------------------------------------------------------------------

#include "ftksys.h"

static FLMATOMIC					gv_startupCount = 0;
static FLMUINT						gv_uiRandomGenInitCount = 0;
static F_MUTEX						gv_hRandomGenMutex = F_MUTEX_NULL;
static IF_RandomGenerator *	gv_pRandomGenerator = NULL;
static IF_ThreadMgr *			gv_pThreadMgr = NULL;
static IF_FileSystem *			gv_pFileSystem = NULL;
static FLMUINT						gv_uiMaxFileSize = FLM_MAXIMUM_FILE_SIZE;
static F_XML *						gv_pXml = NULL;

FLMATOMIC							gv_openFiles = 0;
F_MUTEX 								F_FileHdl::m_hAsyncListMutex = F_MUTEX_NULL;
F_FileAsyncClient *				F_FileHdl::m_pFirstAvailAsync = NULL;
FLMUINT								F_FileHdl::m_uiAvailAsyncCount = 0;

FSTATIC RCODE f_initRandomGenerator( void);

FSTATIC void f_freeRandomGenerator( void);

#ifdef FLM_AIX
	#ifndef nsleep
		extern "C"
		{
			extern int nsleep( struct timestruc_t *, struct timestruc_t *);
		}
	#endif
#endif

/****************************************************************************
Desc:
****************************************************************************/
static FLMBYTE gv_ucSENLengthArray[] =
{
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,		// 0   - 15
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,		// 16  - 31
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,		// 32  - 47
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,		// 48  - 63
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,		// 64  - 79
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,		// 80  - 95
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,		// 96  - 111
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,		// 112 - 127
	2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,		// 128 - 143
	2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,		// 144 - 159
	2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,		// 160 - 175
	2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,		// 176 - 191
	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,		// 192 - 207
	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,		// 208 - 223
	4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,		// 224 - 239
	5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 8, 9		// 240 - 255
};

/****************************************************************************
Desc:
****************************************************************************/
static FLMBYTE ucSENPrefixArray[] =
{
	0,
	0,
	0x80,
	0xC0,
	0xE0,
	0xF0,
	0xF8,
	0xFC,
	0xFE,
	0xFF
};

/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI ftkStartup( void)
{
	RCODE		rc = NE_FLM_OK;
	
	if( f_atomicInc( &gv_startupCount) > 1)
	{
		goto Exit;
	}
	
	// Sanity check -- make sure we are using the correct
	// byte-swap macros for this platform

	f_assert( FB2UD( (FLMBYTE *)"\x0A\x0B\x0C\x0D") == 0x0D0C0B0A);
	f_assert( FB2UW( (FLMBYTE *)"\x0A\x0B") == 0x0B0A);
	
	// Verify that the platform word size is correct
	
#ifdef FLM_64BIT
	f_assert( sizeof( FLMUINT) == 8);
#else
	f_assert( sizeof( FLMUINT) == 4);
#endif

#if defined( FLM_RING_ZERO_NLM)
	if( RC_BAD( rc = f_netwareStartup()))
	{
		goto Exit;
	}
#endif

	f_memoryInit();

#if !defined( FLM_RING_ZERO_NLM)
	f_assert( sizeof( f_va_list) == sizeof( va_list));
#endif
	
	if( RC_BAD( rc = f_initCharMappingTables()))
	{
		goto Exit;
	}
	
	if( RC_BAD( rc = f_verifyDiskStructOffsets()))
	{
		goto Exit;
	}
	
	if( RC_BAD( rc = f_allocFileSystem( &gv_pFileSystem)))
	{
		goto Exit;
	}
	
	if( RC_BAD( rc = f_initFileAsyncClientList()))
	{
		goto Exit;
	}
	
	if( RC_BAD( rc = f_allocThreadMgr( &gv_pThreadMgr)))
	{
		goto Exit;
	}
	
	if( RC_BAD( rc = f_initRandomGenerator()))
	{
		goto Exit;
	}
	
	if( RC_BAD( rc = f_initCRCTable()))
	{
		goto Exit;
	}
	
	f_initFastCheckSum();
	
	if( (gv_pXml = f_new F_XML) == NULL)
	{
		rc = RC_SET( NE_FLM_MEM);
		goto Exit;
	}
		
	if( RC_BAD( rc = gv_pXml->setup()))
	{
		goto Exit;
	}

#ifdef FLM_DEBUG
	if( RC_BAD( rc = f_verifyMetaphoneRoutines()))
	{
		goto Exit;
	}
#endif
	
#if defined( FLM_LINUX)
	f_setupLinuxKernelVersion();
	gv_uiMaxFileSize = f_getLinuxMaxFileSize();
#elif defined( FLM_AIX)

	// Call setrlimit to increase the max allowed file size.
	// We don't have a good way to deal with any errors returned by
	// setrlimit(), so we just hope that there aren't any ...
	
	struct rlimit rlim;
	
	rlim.rlim_cur = RLIM_INFINITY;
	rlim.rlim_max = RLIM_INFINITY;
	
	setrlimit( RLIMIT_FSIZE, &rlim);
#endif

	// Setup logger

	if (RC_BAD( rc = f_loggerInit()))
	{
		goto Exit;
	}
	

Exit:

	if( RC_BAD( rc))
	{
		ftkShutdown();
	}
	
	return( rc);
}

/****************************************************************************
Desc:
****************************************************************************/
void FLMAPI ftkShutdown( void)
{
	if( !gv_startupCount || f_atomicDec( &gv_startupCount) > 0)
	{
		return;
	}
	
	f_assert( !gv_openFiles);
	
	if( gv_pThreadMgr)
	{
		gv_pThreadMgr->Release();
		gv_pThreadMgr = NULL;
	}
	
	f_freeFileAsyncClientList();
	
	if( gv_pFileSystem)
	{
		gv_pFileSystem->Release();
		gv_pFileSystem = NULL;
	}
	
	f_freeCRCTable();
	
	if( gv_pXml)
	{
		gv_pXml->Release();
	}

	f_loggerShutdown();
	f_freeRandomGenerator();
	f_freeCharMappingTables();
	f_memoryCleanup();
	
#if defined( FLM_RING_ZERO_NLM)
	f_netwareShutdown();
#endif
}

/****************************************************************************
Desc: This routine causes the calling process to delay the given number
		of milliseconds.  Due to the nature of the call, the actual sleep
		time is almost guaranteed to be different from requested sleep time.
****************************************************************************/
#ifdef FLM_UNIX
void FLMAPI f_sleep(
	FLMUINT		uiMilliseconds)
{
#ifdef FLM_AIX
	struct timestruc_t timeout;
	struct timestruc_t remain;
#else
	struct timespec timeout;
#endif

	timeout.tv_sec = (uiMilliseconds / 1000);
	timeout.tv_nsec = (uiMilliseconds % 1000) * 1000000;

#ifdef FLM_AIX
	nsleep(&timeout, &remain);
#else
	nanosleep(&timeout, 0);
#endif
}
#endif

/****************************************************************************
Desc:
****************************************************************************/
#ifdef FLM_WIN
void FLMAPI f_sleep(
	FLMUINT		uiMilliseconds)
{
	SleepEx( (DWORD)uiMilliseconds, true);
}
#endif

/****************************************************************************
Desc:
****************************************************************************/
#ifdef FLM_LIBC_NLM
void FLMAPI f_sleep( 
	FLMUINT		uiMilliseconds)
{
	if( !uiMilliseconds )
	{
		pthread_yield();
	}
	else
	{
		delay( uiMilliseconds);
	}
}
#endif

/****************************************************************************
Desc:
****************************************************************************/
#ifdef FLM_RING_ZERO_NLM
void FLMAPI f_sleep( 
	FLMUINT	uiMilliseconds)
{
	if( !uiMilliseconds)
	{
		kYieldThread();
	}
	else
	{
		kDelayThread( uiMilliseconds);
	}
}
#endif

/****************************************************************************
Desc:
****************************************************************************/
FSTATIC RCODE f_initRandomGenerator( void)
{
	FLMUINT					uiTime;
	RCODE						rc = NE_FLM_OK;

	if (++gv_uiRandomGenInitCount > 1)
	{
		goto Exit;
	}

	if( RC_BAD( rc = f_mutexCreate( &gv_hRandomGenMutex)))
	{
		goto Exit;
	}

	f_timeGetSeconds( &uiTime );

	if( RC_BAD( rc = FlmAllocRandomGenerator( &gv_pRandomGenerator)))
	{
		goto Exit;
	}

	gv_pRandomGenerator->setSeed( (FLMUINT32)(uiTime ^ (FLMUINT)f_getpid()));

Exit:

	return( rc);
}

/****************************************************************************
Desc:
****************************************************************************/
FSTATIC void f_freeRandomGenerator( void)
{
	if( (--gv_uiRandomGenInitCount) > 0)
	{
		return;
	}
	
	if( gv_pRandomGenerator)
	{
		gv_pRandomGenerator->Release();
		gv_pRandomGenerator = NULL;
	}

	if( gv_hRandomGenMutex != F_MUTEX_NULL)
	{
		f_mutexDestroy( &gv_hRandomGenMutex);
	}
}

/****************************************************************************
Desc:		This routine will use the operating system calls to generate a
			"globally unique" identifier.  Typically, this is based on the
			MAC address of an ethernet card installed in the machine.  If the
			machine does not have an ethernet card, or if the OS does not
			support generating GUIDs, this routine will generate a pseudo-GUID
			using a random number generator.  A serial number is 16-bytes.
****************************************************************************/
RCODE FLMAPI f_createSerialNumber(
	FLMBYTE *		pszSerialNum)
{
	RCODE						rc = NE_FLM_OK;

#if defined( FLM_WIN)

	UUID			uuidVal;
	RPC_STATUS	err = UuidCreate( &uuidVal);

	if (err == RPC_S_OK || err == RPC_S_UUID_LOCAL_ONLY)
	{
		UD2FBA( (FLMUINT32)uuidVal.Data1, &pszSerialNum[ 0]);
		UW2FBA( (FLMUINT16)uuidVal.Data2, &pszSerialNum[ 4]);
		UW2FBA( (FLMUINT16)uuidVal.Data3, &pszSerialNum[ 6]);
		f_memcpy( &pszSerialNum[ 8], (FLMBYTE *)uuidVal.Data4, 8);
		goto Exit;
	}

#elif defined( FLM_UNIX) || defined( FLM_NLM)

	// Generate a pseudo GUID value

	UD2FBA( f_getRandomUINT32(), &pszSerialNum[ 0]);
	UD2FBA( f_getRandomUINT32(), &pszSerialNum[ 4]);
	UD2FBA( f_getRandomUINT32(), &pszSerialNum[ 8]);
	UD2FBA( f_getRandomUINT32(), &pszSerialNum[ 12]);

#endif

#if defined( FLM_WIN)
Exit:
#endif

	return( rc);
}

/****************************************************************************
Desc: 	
****************************************************************************/
void FLMAPI f_getenv(
	const char *	pszKey,
	FLMBYTE *		pszBuffer,
	FLMUINT			uiBufferSize,
	FLMUINT *		puiValueLen)
{
	FLMUINT			uiValueLen = 0;

	if( !uiBufferSize)
	{
		goto Exit;
	}
	
	pszBuffer[ 0] = 0;
	
#if defined( FLM_WIN) || defined( FLM_UNIX)
	char *	pszValue;
	
   if( (pszValue = getenv( pszKey)) != NULL &&
		 (uiValueLen = f_strlen( pszValue)) < uiBufferSize)
	{
		f_strcpy( (char *)pszBuffer, pszValue);
	}
#else
	F_UNREFERENCED_PARM( pszKey);
#endif

Exit:

	if( puiValueLen)
	{
		*puiValueLen = uiValueLen;
	}

	return;
}

/***************************************************************************
Desc:		Sort an array of items
****************************************************************************/
void FLMAPI f_qsort(
	void *					pvBuffer,
	FLMUINT					uiLowerBounds,
	FLMUINT					uiUpperBounds,
	F_SORT_COMPARE_FUNC	fnCompare,
	F_SORT_SWAP_FUNC		fnSwap)
{
	FLMUINT			uiLBPos;
	FLMUINT			uiUBPos;
	FLMUINT			uiMIDPos;
	FLMUINT			uiCurrentPos;
	FLMUINT			uiLeftItems;
	FLMUINT			uiRightItems;
	FLMINT			iCompare;

Iterate_Larger_Half:

	uiUBPos = uiUpperBounds;
	uiLBPos = uiLowerBounds;
	uiMIDPos = (uiUpperBounds + uiLowerBounds + 1) / 2;
	uiCurrentPos = uiMIDPos;

	for (;;)
	{
		while (uiLBPos == uiMIDPos ||
					((iCompare = 
						fnCompare( pvBuffer, uiLBPos, uiCurrentPos)) < 0))
		{
			if( uiLBPos >= uiUpperBounds)
			{
				break;
			}
			uiLBPos++;
		}

		while( uiUBPos == uiMIDPos ||
					(((iCompare = 
						fnCompare( pvBuffer, uiCurrentPos, uiUBPos)) < 0)))
		{
			if (!uiUBPos)
			{
				break;
			}
			uiUBPos--;
		}
		
		if( uiLBPos < uiUBPos)
		{
			// Exchange [uiLBPos] with [uiUBPos].

			fnSwap( pvBuffer, uiLBPos, uiUBPos);
			uiLBPos++;
			uiUBPos--;
		}
		else
		{
			break;
		}
	}

	// Check for swap( LB, MID ) - cases 3 and 4

	if( uiLBPos < uiMIDPos )
	{

		// Exchange [uiLBPos] with [uiMIDPos]

		fnSwap( pvBuffer, uiMIDPos, uiLBPos);
		uiMIDPos = uiLBPos;
	}
	else if( uiMIDPos < uiUBPos )
	{
		// Exchange [uUBPos] with [uiMIDPos]

		fnSwap( pvBuffer, uiMIDPos, uiUBPos);
		uiMIDPos = uiUBPos;
	}

	// Check the left piece.

	uiLeftItems = (uiLowerBounds + 1 < uiMIDPos)
							? uiMIDPos - uiLowerBounds
							: 0;

	uiRightItems = (uiMIDPos + 1 < uiUpperBounds)
							? uiUpperBounds - uiMIDPos
							: 0;

	if( uiLeftItems < uiRightItems)
	{
		// Recurse on the LEFT side and goto the top on the RIGHT side.

		if( uiLeftItems)
		{
			f_qsort( pvBuffer, uiLowerBounds, uiMIDPos - 1, fnCompare, fnSwap);
		}

		uiLowerBounds = uiMIDPos + 1;
		goto Iterate_Larger_Half;
	}
	else if( uiLeftItems)
	{
		// Recurse on the RIGHT side and goto the top for the LEFT side.

		if (uiRightItems )
		{
			f_qsort( pvBuffer, uiMIDPos + 1, uiUpperBounds, fnCompare, fnSwap);
		}

		uiUpperBounds = uiMIDPos - 1;
		goto Iterate_Larger_Half;
	}
}

/***************************************************************************
Desc:
****************************************************************************/
FLMINT FLMAPI f_qsortUINTCompare(
	void *		pvBuffer,
	FLMUINT		uiPos1,
	FLMUINT		uiPos2)
{
	FLMUINT		uiLeft = *(((FLMUINT *)pvBuffer) + uiPos1);
	FLMUINT		uiRight = *(((FLMUINT *)pvBuffer) + uiPos2);

	if( uiLeft < uiRight)
	{
		return( -1);
	}
	else if( uiLeft > uiRight)
	{
		return( 1);
	}

	return( 0);
}

/***************************************************************************
Desc:
****************************************************************************/
void FLMAPI f_qsortUINTSwap(
	void *		pvBuffer,
	FLMUINT		uiPos1,
	FLMUINT		uiPos2)
{
	FLMUINT *	puiArray = (FLMUINT *)pvBuffer;
	FLMUINT		uiTmp = puiArray[ uiPos1];

	puiArray[ uiPos1] = puiArray[ uiPos2];
	puiArray[ uiPos2] = uiTmp;
}

/****************************************************************************
Desc:
****************************************************************************/
void * FLMAPI f_memcpy(
	void *			pvDest,
	const void *	pvSrc,
	FLMSIZET			iSize)
{
	if( iSize == 1)
	{
		*((FLMBYTE *)pvDest) = *((FLMBYTE *)pvSrc);
		return( pvDest);
	}
	
#ifdef FLM_RING_ZERO_NLM
		CMoveFast( pvSrc, pvDest, iSize);
		return( pvDest);
#else
		return( memcpy( pvDest, pvSrc, iSize));
#endif
}

/****************************************************************************
Desc:
****************************************************************************/
void * FLMAPI f_memmove(
	void *			pvDest,
	const void *	pvSrc,
	FLMSIZET			uiLength)
{
#ifndef FLM_RING_ZERO_NLM

	return( memmove( pvDest, pvSrc, uiLength));
	
#else

	#define CMOVB_THRESHOLD		16
	char			*s = (char *)pvSrc;
	char			*d = (char *)pvDest;
	unsigned		uDiff;

	if( (char *)(s + uiLength) < d || (char *)(d + uiLength) < s)
	{
		// The source and destination do not overlap.

		CMoveFast( (void *)s, d, (LONG)uiLength);
	}
	else if( s < d)
	{
		// Source preceeds the destination, with overlap.

		uDiff = (unsigned)(d - s);
		d += uiLength;
		s += uiLength;
		if( uDiff >= CMOVB_THRESHOLD)
		{
			for( ;;)
			{
				if( uiLength < uDiff)
				{
					break;
				}

				// Copy the tail

				s -= uDiff;
				d -= uDiff;
				uiLength -= uDiff;
				CMoveFast( (void *)s, d, (LONG)uDiff);
			}
		}

		// Copy remaining bytes.

		while( uiLength--)
		{
			*--d = *--s;
		}
	}
	else if( s > d)
	{
		// Source follows the destination, with overlap.

		uDiff = (unsigned)(s - d);
		if( uDiff >= CMOVB_THRESHOLD)
		{
			for( ;;)
			{
				if( uiLength < uDiff)
				{
					break;
				}

				// Copy the head

				CMoveFast( (void *)s, d, (LONG)uDiff);
				uiLength -= uDiff;
				d += uDiff;
				s += uDiff;
			}
		}

		// Copy the remaining bytes

		while( uiLength--)
		{
			*d++ = *s++;
		}
	}

	// Else, the regions overlap completely (s == d).  Do nothing.

	return( pvDest);

#endif
}

/****************************************************************************
Desc:
****************************************************************************/
void * FLMAPI f_memset(
	void *				pvMem,
	unsigned char		ucByte,
	FLMSIZET				uiLength)
{
#ifndef FLM_RING_ZERO_NLM
	return( memset( pvMem, ucByte, uiLength));
#else
	char *			cp = (char *)pvMem;
	unsigned			dwordLength;
	unsigned long	dwordVal;

	dwordVal = ((unsigned long)ucByte << 24) |
		((unsigned long)ucByte << 16) |
		((unsigned long)ucByte << 8) |
		(unsigned long)ucByte;

	while( uiLength && ((long)cp & 3L))
	{
		*cp++ = (char)ucByte;
		uiLength--;
	}

	dwordLength = uiLength >> 2;
	if(  dwordLength != 0)
	{
		CSetD( dwordVal, (void *)cp, dwordLength);
		cp += (dwordLength << 2);
		uiLength -= (dwordLength << 2);
	}

	while( uiLength)
	{
		*cp++ = (char)ucByte;
		uiLength--;
	}

	return( pvMem);
#endif
}

/****************************************************************************
Desc:
****************************************************************************/
FLMINT FLMAPI f_memcmp(
	const void *		pvMem1,
	const void *		pvMem2,
	FLMSIZET				uiLength)
{
#ifndef FLM_NLM
	return( memcmp( pvMem1, pvMem2, uiLength));
#else
	unsigned char *	s1;
	unsigned char *	s2;

	for (s1 = (unsigned char *)pvMem1, s2 = (unsigned char *)pvMem2; 
		uiLength > 0; uiLength--, s1++, s2++)
	{
		if (*s1 == *s2)
		{
			continue;
		}
		else if( *s1 > *s2)
		{
			return( 1);
		}
		else
		{
			return( -1);
		}
	}

	return( 0);
#endif
}

/****************************************************************************
Desc:
****************************************************************************/
char * FLMAPI f_strcpy(
	char *			pszDest,
	const char *	pszSrc)
{
#ifndef FLM_NLM
	return( strcpy( pszDest, pszSrc));
#else
	while ((*pszDest++ = *pszSrc++) != 0);
	return( pszDest);
#endif
}

/****************************************************************************
Desc:
****************************************************************************/
char * FLMAPI f_strncpy(
	char *			pszDest,
	const char *	pszSrc,
	FLMSIZET			uiLength)
{
#ifndef FLM_NLM
	return( strncpy( pszDest, pszSrc, uiLength));
#else
	while( uiLength)
	{
		*pszDest++ = *pszSrc;
		if( *pszSrc)
		{
			pszSrc++;
		}
		
		uiLength--;
	}

	*pszDest = 0;
	return( pszDest);
#endif
}

/****************************************************************************
Desc:
****************************************************************************/
FLMUINT FLMAPI f_strlen(
	const char *	pszStr)
{
#ifndef FLM_NLM
	return( strlen( pszStr));
#else
	const char *	pszStart = pszStr;

	while( *pszStr)
	{
		pszStr++;
	}

	return( pszStr - pszStart);
#endif
}

/****************************************************************************
Desc:
****************************************************************************/
FLMINT FLMAPI f_strcmp(
	const char *		pszStr1,
	const char *		pszStr2)
{
#ifndef FLM_NLM
	return( strcmp( pszStr1, pszStr2));
#else
	while( *pszStr1 == *pszStr2 && *pszStr1)
	{
		pszStr1++;
		pszStr2++;
	}
	
	return( (FLMINT)(*pszStr1 - *pszStr2));
#endif
}
	
/****************************************************************************
Desc:
****************************************************************************/
FLMINT FLMAPI f_stricmp(
	const char *		pszStr1,
	const char *		pszStr2)
{
#ifdef FLM_WIN
	return( _stricmp( pszStr1, pszStr2));
#else 
	while( f_toupper( *pszStr1) == f_toupper( *pszStr2) && *pszStr1)
	{
		pszStr1++;
		pszStr2++;
	}
	return( (FLMINT)( f_toupper( *pszStr1) - f_toupper( *pszStr2)));
#endif
}

/****************************************************************************
Desc:
****************************************************************************/
FLMINT FLMAPI f_strncmp(
	const char *		pszStr1,
	const char *		pszStr2,
	FLMSIZET				uiLength)
{
#ifndef FLM_NLM
	return( strncmp( pszStr1, pszStr2, uiLength));
#else
	while( *pszStr1 == *pszStr2 && *pszStr1 && uiLength)
	{
		pszStr1++;
		pszStr2++;
		uiLength--;
	}

	if( uiLength)
	{
		return( (*pszStr1 - *pszStr2));
	}

	return( 0);
#endif
}
	
/****************************************************************************
Desc:
****************************************************************************/
FLMINT FLMAPI f_strnicmp(
	const char *		pszStr1,
	const char *		pszStr2,
	FLMSIZET				uiLength)
{
#ifdef FLM_WIN
	return( _strnicmp( pszStr1, pszStr2, uiLength));
#else
	FLMINT				iLen = (FLMINT)uiLength;

	if( !pszStr1 || !pszStr2)
	{
		return( (pszStr1 == pszStr2) 
						? 0 
						: (pszStr1 ? 1 : -1));
	}

	while( iLen-- && *pszStr1 && *pszStr2 && 
		(f_toupper( *pszStr1) == f_toupper( *pszStr2)))
	{
		pszStr1++;
		pszStr2++;
	}

	return(	(iLen == -1)
					?	0
					:	(f_toupper( *pszStr1) - f_toupper( *pszStr2)));

#endif
}

/****************************************************************************
Desc:
****************************************************************************/
char * FLMAPI f_strcat(
	char *				pszDest,
	const char *		pszSrc)
{
#ifndef FLM_NLM
	return( strcat( pszDest, pszSrc));
#else
	const char *	p = pszSrc;
	char * 			q = pszDest;
	
	while (*q++);
	q--;
	while( (*q++ = *p++) != 0);
	
	return( pszDest);
#endif
}

/****************************************************************************
Desc:
****************************************************************************/
char * FLMAPI f_strncat(
	char *				pszDest,
	const char *		pszSrc,
	FLMSIZET				uiLength)
{
#ifndef FLM_NLM
	return( strncat( pszDest, pszSrc, uiLength));
#else
	const char *		p = pszSrc;
	char *				q = pszDest;
	
	while (*q++);
	
	q--;
	uiLength++;
	
	while( --uiLength)
	{
		if( (*q++ = *p++) == 0)
		{
			q--;
			break;
		}
	}
	
	*q = 0;
	return( pszDest);
#endif
}

/****************************************************************************
Desc:
****************************************************************************/
char * FLMAPI f_strchr(
	const char *		pszStr,
	unsigned char		ucByte)
{
#ifndef FLM_NLM
	return( (char *)strchr( pszStr, ucByte));
#else
	if( !pszStr)
	{
		return( NULL);
	}

	while (*pszStr && *pszStr != ucByte)
	{
		pszStr++;
	}

	return( (char *)((*pszStr == ucByte) 
								? pszStr
								: NULL));
#endif
}

/****************************************************************************
Desc:
****************************************************************************/
char * FLMAPI f_strrchr(
	const char *		pszStr,
	unsigned char		ucByte)
{
#ifndef FLM_NLM
	return( (char *)strrchr( pszStr, ucByte));
#else
	const char * pszLast = NULL;

	if( !pszStr)
	{
		return( NULL);
	}

	while (*pszStr)
	{
		if( *pszStr == ucByte)
		{
			pszLast = pszStr;
		}
		
		pszStr++;
	}
	
	if( ucByte == '\0')
	{
		pszLast = pszStr;
	}

	return( (char *)pszLast);
#endif
}

/****************************************************************************
Desc:
****************************************************************************/
char * FLMAPI f_strstr(
	const char *		pszStr1,
	const char *		pszStr2)
{
#ifndef FLM_NLM
	return( (char *)strstr( pszStr1, pszStr2));
#else
	FLMUINT 			i;
	FLMUINT			j;
	FLMUINT			k;

	if ( !pszStr1 || !pszStr2)
	{
		return( NULL);
	}

	for( i = 0; pszStr1[i] != '\0'; i++)
	{
		for( j=i, k=0; pszStr2[k] != '\0' &&
			pszStr1[j] == pszStr2[k]; j++, k++)
		{
			;
		}

		if ( k > 0 && pszStr2[k] == '\0')
		{
			return( (char *)&pszStr1[i]);
		}
	}

	return( NULL);
#endif
}

/****************************************************************************
Desc:		Turn a base 24 digit's ordinal value into a native
			alphanumeric value.
Notes:	This is a base 24 alphanumeric value where 
			{a, b, c, d, e, f, i, l, o, r, u, v } values are removed.
****************************************************************************/
FLMBYTE FLMAPI f_getBase24DigitChar( 
	FLMBYTE		ucValue)
{
	f_assert( ucValue <= 23);

	if( ucValue <= 9)
	{
		ucValue += NATIVE_ZERO;
	}
	else
	{
		ucValue = f_toascii( ucValue) - 10 + f_toascii( 'g');
		if( ucValue >= (FLMBYTE)'i')
		{
			ucValue++;
			if( ucValue >= (FLMBYTE)'l')
			{
				ucValue++;
				if( ucValue >= (FLMBYTE)'o')
				{
					ucValue++;
					if( ucValue >= (FLMBYTE)'r')
					{
						ucValue++;
						if( ucValue >= (FLMBYTE)'u')
						{
							ucValue++;
							if( ucValue >= (FLMBYTE)'v')
							{
								ucValue++;
							}
						}
					}
				}
			}
		}
	}
	
	return( ucValue);
}

/****************************************************************************
Desc:
****************************************************************************/
char * FLMAPI f_strupr(
	char *				pszStr)
{
#ifdef FLM_WIN
	return( _strupr( pszStr));
#else
	while( *pszStr)
	{
		*pszStr = f_toupper( *pszStr);
		pszStr++;
	}

	return( pszStr);
#endif
}

/**********************************************************************
Desc:
**********************************************************************/
FLMINT32 FLMAPI f_atomicInc(
	FLMATOMIC *			piTarget)
{
	#if defined( FLM_LIBC_NLM)
	{
		return( (FLMINT32)atomic_retadd( (unsigned long *)piTarget, 1));
	}
	#elif defined( FLM_RING_ZERO_NLM)
	{
		return( nlm_AtomicIncrement( (volatile LONG *)piTarget)); 
	}
	#elif defined( FLM_WIN)
	{
		return( (FLMINT32)InterlockedIncrement( (volatile LONG *)piTarget));
	}
	#elif defined( FLM_AIX)
	{
		return( (FLMINT32)aix_atomic_add( piTarget, 1));
	}
	#elif defined( FLM_OSX)
	{
		return( (FLMINT32)OSAtomicIncrement32Barrier( (int32_t *)piTarget));
	}
	#elif defined( FLM_SPARC_PLUS)
	{
		return( sparc_atomic_add_32( piTarget, 1));
	}
	#elif (defined( __i386__) || defined( __x86_64__)) && defined( FLM_GNUC)
	{
		FLMINT32 			i32Tmp;
		
		__asm__ __volatile__ (
						"lock;"
						"xaddl %0, %1"
							: "=r" (i32Tmp), "=m" (*piTarget)
							: "0" (1), "m" (*piTarget));
	
		return( i32Tmp + 1);
	}
	#elif defined( FLM_PPC) && defined( FLM_GNUC) && defined( FLM_LINUX)
	{
		return( ppc_atomic_add( piTarget, 1));
	}
	#elif defined( FLM_UNIX)
		return( posix_atomic_add_32( piTarget, 1));
	#else
		#error Atomic operations are not supported
	#endif
}

/**********************************************************************
Desc:
**********************************************************************/
FLMINT32 FLMAPI f_atomicDec(
	FLMATOMIC *			piTarget)
{
	#if defined( FLM_LIBC_NLM)
	{
		return( (FLMINT32)atomic_retadd( (unsigned long *)piTarget, -1));
	}
	#elif defined( FLM_RING_ZERO_NLM)
	{
		return( nlm_AtomicDecrement( (volatile LONG *)piTarget)); 
	}
	#elif defined( FLM_WIN)
	{
		return( (FLMINT32)InterlockedDecrement( (volatile LONG *)piTarget));
	}
	#elif defined( FLM_AIX)
	{
		return( (FLMINT32)aix_atomic_add( piTarget, -1));
	}
	#elif defined( FLM_OSX)
	{
		return( (FLMINT32)OSAtomicDecrement32Barrier( (int32_t *)piTarget));
	}
	#elif defined( FLM_SPARC_PLUS)
	{
		return( sparc_atomic_add_32( piTarget, -1));
	}
	#elif (defined( __i386__) || defined( __x86_64__)) && defined( FLM_GNUC)
	{
		FLMINT32				i32Tmp;
		
		__asm__ __volatile__ (
						"lock;" 
						"xaddl %0, %1"
							: "=r" (i32Tmp), "=m" (*piTarget)
							: "0" (-1), "m" (*piTarget));
	
		return( i32Tmp - 1);
	}
	#elif defined( FLM_PPC) && defined( FLM_GNUC) && defined( FLM_LINUX)
	{
		return( ppc_atomic_add( piTarget, -1));
	}
	#elif defined( FLM_UNIX)
		return( posix_atomic_add_32( piTarget, -1));
	#else
		#error Atomic operations are not supported
	#endif
}

/**********************************************************************
Desc:
**********************************************************************/
FLMINT32 FLMAPI f_atomicExchange(
	FLMATOMIC *			piTarget,
	FLMINT32				i32NewVal)
{
	#if defined( FLM_NLM)
	{
		return( (FLMINT32)atomic_xchg( (unsigned long *)piTarget, i32NewVal));
	}
	#elif defined( FLM_WIN)
	{
		return( (FLMINT32)InterlockedExchange( (volatile LONG *)piTarget,
			i32NewVal));
	}
	#elif defined( FLM_AIX)
	{
		int		iOldVal;
		
		for( ;;)
		{ 
			iOldVal = (int)*piTarget;
			
			if( compare_and_swap( (int *)piTarget, &iOldVal, i32NewVal))
			{
				break;
			}
		}
		
		return( (FLMINT32)iOldVal);
	}
	#elif defined( FLM_OSX)
	{
		int32_t		iOldVal;

		for( ;;)
		{
			iOldVal = (int32_t)*piTarget;

			if( OSAtomicCompareAndSwap32Barrier( iOldVal, i32NewVal, 
					(int32_t *)piTarget))
			{
				break;
			}
		}
		
		return( (FLMINT32)iOldVal);
	}
	#elif defined( FLM_SPARC_PLUS)
	{
		return( sparc_atomic_xchg_32( piTarget, i32NewVal));
	}
	#elif (defined( __i386__) || defined( __x86_64__)) && defined( FLM_GNUC)
	{
		FLMINT32 			i32OldVal;
		
		__asm__ __volatile__ (
						"1:	lock;"
						"		cmpxchgl %2, %0;"
						"		jne 1b"
							: "=m" (*piTarget), "=a" (i32OldVal)
							: "r" (i32NewVal), "m" (*piTarget), "a" (*piTarget));
	
		return( i32OldVal);
	}
	#elif defined( FLM_PPC) && defined( FLM_GNUC) && defined( FLM_LINUX)
	{
		return( ppc_atomic_xchg( piTarget, i32NewVal));
	}
	#elif defined( FLM_UNIX)
		return( posix_atomic_xchg_32( piTarget, i32NewVal));
	#else
		#error Atomic operations are not supported
	#endif
}

/**********************************************************************
Desc:
**********************************************************************/
FLMINT FLMAPI F_Object::getRefCount( void)
{
	return( m_refCnt);
}

/**********************************************************************
Desc:
**********************************************************************/
FLMINT FLMAPI F_Object::AddRef( void)
{
	return( ++m_refCnt);
}

/**********************************************************************
Desc:
**********************************************************************/
FLMINT FLMAPI F_Object::Release( void)
{
	FLMINT		iRefCnt = --m_refCnt;

	if( !iRefCnt)
	{
		delete this;
	}

	return( iRefCnt);
}

/**********************************************************************
Desc:
**********************************************************************/
IF_FileSystem * FLMAPI f_getFileSysPtr( void)
{
	return( gv_pFileSystem);
}

/**********************************************************************
Desc:
**********************************************************************/
FLMUINT FLMAPI f_getOpenFileCount( void)
{
	return( gv_openFiles);
}

/**********************************************************************
Desc:
**********************************************************************/
IF_ThreadMgr * f_getThreadMgrPtr( void)
{
	return( gv_pThreadMgr);
}

/**********************************************************************
Desc:
**********************************************************************/
FLMUINT FLMAPI f_getMaxFileSize( void)
{
	return( gv_uiMaxFileSize);
}

/*****************************************************************************
Desc:
******************************************************************************/
RCODE FLMAPI f_readSEN(
	IF_IStream *	pIStream,
	FLMUINT *		puiValue,
	FLMUINT *		puiLength)
{
	RCODE				rc;
	FLMUINT64		ui64Tmp;

	if( RC_BAD( rc = f_readSEN64( pIStream, &ui64Tmp, puiLength)))
	{
		goto Exit;
	}

	if( ui64Tmp > ~((FLMUINT)0))
	{
		rc = RC_SET_AND_ASSERT( NE_FLM_CONV_DEST_OVERFLOW);
		goto Exit;
	}

	if( puiValue)
	{
		*puiValue = (FLMUINT)ui64Tmp;
	}

Exit:

	return( rc);
}

/*****************************************************************************
Desc:
******************************************************************************/
RCODE FLMAPI f_readSEN64(
	IF_IStream *		pIStream,
	FLMUINT64 *			pui64Value,
	FLMUINT *			puiLength)
{
	RCODE					rc = NE_FLM_OK;
	FLMUINT				uiLen;
	FLMUINT				uiSENLength;
	FLMBYTE				ucBuffer[ 16];
	const FLMBYTE *	pucBuffer;

	uiLen = 1;
	if( RC_BAD( rc = pIStream->read( 
		(char *)&ucBuffer[ 0], uiLen, &uiLen)))
	{
		goto Exit;
	}

	uiSENLength = 	gv_ucSENLengthArray[ ucBuffer[ 0]];
	uiLen = uiSENLength - 1;

	if( puiLength)
	{
		*puiLength = uiSENLength;
	}

	if( pui64Value)
	{
		pucBuffer = &ucBuffer[ 1];
	}
	else
	{
		pucBuffer = NULL;
	}

	if( uiLen)
	{
		if( RC_BAD( rc = pIStream->read( 
			(char *)pucBuffer, uiLen, &uiLen)))
		{
			goto Exit;
		}
	}

	if( pui64Value)
	{
		pucBuffer = &ucBuffer[ 0];
		if( RC_BAD( rc = f_decodeSEN64( &pucBuffer,
			&ucBuffer[ sizeof( ucBuffer)], pui64Value)))
		{
			goto Exit;
		}
	}

Exit:

	return( rc);
}

/*****************************************************************************
Desc:
******************************************************************************/
FLMUINT FLMAPI f_getSENLength(
	FLMBYTE 					ucByte)
{
	return( gv_ucSENLengthArray[ ucByte]);
}

/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI f_decodeSEN64(
	const FLMBYTE **		ppucBuffer,
	const FLMBYTE *		pucEnd,
	FLMUINT64 *				pui64Value)
{
	RCODE						rc = NE_FLM_OK;
	FLMUINT					uiSENLength;
	const FLMBYTE *		pucBuffer = *ppucBuffer;

	uiSENLength = gv_ucSENLengthArray[ *pucBuffer];
	if( pucBuffer + uiSENLength > pucEnd)
	{
		if (pui64Value)
		{
			*pui64Value = 0;
		}
		rc = RC_SET( NE_FLM_BAD_SEN);
		goto Exit;
	}

	if (pui64Value)
	{
		switch( uiSENLength)
		{
			case 1:
				*pui64Value = *pucBuffer;
				break;
	
			case 2:
				*pui64Value = (((FLMUINT64)(*pucBuffer & 0x3F)) << 8) + pucBuffer[ 1];
				break;
	
			case 3:
				*pui64Value = (((FLMUINT64)(*pucBuffer & 0x1F)) << 16) +
					(((FLMUINT64)pucBuffer[ 1]) << 8) + pucBuffer[ 2];
				break;
	
			case 4:
				*pui64Value = (((FLMUINT64)(*pucBuffer & 0x0F)) << 24) +
					(((FLMUINT64)pucBuffer[ 1]) << 16) +
					(((FLMUINT64)pucBuffer[ 2]) << 8) + pucBuffer[ 3];
				break;
	
			case 5:
				*pui64Value = (((FLMUINT64)(*pucBuffer & 0x07)) << 32) +
					(((FLMUINT64)pucBuffer[ 1]) << 24) +
					(((FLMUINT64)pucBuffer[ 2]) << 16) +
					(((FLMUINT64)pucBuffer[ 3]) << 8) + pucBuffer[ 4];
				break;
	
			case 6:
				*pui64Value = (((FLMUINT64)(*pucBuffer & 0x03)) << 40) +
					(((FLMUINT64)pucBuffer[ 1]) << 32) +
					(((FLMUINT64)pucBuffer[ 2]) << 24) +
					(((FLMUINT64)pucBuffer[ 3]) << 16) +
					(((FLMUINT64)pucBuffer[ 4]) << 8) + pucBuffer[ 5];
				break;
	
			case 7:
				*pui64Value = (((FLMUINT64)(*pucBuffer & 0x01)) << 48) +
					(((FLMUINT64)pucBuffer[ 1]) << 40) +
					(((FLMUINT64)pucBuffer[ 2]) << 32) +
					(((FLMUINT64)pucBuffer[ 3]) << 24) +
					(((FLMUINT64)pucBuffer[ 4]) << 16) +
					(((FLMUINT64)pucBuffer[ 5]) << 8) + pucBuffer[ 6];
				break;
	
			case 8:
				*pui64Value = (((FLMUINT64)pucBuffer[ 1]) << 48) +
					(((FLMUINT64)pucBuffer[ 2]) << 40) +
					(((FLMUINT64)pucBuffer[ 3]) << 32) +
					(((FLMUINT64)pucBuffer[ 4]) << 24) +
					(((FLMUINT64)pucBuffer[ 5]) << 16) +
					(((FLMUINT64)pucBuffer[ 6]) << 8) + pucBuffer[ 7];
				break;
	
			case 9:
				*pui64Value = (((FLMUINT64)pucBuffer[ 1]) << 56) +
					(((FLMUINT64)pucBuffer[ 2]) << 48) +
					(((FLMUINT64)pucBuffer[ 3]) << 40) +
					(((FLMUINT64)pucBuffer[ 4]) << 32) +
					(((FLMUINT64)pucBuffer[ 5]) << 24) +
					(((FLMUINT64)pucBuffer[ 6]) << 16) +
					(((FLMUINT64)pucBuffer[ 7]) << 8) + pucBuffer[ 8];
				break;
	
			default:
				*pui64Value = 0;
				f_assert( 0);
				break;
		}
	}

Exit:

	*ppucBuffer = pucBuffer + uiSENLength;

	return( rc);
}

/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI f_decodeSEN(
	const FLMBYTE **		ppucBuffer,
	const FLMBYTE *		pucEnd,
	FLMUINT *				puiValue)
{
	RCODE				rc = NE_FLM_OK;
	FLMUINT64		ui64Value;
	
	if( RC_BAD( rc = f_decodeSEN64( ppucBuffer, pucEnd, &ui64Value)))
	{
		return( rc);
	}
	
	if( ui64Value > FLM_MAX_UINT)
	{
		return( RC_SET_AND_ASSERT( NE_FLM_CONV_NUM_OVERFLOW));
	}
	
	if( puiValue)
	{
		*puiValue = (FLMUINT)ui64Value;
	}

	return( rc);
}
	
/****************************************************************************
Desc:
****************************************************************************/
FINLINE FLMBYTE f_shiftRightRetByte(
	FLMUINT64	ui64Num,
	FLMBYTE		ucBits)
{
	return( ucBits < 64 ? (FLMBYTE)(ui64Num >> ucBits) : 0);
}

/****************************************************************************
Desc:
****************************************************************************/
FLMUINT FLMAPI f_getSENByteCount(
	FLMUINT64	ui64Num)
{
	FLMUINT		uiCount = 0;

	if( ui64Num < 0x80)
	{
		return( 1);
	}

	while( ui64Num)
	{
		uiCount++;
		ui64Num >>= 7;
	}

	// If the high bit is set, the counter will be incremented 1 beyond
	// the actual number of bytes need to represent the SEN.  We will need
	// to re-visit this if we ever go beyond 64-bits.

	return( uiCount < FLM_MAX_SEN_LEN ? uiCount : FLM_MAX_SEN_LEN);
}

/****************************************************************************
Desc:		Encodes a number as a SEN
****************************************************************************/
FLMUINT FLMAPI f_encodeSEN(
	FLMUINT64		ui64Value,
	FLMBYTE **		ppucBuffer,
	FLMUINT			uiSizeWanted)
{
	FLMBYTE *		pucBuffer = *ppucBuffer;
	FLMUINT			uiSenLen = f_getSENByteCount( ui64Value);

	f_assert( uiSizeWanted <= FLM_MAX_SEN_LEN && 
				  (!uiSizeWanted || uiSizeWanted >= uiSenLen));

	uiSenLen = uiSizeWanted > uiSenLen ? uiSizeWanted : uiSenLen;

	if( uiSenLen == 1)
	{
		*pucBuffer++ = (FLMBYTE)ui64Value;
	}
	else
	{
		FLMUINT			uiTmp = (uiSenLen - 1) << 3;

		*pucBuffer++ = ucSENPrefixArray[ uiSenLen] + 
							f_shiftRightRetByte( ui64Value, (FLMBYTE)uiTmp);
		while( uiTmp)
		{
			uiTmp -= 8;
			*pucBuffer++ = f_shiftRightRetByte( ui64Value, (FLMBYTE)uiTmp);
		}
	}

	*ppucBuffer = pucBuffer;
	return( uiSenLen);
}

/****************************************************************************
Desc:		Encodes a number as a SEN
****************************************************************************/
RCODE FLMAPI f_encodeSEN(
	FLMUINT64		ui64Value,
	FLMBYTE **		ppucBuffer,
	FLMBYTE *		pucEnd)
{
	RCODE				rc = NE_FLM_OK;
	FLMBYTE *		pucBuffer = *ppucBuffer;
	FLMUINT			uiSenLen = f_getSENByteCount( ui64Value);
	
	if( *ppucBuffer + uiSenLen > pucEnd)
	{
		rc = RC_SET_AND_ASSERT( NE_FLM_CONV_DEST_OVERFLOW);
		goto Exit;
	}

	if( uiSenLen == 1)
	{
		*pucBuffer++ = (FLMBYTE)ui64Value;
	}
	else
	{
		FLMUINT			uiTmp = (uiSenLen - 1) << 3;

		*pucBuffer++ = ucSENPrefixArray[ uiSenLen] + 
							f_shiftRightRetByte( ui64Value, (FLMBYTE)uiTmp);
		while( uiTmp)
		{
			uiTmp -= 8;
			*pucBuffer++ = f_shiftRightRetByte( ui64Value, (FLMBYTE)uiTmp);
		}
	}

	*ppucBuffer = pucBuffer;
	
Exit:

	return( rc);
}

/****************************************************************************
Desc:		Encodes a number as a SEN
****************************************************************************/
FLMUINT FLMAPI f_encodeSENKnownLength(
	FLMUINT64		ui64Value,
	FLMUINT			uiSenLen,
	FLMBYTE **		ppucBuffer)
{
	FLMBYTE *			pucBuffer = *ppucBuffer;

	if( uiSenLen == 1)
	{
		*pucBuffer++ = (FLMBYTE)ui64Value;
	}
	else
	{
		FLMUINT			uiTmp = (uiSenLen - 1) << 3;

		*pucBuffer++ = ucSENPrefixArray[ uiSenLen] + 
							f_shiftRightRetByte( ui64Value, (FLMBYTE)uiTmp);
		while( uiTmp)
		{
			uiTmp -= 8;
			*pucBuffer++ = f_shiftRightRetByte( ui64Value, (FLMBYTE)uiTmp);
		}
	}

	*ppucBuffer = pucBuffer;
	return( uiSenLen);
}

/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI FlmGetXMLObject(
	IF_XML **				ppXmlObject)
{
	*ppXmlObject = gv_pXml;
	(*ppXmlObject)->AddRef();
	
	return( NE_FLM_OK);
}

/****************************************************************************
Desc:
****************************************************************************/
IF_XML * f_getXmlObjPtr( void)
{
	return( gv_pXml);
}

/****************************************************************************
Desc:
****************************************************************************/
FLMUINT32 FLMAPI f_getRandomUINT32(
	FLMUINT32		ui32Low,
	FLMUINT32		ui32High)
{
	FLMUINT32		ui32Value;

	f_mutexLock( gv_hRandomGenMutex);	
	ui32Value = gv_pRandomGenerator->getUINT32( ui32Low, ui32High);
	f_mutexUnlock( gv_hRandomGenMutex);	
	
	return( ui32Value);
}

/****************************************************************************
Desc:
****************************************************************************/
FLMBYTE FLMAPI f_getRandomByte( void)
{
	FLMBYTE		ucValue;

	f_mutexLock( gv_hRandomGenMutex);	
	ucValue = (FLMBYTE)(gv_pRandomGenerator->getUINT32( 0, 0xFF));
	f_mutexUnlock( gv_hRandomGenMutex);	
	
	return( ucValue);
}

/****************************************************************************
Desc:
****************************************************************************/
void F_ListManager::insertFirst(
	FLMUINT				uiList,
	F_ListItem *		pNewFirstItem)
{
	F_ListNode *		pListNode;
	
	f_assert( uiList < m_uiListNodeCnt);
	
	pNewFirstItem->AddRef();
	pListNode = &m_pListNodes[ uiList];
	
	if( !pListNode->pNextItem)
	{
		pListNode->pPrevItem = pNewFirstItem;
		pNewFirstItem->setNextListItem( uiList, NULL);
	}
	else
	{
		// Add this new item to the first of the list.
		
		pListNode->pNextItem->setPrevListItem( uiList, pNewFirstItem);
		pNewFirstItem->setNextListItem( uiList, pListNode->pNextItem);
	}

	pListNode->pNextItem = pNewFirstItem;
	pNewFirstItem->setPrevListItem( uiList, NULL);
	pNewFirstItem->m_bInList = TRUE;
	pListNode->uiListCount++;
}

/****************************************************************************
Desc:
****************************************************************************/
void F_ListManager::insertLast(
	FLMUINT				uiList,
	F_ListItem *		pNewLastItem)
{
	F_ListNode *		pListNode;
	
	f_assert( uiList < m_uiListNodeCnt);
	
	pNewLastItem->AddRef();
	pListNode = &m_pListNodes[ uiList];
	
	if( !pListNode->pPrevItem)
	{
		pListNode->pNextItem = pNewLastItem;
		pNewLastItem->setPrevListItem( uiList, NULL);
	}
	else
	{
		// Add this new item to the end of the list.
		
		pListNode->pPrevItem->setNextListItem( uiList, pNewLastItem);
		pNewLastItem->setPrevListItem( uiList, pListNode->pPrevItem);
	}

	pListNode->pPrevItem = pNewLastItem;
	pNewLastItem->setNextListItem( uiList, NULL);
	pNewLastItem->m_bInList = TRUE;
	pListNode->uiListCount++;
}

/****************************************************************************
Desc:
****************************************************************************/
F_ListItem * F_ListManager::getItem(
	FLMUINT				uiList,
	FLMUINT				nth)
{
	F_ListNode *		pListNode;
	F_ListItem *		pListItem;
	
	// Check bounds with assert.  
	
	f_assert( uiList < m_uiListNodeCnt );

	pListNode = &m_pListNodes[ uiList ];
	pListItem = pListNode ? pListNode->pNextItem : NULL;
	
	while( nth--)
	{
		pListItem = pListItem->getNextListItem( uiList);
	}
	
	return( pListItem);
}

/****************************************************************************
Desc:
****************************************************************************/
void F_ListManager::removeItem(
	FLMUINT				uiList,
	F_ListItem *		pItem)
{
	F_ListNode *		pMgrListNode;
	F_ListItem *		pPrevItem;
	F_ListItem *		pNextItem;
	
	f_assert( uiList < m_uiListNodeCnt);

	pMgrListNode = &m_pListNodes[ uiList];

	// Get this item's prev and next items

	pPrevItem = pItem->getPrevListItem( uiList);
	pNextItem = pItem->getNextListItem( uiList);

	if( !pPrevItem && !pNextItem && pMgrListNode->pPrevItem != pItem && 
		 pMgrListNode->pNextItem != pItem)
	{
		// If the item is not within the list then skip to the end
		
		goto Exit;
	}

	// Determine if this item is pointed to by the head or tail pointers
	// that the list manager maintains

	if( pMgrListNode->pPrevItem == pItem)
	{
		pMgrListNode->pPrevItem = pItem->getPrevListItem( uiList);
	}

	if( pMgrListNode->pNextItem == pItem)
	{
		pMgrListNode->pNextItem = pItem->getNextListItem( uiList);
	}

	// If there is a prev item - change it's next ptr to be items next ptr
	
	if( pPrevItem)
	{
		pPrevItem->setNextListItem( uiList, pItem->getNextListItem( uiList));
	}

	// If there is a next item - change it's prev ptr to be items prev ptr
	
	if( pNextItem)
	{
		pNextItem->setPrevListItem( uiList, pItem->getPrevListItem( uiList));
	}

	// Clear out this items prev and next links
	
	pItem->setPrevListItem( uiList, NULL);
	pItem->setNextListItem( uiList, NULL);
	pItem->m_bInList = FALSE;
	pItem->Release();
	pMgrListNode->uiListCount--;

Exit:

	return;
}

/****************************************************************************
Desc:
****************************************************************************/
void F_ListManager::clearList(
	FLMUINT			uiList)
{
	FLMUINT			uiListCnt;
	F_ListNode *	pListNode;

	f_assert( (FLM_ALL_LISTS == uiList) || (uiList < m_uiListNodeCnt));

	if( uiList == FLM_ALL_LISTS)
	{
		uiList = 0;
		uiListCnt = m_uiListNodeCnt;
		pListNode = m_pListNodes;
	}
	else
	{
		uiListCnt = 1;
		pListNode = &m_pListNodes[ uiList ];
	}
	
	for( ; uiListCnt--; pListNode++, uiList++)
	{
		F_ListItem *		pItem;
		F_ListItem *		pNextItem;
		
		// Go through the list Releasing every list item.
		
		for( pItem = pListNode->pNextItem; pItem; pItem = pNextItem)
		{
			pNextItem = pItem->getNextListItem( uiList);
			removeItem( uiList, pItem);
		}

		// At this point the ListCount should be at 0.
		
		f_assert( !pListNode->uiListCount);

		// Clear the managers head and tail list pointers.
		
		pListNode->pNextItem = pListNode->pPrevItem = NULL;
	}
}

/****************************************************************************
Desc:
****************************************************************************/
FLMUINT F_ListManager::getItemCount(
	FLMUINT			uiList)
{
	FLMUINT			uiListNodeCnt;
	FLMUINT			uiCount = 0;
	F_ListNode *	pListNode;

	f_assert( (FLM_ALL_LISTS == uiList) || (uiList < m_uiListNodeCnt));

	if( uiList == FLM_ALL_LISTS)
	{
		uiListNodeCnt = m_uiListNodeCnt;
		pListNode = m_pListNodes;
	}
	else
	{
		uiListNodeCnt = 1;
		pListNode = &m_pListNodes[ uiList];
	}

	for( ; uiListNodeCnt--; pListNode++)
	{
		uiCount += pListNode->uiListCount;
	}

	return( uiCount);
}

/****************************************************************************
Desc:
****************************************************************************/
F_ListItem::~F_ListItem()
{
#ifdef FLM_DEBUG
	FLMUINT			uiLoop;
	F_ListNode *	pTmpNd;

	f_assert( !m_bInList);

	for( uiLoop = 0; uiLoop < m_uiListNodeCnt; uiLoop++)
	{
		pTmpNd = &m_pListNodes[ uiLoop];
		f_assert( !pTmpNd->pPrevItem && !pTmpNd->pNextItem);
	}
#endif
}

/****************************************************************************
Desc:
****************************************************************************/
void F_ListItem::setup(
	F_ListManager *	pListMgr,
	F_ListNode *		pListNodes,
	FLMUINT				uiListNodeCnt)
{
	f_assert( pListMgr);
	f_assert( pListNodes);
	f_assert( uiListNodeCnt);

	m_pListManager = pListMgr;
	m_uiListNodeCnt = uiListNodeCnt;
	m_pListNodes = pListNodes;
	
	f_memset( pListNodes, 0, sizeof( F_ListNode) * uiListNodeCnt );
}

/****************************************************************************
Desc:
****************************************************************************/
void F_ListItem::removeFromList(
	FLMUINT			uiList)
{

	f_assert( (uiList < m_uiListNodeCnt) || (uiList == FLM_ALL_LISTS));

	if( uiList == FLM_ALL_LISTS)
	{
		FLMUINT			uiListCnt = m_uiListNodeCnt;
		F_ListNode *	pListNode = m_pListNodes;

		uiList = 0;

		// Remove this item from all lists

		for( ; uiListCnt--; uiList++, pListNode++)
		{
			m_pListManager->removeItem( uiList, this);
		}
	}
	else
	{
		// Remove item from a specific list
		
		m_pListManager->removeItem( uiList, this);
	}
}

/****************************************************************************
Desc: This routine allocates and initializes a hash table.
****************************************************************************/
RCODE FLMAPI f_allocHashTable(
	FLMUINT					uiHashTblSize,
	F_BUCKET **				ppHashTblRV)
{
	RCODE						rc = NE_FLM_OK;
	F_BUCKET *				pHashTbl = NULL;
	IF_RandomGenerator *	pRandGen = NULL;
	FLMUINT					uiCnt;
	FLMUINT					uiRandVal;
	FLMUINT					uiTempVal;
	
	// Allocate memory for the hash table

	if (RC_BAD( rc = f_calloc(
		(FLMUINT)(sizeof( F_BUCKET)) * uiHashTblSize, &pHashTbl)))
	{
		goto Exit;
	}

	// Set up the random number generator
	
	if( RC_BAD( rc = FlmAllocRandomGenerator( &pRandGen)))
	{
		goto Exit;
	}

	pRandGen->setSeed( 1);

	for (uiCnt = 0; uiCnt < uiHashTblSize; uiCnt++)
	{
		pHashTbl [uiCnt].uiHashValue = (FLMBYTE)uiCnt;
		pHashTbl [uiCnt].pFirstInBucket = NULL;
	}

	if( uiHashTblSize <= 256)
	{
		for( uiCnt = 0; uiCnt < uiHashTblSize - 1; uiCnt++)
		{
			uiRandVal = (FLMBYTE) pRandGen->getUINT32( (FLMUINT32)uiCnt,
										(FLMUINT32)(uiHashTblSize - 1));
			if( uiRandVal != uiCnt)
			{
				uiTempVal = (FLMBYTE)pHashTbl [uiCnt].uiHashValue;
				pHashTbl [uiCnt].uiHashValue = pHashTbl [uiRandVal].uiHashValue;
				pHashTbl [uiRandVal].uiHashValue = uiTempVal;
			}
		}
	}

Exit:

	if( pRandGen)
	{
		pRandGen->Release();
	}

	*ppHashTblRV = pHashTbl;
	return( rc);
}

/****************************************************************************
Desc: This routine determines the hash bucket for a string.
****************************************************************************/
FLMUINT FLMAPI f_strHashBucket(
	char *		pszStr,
	F_BUCKET *	pHashTbl,
	FLMUINT		uiNumBuckets)
{
	FLMUINT	uiHashIndex;

	if ((uiHashIndex = (FLMUINT)*pszStr) >= uiNumBuckets)
	{
		uiHashIndex -= uiNumBuckets;
	}

	while (*pszStr)
	{
		if ((uiHashIndex = (FLMUINT)((pHashTbl [uiHashIndex].uiHashValue) ^ 
			(FLMUINT)(f_toupper( *pszStr)))) >= uiNumBuckets)
		{
			uiHashIndex -= uiNumBuckets;
		}
		pszStr++;
	}

	return( uiHashIndex);
}

/****************************************************************************
Desc: This routine determines the hash bucket for a binary array of
		characters.
****************************************************************************/
FLMUINT FLMAPI f_binHashBucket(
	void *		pBuf,
	FLMUINT		uiBufLen,
	F_BUCKET *	pHashTbl,
	FLMUINT		uiNumBuckets)
{
	FLMUINT		uiHashIndex;
	FLMBYTE *	ptr = (FLMBYTE *)pBuf;

	if ((uiHashIndex = (FLMUINT)*ptr) >= uiNumBuckets)
		uiHashIndex -= uiNumBuckets;
	while (uiBufLen)
	{
		if ((uiHashIndex =
				(FLMUINT)((pHashTbl [uiHashIndex].uiHashValue) ^ (FLMUINT)(*ptr))) >=
					uiNumBuckets)
			uiHashIndex -= uiNumBuckets;
		ptr++;
		uiBufLen--;
	}
	return( uiHashIndex);
}

/****************************************************************************
Desc:
****************************************************************************/
F_HashTable::F_HashTable()
{
	m_hMutex = F_MUTEX_NULL;
	m_pMRUObject = NULL;
	m_pLRUObject = NULL;
	m_ppHashTable = NULL;
	m_uiBuckets = 0;
	m_uiObjects = 0;
	m_uiMaxObjects = 0;
}

/****************************************************************************
Desc:
****************************************************************************/
F_HashTable::~F_HashTable()
{
	F_HashObject *		pCur;
	F_HashObject *		pNext;

	pCur = m_pMRUObject;
	while( pCur)
	{
		pNext = pCur->m_pNextInGlobal;
		unlinkObject( pCur);
		pCur->Release();
		pCur = pNext;
	}

	f_assert( !m_uiObjects);
	f_assert( !m_pMRUObject);
	f_assert( !m_pLRUObject);

	if( m_ppHashTable)
	{
		f_free( &m_ppHashTable);
	}

	if( m_hMutex != F_MUTEX_NULL)
	{
		f_mutexDestroy( &m_hMutex);
	}
}

/****************************************************************************
Desc:	Configures the hash table prior to first use
****************************************************************************/
RCODE FLMAPI F_HashTable::setupHashTable(
	FLMBOOL			bMultithreaded,
	FLMUINT			uiNumBuckets,
	FLMUINT			uiMaxObjects)
{
	RCODE			rc = NE_FLM_OK;

	f_assert( uiNumBuckets);

	// Create the hash table

	if( RC_BAD( rc = f_alloc( 
		sizeof( F_HashObject *) * uiNumBuckets, &m_ppHashTable)))
	{
		goto Exit;
	}
	
	m_uiObjects = 0;
	m_uiMaxObjects = uiMaxObjects;
	m_uiBuckets = uiNumBuckets;
	f_memset( m_ppHashTable, 0, sizeof( F_HashObject *) * uiNumBuckets);

	if( bMultithreaded)
	{
		// Initialize the mutex

		if( RC_BAD( rc = f_mutexCreate( &m_hMutex)))
		{
			goto Exit;
		}
	}

Exit:

	return( rc);
}

/****************************************************************************
Desc:	Retrieves an object from the hash table with the specified key.
		This routine assumes the table's mutex has already been locked.
		A reference IS NOT added to the object for the caller.
****************************************************************************/
RCODE F_HashTable::findObject(
	const void *		pvKey,
	FLMUINT				uiKeyLen,
	F_HashObject **	ppObject)
{
	RCODE					rc = NE_FLM_OK;
	F_HashObject *		pObject = NULL;
	FLMUINT				uiBucket;
	FLMUINT32			ui32CRC = 0;

	*ppObject = NULL;

	// Calculate the hash bucket and mutex offset

	uiBucket = getHashBucket( pvKey, uiKeyLen, &ui32CRC);

	// Search the bucket for an object with a matching
	// key.

	pObject = m_ppHashTable[ uiBucket];
	while( pObject)
	{
		if( pObject->getKeyCRC() == ui32CRC)
		{
			const void *	pvTmpKey = pObject->getKey();
			FLMUINT			uiTmpKeyLen = pObject->getKeyLength();

			if( uiTmpKeyLen == uiKeyLen &&
				f_memcmp( pvTmpKey, pvKey, uiKeyLen) == 0)
			{
				break;
			}
		}
		
		pObject = pObject->m_pNextInBucket;
	}

	if( !pObject)
	{
		rc = RC_SET( NE_FLM_NOT_FOUND);
		goto Exit;
	}

	*ppObject = pObject;

Exit:

	return( rc);
}

/****************************************************************************
Desc:	Adds an object to the hash table
****************************************************************************/
RCODE FLMAPI F_HashTable::addObject(
	F_HashObject *		pObject,
	FLMBOOL				bAllowDuplicates)
{
	RCODE					rc = NE_FLM_OK;
	FLMUINT				uiBucket;
	F_HashObject *		pTmp;
	const void *		pvKey;
	FLMUINT				uiKeyLen;
	FLMUINT32			ui32CRC;
	FLMBOOL				bMutexLocked = FALSE;

	// Calculate and set the objects hash bucket

	f_assert( pObject->getHashBucket() == F_INVALID_HASH_BUCKET);

	pvKey = pObject->getKey();
	uiKeyLen = pObject->getKeyLength();
	f_assert( uiKeyLen);

	uiBucket = getHashBucket( pvKey, uiKeyLen, &ui32CRC);
	pObject->m_ui32KeyCRC = ui32CRC;

	// Lock the mutex

	if( m_hMutex != F_MUTEX_NULL)
	{
		f_mutexLock( m_hMutex);
		bMutexLocked = TRUE;
	}

	// Make sure the object doesn't already exist

	if( !bAllowDuplicates)
	{
		if( RC_BAD( rc = findObject( pvKey, uiKeyLen, &pTmp)))
		{
			if( rc != NE_FLM_NOT_FOUND)
			{
				goto Exit;
			}
			rc = NE_FLM_OK;
		}
		else
		{
			rc = RC_SET_AND_ASSERT( NE_FLM_EXISTS);
			goto Exit;
		}
	}

	// Add a reference to the object

	pObject->AddRef();

	// Link the object into the appropriate lists

	linkObject( pObject, uiBucket);
	
	// Make sure the maximum number of objects hasn't been exceeded
	
	if( m_uiMaxObjects)
	{
		while( m_uiObjects > m_uiMaxObjects)
		{
			if( (pTmp = m_pLRUObject) == NULL)
			{
				break;
			}

			unlinkObject( pTmp);
		}
	}

Exit:

	if( bMutexLocked)
	{
		f_mutexUnlock( m_hMutex);
	}

	return( rc);
}

/****************************************************************************
Desc:	Returns the next object in the linked list of objects in the hash
		table.  If *ppObject == NULL, the first object will be returned.
****************************************************************************/
RCODE FLMAPI F_HashTable::getNextObjectInGlobal(
	F_HashObject **	ppObject)
{
	RCODE					rc = NE_FLM_OK;
	FLMBOOL				bMutexLocked = FALSE;
	F_HashObject *		pOldObj;

	// Lock the mutex

	if( m_hMutex != F_MUTEX_NULL)
	{
		f_mutexLock( m_hMutex);
		bMutexLocked = TRUE;
	}

	if( !(*ppObject))
	{
		*ppObject = m_pMRUObject;
	}
	else
	{
		pOldObj = *ppObject;
		*ppObject = (*ppObject)->m_pNextInGlobal;
		pOldObj->Release();
	}

	if( *ppObject == NULL)
	{
		rc = RC_SET( NE_FLM_EOF_HIT);
		goto Exit;
	}

	(*ppObject)->AddRef();

Exit:

	if( bMutexLocked)
	{
		f_mutexUnlock( m_hMutex);
	}

	return( rc);
}

/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI F_HashTable::getNextObjectInBucket(
	F_HashObject **	ppObject)
{
	RCODE					rc = NE_FLM_OK;
	FLMBOOL				bMutexLocked = FALSE;
	F_HashObject *		pOldObj;

	if( m_hMutex != F_MUTEX_NULL)
	{
		f_mutexLock( m_hMutex);
		bMutexLocked = TRUE;
	}

	if( !(*ppObject))
	{
		rc = RC_SET( NE_FLM_EOF_HIT);
		goto Exit;
	}
	
	pOldObj = *ppObject;
	*ppObject = (*ppObject)->m_pNextInBucket;
	pOldObj->Release();

	if( *ppObject == NULL)
	{
		rc = RC_SET( NE_FLM_EOF_HIT);
		goto Exit;
	}

	(*ppObject)->AddRef();

Exit:

	if( bMutexLocked)
	{
		f_mutexUnlock( m_hMutex);
	}

	return( rc);
}

/****************************************************************************
Desc:	Retrieves an object from the hash table with the specified key
****************************************************************************/
RCODE FLMAPI F_HashTable::getObject(
	const void *		pvKey,
	FLMUINT				uiKeyLen,
	F_HashObject **	ppObject,
	FLMBOOL				bRemove)
{
	RCODE				rc = NE_FLM_OK;
	F_HashObject *	pObject;
	FLMBOOL			bMutexLocked = FALSE;

	// Lock the mutex

	if( m_hMutex != F_MUTEX_NULL)
	{
		f_mutexLock( m_hMutex);
		bMutexLocked = TRUE;
	}

	// Search for an object with a matching key.

	if( RC_BAD( rc = findObject( pvKey, uiKeyLen, &pObject)))
	{
		goto Exit;
	}

	if( pObject && bRemove)
	{
		unlinkObject( pObject);
		if( !ppObject)
		{
			pObject->Release();
			pObject = NULL;
		}
	}

	if( ppObject)
	{
		if( !bRemove)
		{
			pObject->AddRef();
		}
		
		*ppObject = pObject;
		pObject = NULL;
	}

Exit:

	if( bMutexLocked)
	{
		f_mutexUnlock( m_hMutex);
	}

	return( rc);
}

/****************************************************************************
Desc:	Removes an object from the hash table by key
****************************************************************************/
RCODE FLMAPI F_HashTable::removeObject(
	void *			pvKey,
	FLMUINT			uiKeyLen)
{
	return( getObject( pvKey, uiKeyLen, NULL, TRUE));
}

/****************************************************************************
Desc:	Removes an object from the hash table by object pointer
****************************************************************************/
RCODE FLMAPI F_HashTable::removeObject(
	F_HashObject *		pObject)
{
	const void *	pvKey = pObject->getKey();
	FLMUINT			uiKeyLen = pObject->getKeyLength();

	return( getObject( pvKey, uiKeyLen, NULL, TRUE));
}

/****************************************************************************
Desc:
****************************************************************************/
void FLMAPI F_HashTable::removeAllObjects( void)
{
	F_HashObject *		pCur;
	FLMBOOL				bMutexLocked = FALSE;

	for( ;;)
	{
		if( m_hMutex != F_MUTEX_NULL)
		{
			f_mutexLock( m_hMutex);
			bMutexLocked = TRUE;
		}
		
		if( (pCur = m_pMRUObject) == NULL)
		{
			break;
		}

		unlinkObject( pCur);

		if( bMutexLocked)
		{
			f_mutexUnlock( m_hMutex);
			bMutexLocked = FALSE;
		}

		pCur->Release();
	}

	f_assert( !m_pMRUObject);
	f_assert( !m_pLRUObject);

	if( bMutexLocked)
	{
		f_mutexUnlock( m_hMutex);
	}
}

/****************************************************************************
Desc:
****************************************************************************/
void FLMAPI F_HashTable::removeAgedObjects(
	FLMUINT				uiMaxAge)
{
	F_HashObject *		pCur;
	FLMBOOL				bMutexLocked = FALSE;
	FLMUINT				uiCurrentTime = FLM_GET_TIMER();

	for( ;;)
	{
		if( m_hMutex != F_MUTEX_NULL)
		{
			f_mutexLock( m_hMutex);
			bMutexLocked = TRUE;
		}
		
		if( (pCur = m_pLRUObject) == NULL)
		{
			break;
		}
		
		if( FLM_TIMER_UNITS_TO_SECS( 
			FLM_ELAPSED_TIME( uiCurrentTime, pCur->m_uiTimeAdded)) < uiMaxAge)
		{
			break;
		}

		unlinkObject( pCur);

		if( bMutexLocked)
		{
			f_mutexUnlock( m_hMutex);
			bMutexLocked = FALSE;
		}

		pCur->Release();
	}

	if( bMutexLocked)
	{
		f_mutexUnlock( m_hMutex);
	}
}

/****************************************************************************
Desc:
****************************************************************************/
FLMUINT FLMAPI F_HashTable::getMaxObjects( void)
{
	FLMUINT		uiMaxObjects;
	
	if( m_hMutex != F_MUTEX_NULL)
	{
		f_mutexLock( m_hMutex);
	}
	
	uiMaxObjects = m_uiMaxObjects;
	
	if( m_hMutex != F_MUTEX_NULL)
	{
		f_mutexUnlock( m_hMutex);
	}
	
	return( m_uiMaxObjects);
}
	
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI F_HashTable::setMaxObjects(
	FLMUINT				uiMaxObjects)
{
	F_HashObject *		pCur;
	FLMBOOL				bMutexLocked = FALSE;
	
	if( m_hMutex != F_MUTEX_NULL)
	{
		f_mutexLock( m_hMutex);
		bMutexLocked = TRUE;
	}
	
	m_uiMaxObjects = uiMaxObjects;
	
	while( m_uiObjects > m_uiMaxObjects)
	{
		if( !bMutexLocked && m_hMutex != F_MUTEX_NULL)
		{
			f_mutexLock( m_hMutex);
			bMutexLocked = TRUE;
		}
		
		if( (pCur = m_pLRUObject) == NULL)
		{
			break;
		}
		
		unlinkObject( pCur);

		if( bMutexLocked)
		{
			f_mutexUnlock( m_hMutex);
			bMutexLocked = FALSE;
		}

		pCur->Release();
	}
	
	if( bMutexLocked)
	{
		f_mutexUnlock( m_hMutex);
	}
	
	return( NE_FLM_OK);
}
	
/****************************************************************************
Desc:	Calculates the hash bucket of a key and optionally returns the key's
		CRC.
****************************************************************************/
FLMUINT F_HashTable::getHashBucket(
	const void *	pvKey,
	FLMUINT			uiLen,
	FLMUINT32 *		pui32KeyCRC)
{
	FLMUINT32		ui32CRC = 0;

	f_updateCRC( (FLMBYTE *)pvKey, uiLen, &ui32CRC);
	
	if( pui32KeyCRC)
	{
		*pui32KeyCRC = ui32CRC;
	}
	
	return( ui32CRC % m_uiBuckets);
}

/****************************************************************************
Desc:		Links an object to the global list and also to its bucket
Notes:	This routine assumes that the bucket's mutex is already locked
			if the hash table is multi-threaded.
****************************************************************************/
void F_HashTable::linkObject(
	F_HashObject *		pObject,
	FLMUINT				uiBucket)
{
	f_assert( uiBucket < m_uiBuckets);
	f_assert( pObject->getHashBucket() == F_INVALID_HASH_BUCKET);

	// Set the object's bucket

	pObject->setHashBucket( uiBucket);

	// Link the object to its hash bucket

	pObject->m_pNextInBucket = m_ppHashTable[ uiBucket];
	if( m_ppHashTable[ uiBucket])
	{
		m_ppHashTable[ uiBucket]->m_pPrevInBucket = pObject;
	}
	m_ppHashTable[ uiBucket] = pObject;

	// Link to the global list

	if( (pObject->m_pNextInGlobal = m_pMRUObject) != NULL)
	{
		m_pMRUObject->m_pPrevInGlobal = pObject;
	}
	else
	{
		m_pLRUObject = pObject;
	}
	
	pObject->m_uiTimeAdded = FLM_GET_TIMER();
	m_pMRUObject = pObject;
	m_uiObjects++;
}

/****************************************************************************
Desc:		Unlinks an object from its bucket and the global list.
Notes:	This routine assumes that the bucket's mutex is already locked
			if the hash table is multi-threaded.
****************************************************************************/
void F_HashTable::unlinkObject(
	F_HashObject *		pObject)
{
	FLMUINT		uiBucket = pObject->getHashBucket();

	// Is the bucket valid?

	f_assert( uiBucket < m_uiBuckets);

	// Unlink from the hash bucket

	if( pObject->m_pNextInBucket)
	{
		pObject->m_pNextInBucket->m_pPrevInBucket = pObject->m_pPrevInBucket;
	}

	if( pObject->m_pPrevInBucket)
	{
		pObject->m_pPrevInBucket->m_pNextInBucket = pObject->m_pNextInBucket;
	}
	else
	{
		m_ppHashTable[ uiBucket] = pObject->m_pNextInBucket;
	}

	pObject->m_pPrevInBucket = NULL;
	pObject->m_pNextInBucket = NULL;
	pObject->setHashBucket( F_INVALID_HASH_BUCKET);

	// Unlink from the global list

	if( pObject->m_pNextInGlobal)
	{
		pObject->m_pNextInGlobal->m_pPrevInGlobal = pObject->m_pPrevInGlobal;
	}
	else
	{
		m_pLRUObject = pObject->m_pPrevInGlobal;
	}

	if( pObject->m_pPrevInGlobal)
	{
		pObject->m_pPrevInGlobal->m_pNextInGlobal = pObject->m_pNextInGlobal;
	}
	else
	{
		m_pMRUObject = pObject->m_pNextInGlobal;
	}

	pObject->m_pPrevInGlobal = NULL;
	pObject->m_pNextInGlobal = NULL;
	pObject->m_uiTimeAdded = 0;

	f_assert( m_uiObjects);
	m_uiObjects--;
}

/****************************************************************************
Desc:
****************************************************************************/
RCODE f_initFileAsyncClientList( void)
{
	RCODE			rc = NE_FLM_OK;
	
	if( RC_BAD( rc = f_mutexCreate( &F_FileHdl::m_hAsyncListMutex)))
	{
		goto Exit;
	}
	
	F_FileHdl::m_pFirstAvailAsync = NULL;
	F_FileHdl::m_uiAvailAsyncCount = 0;
	
Exit:

	return( rc);
}

/****************************************************************************
Desc:
****************************************************************************/
void f_freeFileAsyncClientList( void)
{
	F_FileAsyncClient *		pAsyncClient;
	
	while( F_FileHdl::m_pFirstAvailAsync)
	{
		pAsyncClient = F_FileHdl::m_pFirstAvailAsync;
		F_FileHdl::m_pFirstAvailAsync = F_FileHdl::m_pFirstAvailAsync->m_pNext;
		pAsyncClient->m_pNext = NULL;
		pAsyncClient->Release( FALSE);
	}
	
	if( F_FileHdl::m_hAsyncListMutex != F_MUTEX_NULL)
	{
		f_mutexDestroy( &F_FileHdl::m_hAsyncListMutex);
	}
	
	F_FileHdl::m_uiAvailAsyncCount = 0;
}
