                          
//                                        Ruler
//       1         2         3         4         5         6         7         8
//345678901234567890123456789012345678901234567890123456789012345678901234567890

    /********************************************************************/
    /*                                                                  */
    /*   The standard layout.                                           */
    /*                                                                  */
    /*   The standard layout for 'cpp' files in this code is as         */
    /*   follows:                                                       */
    /*                                                                  */
    /*      1. Include files.                                           */
    /*      2. Constants local to the class.                            */
    /*      3. Data structures local to the class.                      */
    /*      4. Data initializations.                                    */
    /*      5. Static functions.                                        */
    /*      6. Class functions.                                         */
    /*                                                                  */
    /*   The constructor is typically the first function, class         */
    /*   member functions appear in alphabetical order with the         */
    /*   destructor appearing at the end of the file.  Any section      */
    /*   or function this is not required is simply omitted.            */
    /*                                                                  */
    /********************************************************************/

#include "HeapPCH.hpp"

#include "Cache.hpp"
#include "Find.hpp"
#include "Heap.hpp"
#include "RockallBackEnd.hpp"

    /********************************************************************/
    /*                                                                  */
    /*   Constants local to the class.                                  */
    /*                                                                  */
    /*   The constants supplied here control minimum size of an         */
    /*   allocation bucket.                                             */
    /*                                                                  */
    /********************************************************************/

CONST SBIT32 MinParentSize			  = 32;
CONST SBIT32 MaxSummaryColumns		  = 2;
#ifdef ENABLE_HEAP_STATISTICS
GLOBALLOCK*		HEAP::_Globallock	  = NULL;
BYTE			HEAP::_LockMem[sizeof(*_Globallock)];
VOLATILE SBIT32 HEAP::_HeapCount	  = 0;
#endif
    /********************************************************************/
    /*                                                                  */
    /*   Class constructor.                                             */
    /*                                                                  */
    /*   Create a heap and prepare it for use.  Additionally, make      */
    /*   sure that the heap configuration makes sense.  This is         */
    /*   tricky as the whole structure of the heap can be changed       */
    /*   by the external configuration information.                     */
    /*                                                                  */
    /********************************************************************/

HEAP::HEAP
		(
		CACHE						  *Caches1[],
		CACHE						  *Caches2[],
		SBIT32						  MaxFreeSpace,
		NEW_PAGE					  *NewPages,
		FIND						  *NewPrivateFind,
		FIND						  *NewPublicFind,
		ROCKALL_BACK_END			  *NewRockallBackEnd,
		SBIT32						  Size1,
		SBIT32						  Size2,
		SBIT32						  Stride1,
		SBIT32						  Stride2,
		THREAD_SAFE					  *NewThreadSafe
		)
    {
	//
	//   The top three buckets are special and a user can not 
	//   allocate memory from two of them.  Thus, unless we have  
	//   at least four buckets the memory allocator is not going 
	//   to be very useful. 
	//
	if ( (Size1 >= 1) && (Size2 >= 3) )
		{
		REGISTER CACHE *FirstCache = Caches1[0];
		REGISTER CACHE *MiddleCache = Caches2[0];
		REGISTER CACHE *LastCache = Caches2[ (Size2-3) ];

		//
		//   Calculate the minimum and maximum allocation sizes.
		//   All allocations outside of this range will be passed
		//   directly to the external allocator.
		//
		CachesSize = (Size1 + Size2);
		MinCacheSize = FirstCache -> GetAllocationSize();
		MidCacheSize = MiddleCache -> GetAllocationSize();
		MaxCacheSize = LastCache -> GetAllocationSize();

		//
		//   Calculate and save various useful pointers needed
		//   during the course of execution.
		//
		Caches = Caches1;
		ExternalCache = (Caches2[ (Size2-1) ]);
		NewPage = NewPages;
		PrivateFind = NewPrivateFind;
		PublicFind = NewPublicFind;
		RockallBackEnd = NewRockallBackEnd;
		TopCache = (Caches2[ (Size2-2) ]);
#ifdef ENABLE_HEAP_STATISTICS

		//
		//   Zero the heap statistics.
		//
		CopyMisses = 0;
		MaxCopySize = 0;
		MaxNewSize = 0;
		NewMisses = 0;
		Reallocations = 0;
		TotalCopySize = 0;
		TotalNewSize = 0;

		//		Placing the global lock into the static memory reserved by lockMem
		if (AtomicIncrement(&_HeapCount) == 1)
		{
			_Globallock = PLACEMENT_NEW(_LockMem,GLOBALLOCK);
		}
		
#endif

		//
		//   The external allocation size must be reasonable.
		//   All allocation sizes must be a multiple of the
		//   minimum allocation size.  The minimum allocation
		//   size and the middle allocation size must be a 
		//   power of two.
		//   
		if 
				( 
				(ExternalCache -> GetPageSize() == TopCache -> GetPageSize())
					&& 
				(PowerOfTwo( RockallBackEnd -> NaturalSize() ))
					&&
				(RockallBackEnd -> NaturalSize() >= PageSize())
					&&
				(TopCache -> GetPageSize() >= PageSize())
					&&
				(PowerOfTwo( TopCache -> GetPageSize() ))
					&&
				((Stride1 > 0) && (PowerOfTwo( Stride1 )))
					&&
				((Stride2 >= Stride1) && (PowerOfTwo( Stride2 )))
					&&
				(ConvertDivideToShift( Stride1,& ShiftSize1 ))
					&&
				(ConvertDivideToShift( Stride2,& ShiftSize2 ))
				)
			{
			REGISTER SBIT32 Count1;
			REGISTER SBIT32 TopCacheSize = (TopCache -> GetPageSize());
			REGISTER SBIT32 MaxSize1 = (MidCacheSize / Stride1);
			REGISTER SBIT32 MaxSize2 = (TopCacheSize / Stride2);

			//
			//   Calculate the maximum number of free pages 
			//   that can be kept.  Also set the smallest parent
			//   mask to the maximum value.
			//
			MaxFreePages = (MaxFreeSpace / (TopCache -> GetAllocationSize()));
			SmallestParentMask = ((TopCache -> GetAllocationSize())-1);
			ThreadSafe = NewThreadSafe;

			//
			//   Calculate the sizes of the arrays that map 
			//   sizes to caches.
			//
			MaxTable1 = (MaxSize1 * sizeof(CACHE*));
			MaxTable2 = (MaxSize2 * sizeof(CACHE*));

			//
			//   The heap pages must be specified in asceding
			//   order of size and be an exact multiple of the
			//   minimum allocation size.
			//
			for ( Count1=0;Count1 < Size1;Count1 ++ )
				{
				REGISTER CACHE *Current = Caches1[ Count1 ];
				REGISTER CACHE *Next = Caches1[ (Count1+1) ];
				REGISTER SBIT32 AllocationSize = Current -> GetAllocationSize();
				REGISTER SBIT32 ChunkSize = Current -> GetChunkSize();
				REGISTER SBIT32 PageSize = Current -> GetPageSize();

				//
				//   Ensure each cache specification meets the
				//   requirements of the heap.  If not fail
				//   the heap entire heap creation.
				//
				if ( (AllocationSize % Stride1) != 0 )
					{ Failure( "Cache size not multiple of stride" ); }

				if ( AllocationSize >= Next -> GetAllocationSize() )
					{ Failure( "Cache sizes not in ascending order" ); }

				if ( (AllocationSize > ChunkSize) || (ChunkSize > PageSize) )
					{ Failure( "Chunk size not suitable for cache" ); }

				if ( AllocationSize >= PageSize )
					{ Failure( "Cache size larger than parent size" ); }

				if ( PageSize > TopCacheSize )
					{ Failure( "Parent size exceeds 'TopCache' size" ); }
				}

			//
			//   The heap pages must be specified in asceding
			//   order of size and be an exact multiple of the
			//   minimum allocation size.
			//
			for ( Count1=0;Count1 < (Size2-2);Count1 ++ )
				{
				REGISTER CACHE *Current = Caches2[ Count1 ];
				REGISTER CACHE *Next = Caches2[ (Count1+1) ];
				REGISTER SBIT32 AllocationSize = Current -> GetAllocationSize();
				REGISTER SBIT32 ChunkSize = Current -> GetChunkSize();
				REGISTER SBIT32 PageSize = Current -> GetPageSize();

				//
				//   Ensure each cache specification meets the
				//   requirements of the heap.  If not fail
				//   the heap entire heap creation.
				//
				if ( (AllocationSize % Stride2) != 0 )
					{ Failure( "Cache size not multiple of stride" ); }

				if ( AllocationSize >= Next -> GetAllocationSize() )
					{ Failure( "Cache sizes not in ascending order" ); }

				if ( (AllocationSize > ChunkSize) || (ChunkSize > PageSize) )
					{ Failure( "Chunk size not suitable for cache" ); }

				if ( AllocationSize >= PageSize )
					{ Failure( "Cache size larger than parent size" ); }

				if ( PageSize > TopCacheSize )
					{ Failure( "Parent size exceeds 'TopCache' size" ); }
				}

			//
			//   The external and top caches have special rules
			//   which must be checked to ensure these caches
			//   are valid.
			//
			for ( Count1=(Size2-2);Count1 < Size2;Count1 ++ )
				{
				REGISTER CACHE *Current = Caches2[ Count1 ];
				REGISTER SBIT32 AllocationSize = Current -> GetAllocationSize();

				//
				//   Ensure each cache specification meets the
				//   requirements of the heap.  If not fail
				//   the heap entire heap creation.
				//
				if ( (AllocationSize % Stride2) != 0 )
					{ Failure( "Top cache size not multiple of minimum" ); }

				if ( AllocationSize != Current -> GetChunkSize() )
					{ Failure( "Chunk size not suitable for top cache" ); }

				if ( AllocationSize != Current -> GetPageSize() )
					{ Failure( "Page size not suitable for top cache" ); }

				if ( Current -> GetCacheSize() != 0 )
					{ Failure( "Cache size not zero for top cache" ); }
				}

			//
			//   We need to allocate two arrays to enable requested
			//   sizes to be quickly mapped to allocation caches.
			//   Here we allocate the tables and later fill in all
			//   the necessary mapping information.
			//
			SizeToCache1 = (CACHE**) 
				(
				RockallBackEnd -> NewArea
					(
					(RockallBackEnd -> NaturalSize() - 1),
					(MaxTable1 + MaxTable2),
					False
					)
				);
#ifdef ENABLE_HEAP_STATISTICS

			//
			//   When we are compiled for statistics we keep
			//   information on all the allocations we see.
			//
			Statistics = (SBIT32*)
				(
				RockallBackEnd -> NewArea
					(
					(RockallBackEnd -> NaturalSize() - 1),
					(MaxCacheSize * sizeof(SBIT32)),
					False
					)
				);
#endif

			//
			//   We make sure that the allocations we made 
			//   did not fail.  If not we have to fail the 
			//   creation of the whole heap.
			//
			if 
					( 
					(SizeToCache1 != ((CACHE**) AllocationFailure))
#ifdef ENABLE_HEAP_STATISTICS
						&&
					(Statistics != ((SBIT32*) AllocationFailure))
#endif
					) 
				{
				REGISTER SBIT32 Count2;

				//
				//   Cycle through the first segment of the 
				//   mapping table creating approriate 
				//   translations.
				//
				for ( Count1=0,Count2=0;Count1 < MaxSize1;Count1 ++ )
					{
					//
					//   We make sure that the current allocation
					//   page is large enough to hold an element
					//   of some given size.  If not we move on to
					//   the next allocation page.
					//
					if 
							( 
							((Count1 + 1) * Stride1)
								> 
							(Caches1[ Count2 ] -> GetAllocationSize()) 
							)
						{ Count2 ++; }

					//
					//   Store a pointer so that a request for
					//   this size of allocation goes directly
					//   to the correct page.
					//
					SizeToCache1[ Count1 ] = Caches1[ Count2 ];
					}

				//
				//   Compute the start address for the second
				//   segment of the table.
				//
				SizeToCache2 = 
					((CACHE**) & ((CHAR*) SizeToCache1)[ MaxTable1 ]);

				//
				//   Cycle through the second segment of the 
				//   mapping table creating approriate 
				//   translations.
				//
				for ( Count1=0,Count2=0;Count1 < MaxSize2;Count1 ++ )
					{
					//
					//   We make sure that the current allocation
					//   page is large enough to hold an element
					//   of some given size.  If not we move on to
					//   the next allocation page.
					//
					if 
							( 
							((Count1 + 1) * Stride2)
								> 
							(Caches2[ Count2 ] -> GetAllocationSize()) 
							)
						{ Count2 ++; }

					//
					//   Store a pointer so that a request for
					//   this size of allocation goes directly
					//   to the correct page.
					//
					SizeToCache2[ Count1 ] = Caches2[ Count2 ];
					}

				//
				//   Now that we have created the size to cache 
				//   mappings lets use them to link each cache to  
				//   the cache it uses to allocate additional 
				//   memory.
				//
				for ( Count1=0;Count1 < (CachesSize-1);Count1 ++ )
					{
					REGISTER CACHE *CurrentCache = Caches[ Count1 ];
					REGISTER SBIT32 PageSize = CurrentCache -> GetPageSize();
					REGISTER CACHE *ParentCache = FindCache( PageSize );
					REGISTER BOOLEAN Top = (CurrentCache == ParentCache);

					//
					//   Ensure that the parent cache is suitable
					//   and in line with what we were expecting. 
					//
					if 
							(
							(PowerOfTwo( PageSize ))
								&&
							(PageSize >= MinParentSize)
								&&
							(PageSize == (ParentCache -> GetAllocationSize()))
							)
						{
						//
						//   We keep track of the smallest
						//   cache that is a parent.  We can
						//   use this to improve the performance
						//   of the find hash table.
						//
						if ( ((BIT32) PageSize) < SmallestParentMask )
							{ SmallestParentMask = (PageSize-1); }

						//
						//   Update the current cache with  
						//   information about it's parent 
						//   cache.
						//
						CurrentCache -> UpdateCache
							(
							this,
							NewPages,
							((Top) ? ((CACHE*) GlobalRoot) : ParentCache),
							PrivateFind,
							PublicFind
							); 
						} 
					else
						{ Failure( "Parent bucket is invalid" ); }
					}
#ifdef ENABLE_HEAP_STATISTICS

				//
				//   It is probably a good idea to zero the 
				//   statistics area.
				//
				ZEROMEMORY( Statistics,(MaxCacheSize * sizeof(SBIT32)) );
#endif

				//
				//   The external cache is an exact duplicate
				//   of the top cache and is used to hold all
				//   memory allocations that are too large for
				//   any bucket.  Nonetheless, its parent is
				//   still the top cache.
				//
				ExternalCache -> UpdateCache
					(
					this,
					NewPages,
					TopCache,
					PrivateFind,
					PublicFind
					);

				//
				//   Update the new page structure with the 
				//   details of the top cache.
				//
				NewPage -> UpdateNewPage( TopCache );

				//
				//   Update the hash table with the minimum
				//   parent size for this heap.
				//
				PrivateFind -> UpdateFind
					(
					(TopCache -> GetAllocationSize()-1),
					SmallestParentMask 
					);

				//
				//   If there is a public find table then
				//   update the hash table with the minimum
				//   parent size for this heap.
				//
				if ( PublicFind != NULL )
					{
					PublicFind -> UpdateFind
						(
						(TopCache -> GetAllocationSize()-1),
						SmallestParentMask 
						);
					}

				//
				//   Activate the heap.
				//
				Active = True;
				}
			else
				{ Failure( "Mapping table in constructor for HEAP" ); }
			}
		else
			{ Failure( "The allocation sizes in constructor for HEAP" ); }
		}
	else
		{ Failure( "A heap size in constructor for HEAP" ); }
    }

    /********************************************************************/
    /*                                                                  */
    /*   Memory deallocation.                                           */
    /*                                                                  */
    /*   We need to release some memory.  First we try to slave the     */
    /*   request in the free cache so we can do a batch of releases     */
    /*   later.  If not we are forced to do it at once.                 */
    /*                                                                  */
    /********************************************************************/

BOOLEAN HEAP::Delete( VOID *Address,SBIT32 Size )
	{
	//
	//   Although normally a class is never called before
	//   its constructor.  The heap is subject to some strange
	//   behaviour so we check to make sure this is not the
	//   case.
	//
	if ( Active )
		{
		//
		//   When the caller gives us the size of the 
		//   allocation we can short cut the deallocation 
		//   process by skipping directly to the correct 
		//   cache.  However, if the user supplies us
		//   with bogus data we will retry using the
		//   the full deallocation process.
		//
		if ( (Size > 0) && (Size <= MaxCacheSize) )
			{
			REGISTER CACHE *Cache = (FindCache( Size ));

			if ( PrivateFind -> Delete( Address,Cache ) )
				{ return True; }
			}

		//
		//   It looks like all we have is the address so 
		//   deallocate using the long path.
		//
		if ( PrivateFind -> Delete( Address,TopCache ) )
			{ return True; }
		else
			{
			//
			//   The 'Address' does not belong to this
			//   heap so lets try the public find table.
			//
			if ( PublicFind != NULL )
				{
				//
				//   When the caller gives us the size of the 
				//   allocation we can short cut the deallocation 
				//   process by skipping directly to the correct 
				//   cache.  However, if the user supplies us
				//   with bogus data we will retry using the
				//   the full deallocation process.
				//
				if ( (Size > 0) && (Size <= MaxCacheSize) )
					{
					REGISTER CACHE *Cache = (FindCache( Size ));

					if ( PublicFind -> Delete( Address,Cache ) )
						{ return True; }
					}

				//
				//   It looks like all we have is the address  
				//   so deallocate using the long path.
				//
				return (PublicFind -> Delete( Address,TopCache ));
				}
			}
		}

	return False;
	}

    /********************************************************************/
    /*                                                                  */
    /*   Delete all allocations.                                        */
    /*                                                                  */
    /*   We delete the entire heap and free all existing allocations.   */
    /*   If 'Recycle' is requested we slave the allocated memory as     */
    /*   we expect some new allocations.  If not we return all the      */
    /*   memory to the external allocator.                              */
    /*                                                                  */
    /********************************************************************/

VOID HEAP::DeleteAll( BOOLEAN Recycle )
	{
	//
	//   Although normally a class is never called before
	//   its constructor.  The heap is subject to some strange
	//   behaviour so we check to make sure this is not the
	//   case.
	//
	if ( Active )
		{
		REGISTER SBIT32 Count;

		//
		//   We claim all of the heap locks to freeze
		//   all new allocations or deletions.
		//
		LockAll();

		//
		//   Now reset all the caches and the find
		//   hash table statistics.
		//
		PrivateFind -> DeleteAll();

		for ( Count=0;Count < CachesSize;Count ++ )
			{ Caches[ Count ] -> DeleteAll(); }

		//
		//   Delete the heap.
		//
		NewPage -> DeleteAll( Recycle );

		//
		//   Now release all the heap locks we claimed
		//   earlier and unfreeze the heap.
		//
		UnlockAll();

		//
		//   Trim the free space if needed.
		//
		if ( Recycle )
			{ TopCache -> ReleaseSpace( MaxFreePages ); }
		}
	}

    /********************************************************************/
    /*                                                                  */
    /*   Details of a memory allocation.                                */
    /*                                                                  */
    /*   We need to the details of a particular memory allocation.      */
    /*   All we have is an address.  We use this to find the largest    */
    /*   allocation page this address is contained in and then          */
    /*   navigate through the sub-divisions of this page until we       */
    /*   find the allocation.                                           */
    /*                                                                  */
    /********************************************************************/

BOOLEAN HEAP::Details
		(
		VOID						  *Address,
		SEARCH_PAGE					  *Details,
		SBIT32						  *Size
		)
    {
	//
	//   Although normally a class is never called before
	//   its constructor.  The heap is subject to some strange
	//   behaviour so we check to make sure this is not the
	//   case.
	//
	if ( Active )
		{
		AUTO SBIT32 Dummy;

		//
		//   We allow the caller to omit the 'Size' parameter.
		//   I can see little reason for this but it is supported
		//   anyway.
		//
		if ( Size == NULL )
			{ Size = & Dummy; }

		//
		//   We may need to try more than one find table
		//   but we always try the the private one
		//   first to minimize shared memory accesses.
		//
		if ( PrivateFind -> Details( Address,Details,TopCache,Size ) )
			{ return True; }
		else
			{
			//
			//   We need to try the public find table
			//   as the private find table could not 
			//   locate the address.
			//
			return
				(
				(PublicFind != NULL)
					&&
				(PublicFind -> Details( Address,Details,TopCache,Size ))
				);
			}
		}

	return False;
	}

    /********************************************************************/
    /*                                                                  */
    /*   Find a cache.                                                  */
    /*                                                                  */
    /*   Find the allocation cache for the size supplied and return     */
    /*   a pointer to it.                                               */
    /*                                                                  */
    /********************************************************************/

CACHE *HEAP::FindCache( SBIT32 Size )
	{
#ifdef DISABLED_AT_PRESENT
	REGISTER CACHE *Cache;

#endif
	//
	//   Compute the cache address.
	//
	if ( Size < MidCacheSize )
		{ return (SizeToCache1[ ((Size-1) >> ShiftSize1) ]); }
	else
		{ return (SizeToCache2[ ((Size-1) >> ShiftSize2) ]); }
#ifdef DISABLED_AT_PRESENT

	//
	//   Prefetch the class data if we are running a
	//   Pentium III or better with locks.  We do this
	//   because prefetching hot SMP data structures
	//   really helps.  However, if the structures are
	//   not shared (i.e. no locks) then it is worthless
	//   overhead.
	//
	if ( ThreadSafe )
	{ PREFETCH::Nta( ((CHAR*) Cache),sizeof(CACHE) ); }

	return Cache;
#endif
	}

    /********************************************************************/
    /*                                                                  */
    /*   A known area.                                                  */
    /*                                                                  */
    /*   A known area is an address that is understood to be            */
    /*   accessable from the current heap.  If the 'SingleImage'        */
    /*   flag is set it may actually be part of another heap that       */
    /*   the current heap can access.                                   */
    /*                                                                  */
    /********************************************************************/

BOOLEAN HEAP::KnownArea( VOID *Address )
	{
	//
	//   Although normally a class is never called before
	//   its constructor.  The heap is subject to some strange
	//   behaviour so we check to make sure this is not the
	//   case.
	//
	if ( Active )
		{
		//
		//   We may need to try more than one find table
		//   but we always try the the private one
		//   first to minimize shared memory accesses.
		//
		if ( PrivateFind -> KnownArea( Address,TopCache ) )
			{ return True; }
		else
			{
			//
			//   We need to try the public find table
			//   as the private find table could not 
			//   locate the area.
			//
			if ( PublicFind != NULL )
				{ return (PublicFind -> KnownArea( Address,TopCache )); }
			}
		}

	return False;
	}
    /********************************************************************/
    /*                                                                  */
    /*   Claim a lock on the entire heap.                               */
    /*                                                                  */
    /*   We claim a lock on the heap to improve performance             */
    /*   or prevent others from performing heap operations.             */
    /*                                                                  */
    /********************************************************************/

VOID HEAP::LockAll( VOID )
	{
	//
	//   Although normally a class is never called before
	//   its constructor.  The heap is subject to some strange
	//   behaviour so we check to make sure this is not the
	//   case.
	//
	if ( Active )
		{
		//
		//   We claim the locks if we have not already
		//   claimed them earlier.
		//
		if ( ThreadSafe -> ClaimGlobalLock() )
			{
			REGISTER SBIT32 Count;

			//
			//   We claim all of the heap locks to freeze
			//   all new allocations or deletions.
			//
			for ( Count=0;Count < CachesSize;Count ++ )
				{ Caches[ Count ] -> ClaimCacheLock(); }

			//
			//  Although the heap is frozen at this point
			//  we claim the last few locks just to be
			//  tidy.
			//
			PrivateFind -> ClaimFindExclusiveLock();

			NewPage -> ClaimNewPageLock();

			//
			//   Engage the global lock and make it active
			//   to prevent further lock calls on this
			//   thread.
			//
			ThreadSafe -> EngageGlobalLock();
			}
		}
	}

    /********************************************************************/
    /*                                                                  */
    /*   Delete multiple allocations.                                   */
    /*                                                                  */
    /*   We need to release multiple memory allocations.  First we try  */
    /*   to slave the requets in the free cache so we can do a batch    */
    /*   of releases later.  If not we are forced to do it immediately. */
    /*                                                                  */
    /********************************************************************/

BOOLEAN HEAP::MultipleDelete
		( 
		SBIT32						  Actual,
		VOID						  *Array[],
		SBIT32						  Size 
		)
	{
	//
	//   Although normally a class is never called before
	//   its constructor.  The heap is subject to some strange
	//   behaviour so we check to make sure this is not the
	//   case.
	//
	if ( Active )
		{
		REGISTER SBIT32 Count;
		REGISTER BOOLEAN Result = True;
		REGISTER CACHE *ParentCache = ((CACHE*) GlobalRoot);

		//
		//   When the caller gives us the size of the allocation
		//   we can short cut the deallocation process by skipping
		//   directly to the correct cache.  However, if the user
		//   supplies us with bogus data we will retry using the
		//   the long path.
		//
		if ( (Size > 0) && (Size <= MaxCacheSize) )
			{
			REGISTER CACHE *Cache = (FindCache( Size ));

			//
			//   Compute a pointer to the parent cache
			//   so we can optimize the deletes.
			//
			ParentCache = (Cache -> GetParentCache());

			//
			//   Delete each memory allocation one at a time.
			//   We would like to delete them all at once but
			//   we can't be sure they are all vaild or related.
			//
			for ( Count=0;Count < Actual;Count ++ )
				{
				REGISTER VOID *Address = Array[ Count ];

				//
				//   Lets try to optimize the delete.
				//
				if ( ! PrivateFind -> Delete( Address,ParentCache ) ) 
					{
					//
					//   Lets try a standard full delete.
					//
					if ( ! PrivateFind -> Delete( Address,TopCache ) )
						{
						//
						//   The 'Address' does not belong
						//   to the current heap so lets
						//   try using the global find table
						//   to try a global delete.
						//
						if ( ! PublicFind -> Delete( Address,TopCache ) )
							{ Result = False; }
						}
					}
				}
			}
		else
			{
			//
			//   Delete each memory allocation one at a time.
			//   We would like to delete them all at once but
			//   we can't be sure they are all vaild or related.
			//
			for ( Count=0;Count < Actual;Count ++ )
				{
				REGISTER VOID *Address = Array[ Count ];

				//
				//   Lets try a standard full delete.
				//
				if ( ! PrivateFind -> Delete( Address,TopCache ) )
					{
					//
					//   The 'Address' does not belong to
					//   the current heap so lets try using
					//   the global find table to try a
					//   global delete.
					//
					if ( ! PublicFind -> Delete( Address,TopCache ) )
						{ Result = False; }
					}
				}
			}

		return Result;
		}
	else
		{ return False; }
	}

    /********************************************************************/
    /*                                                                  */
    /*   Multiple memory allocations.                                   */
    /*                                                                  */
    /*   We have been asked to allocate muliple memory blocks.   We     */
    /*   we do this by using the cache and then claiming and addition   */
    /*   space from the heap as needed.                                 */
    /*                                                                  */
    /********************************************************************/

BOOLEAN HEAP::MultipleNew
		( 
		SBIT32						  *Actual,
		VOID						  *Array[],
		SBIT32						  Requested,
		SBIT32						  Size,
		SBIT32						  *Space,
		BOOLEAN						  Zero
		)
	{
	//
	//   Although normally a class is never called before
	//   its constructor.  The heap is subject to some strange
	//   behaviour so we check to make sure this is not the
	//   case.
	//
	if ( Active )
		{
		AUTO SBIT32 Dummy;

		//
		//   We allow the caller to omit the 'Actual' parameter.
		//   I can see little reason for this but it is supported
		//   anyway.  Regardless we zero it.
		//
		if ( Actual == NULL )
			{ Actual = & Dummy; }

		(*Actual) = 0;

		//
		//   We need to be sure that the size requested is in the 
		//   range supported by the memory allocator.  If not we
		//   do a series of single allocations from the default
		//   allocator.
		//
		if ( (Size > 0) && (Size <= MaxCacheSize) )
			{
			REGISTER CACHE *Cache = (FindCache( Size ));
			REGISTER SBIT32 NewSize = (Cache -> GetAllocationSize());

			//
			//   Allocate memory from the appropriate 
			//   allocation bucket.
			//
			(VOID) Cache -> MultipleNew( Actual,Array,Requested );

			//
			//   If needed return the actual amount  
			//   of space allocated for each element.
			//
			if ( Space != NULL )
				{ (*Space) = NewSize; }
#ifdef ENABLE_HEAP_STATISTICS

			//
			//   Update the allocation statistics.
			//
			Statistics[ (Size-1) ] += Requested;
#endif

			//
			//   If needed zero each element that is  
			//   allocated.
			//
			if ( Zero )
				{
				REGISTER SBIT32 Count;

				for ( Count=((*Actual)-1);Count >= 0;Count -- )
					{ ZEROMEMORY( Array[ Count ],NewSize ); }
				}

			return ((*Actual) == Requested);
			}
		else
			{
			//
			//   If the allocation size is greater than
			//   zero we create the allocations.  If not
			//   we fail the request.
			//
			if ( Size > 0 )
				{
				//
				//   We have got a request for an element size
				//   larger than the largest bucket size.  So 
				//   we call the single allocation interface 
				//   as this supports large sizes.
				//
				for 
					( 
					/* void */;
					((*Actual) < Requested)
						&&
					((Array[ (*Actual) ] = New( Size )) != AllocationFailure);
					(*Actual) ++ 
					);

				//
				//   If needed return the actual amount of space 
				//   allocated for each element.
				//
				if ( Space != NULL )
					{ (*Space) = Size; }

				return ((*Actual) == Requested);
				}
			}
		}

	return False;
	}

    /********************************************************************/
    /*                                                                  */
    /*   Memory allocation.                                             */
    /*                                                                  */
    /*   We have been asked to allocate some memory.  Hopefully,        */
    /*   we will be able to do this out of the cache.  If not we        */
    /*   will need to pass it along to the appropriate allocation       */
    /*   bucket.                                                        */
    /*                                                                  */
    /********************************************************************/

VOID *HEAP::New( SBIT32 Size,SBIT32 *Space,BOOLEAN Zero )
	{
	REGISTER VOID *NewMemory = ((VOID*) AllocationFailure);

	//
	//   Although normally a class is never called before
	//   its constructor. The heap is subject to some strange
	//   behaviour so we check to make sure this is not the
	//   case.
	//
	if ( Active )
		{
		//
		//   We ensure the allocation size is in
		//   the range supported by the heap.
		//
		if ( (Size > 0) && (Size <= MaxCacheSize) )
			{
			REGISTER CACHE *Cache = (FindCache( Size ));
#ifdef ENABLE_HEAP_STATISTICS

			//
			//   Update the allocation statistics.
			//
			Statistics[ (Size-1) ] ++;
#endif

			//
			//   Allocate memory from the appropriate
			//   cache in the heap.
			//
			NewMemory = (Cache -> New()); 
			Size = (Cache -> GetAllocationSize());
			}
		else
			{ 
			//
			//   If the allocation size is greater than
			//   zero we create the allocation.  If not
			//   we fail the request.
			//
			if ( Size > 0 )
				{
#ifdef ENABLE_HEAP_STATISTICS
				//
				//   Update the allocation statistics.
				//
				if ( Size > MaxNewSize )
					{ MaxNewSize = Size; }

				NewMisses ++;
				TotalNewSize += Size;

#endif
				//
				//   Allocate memory from a special
				//   cache bucket which gets space
				//   externally.
				//
				NewMemory = (ExternalCache -> New( False,Size ));
				}
			else
				{ NewMemory = ((VOID*) AllocationFailure); }
			}

		//
		//   We need to be sure that the allocation 
		//   request did not fail.
		//
		if ( NewMemory != ((VOID*) AllocationFailure) )
			{
			//
			//   If needed return the actual amount of space 
			//   allocated for this request.
			//
			if ( Space != NULL )
				{ (*Space) = Size; }

			//
			//   Zero the memory if the needed.
			//
			if ( Zero )
				{ ZEROMEMORY( NewMemory,Size ); }
			}
		}

	return NewMemory;
	}
#ifdef ENABLE_HEAP_STATISTICS

    /********************************************************************/
    /*                                                                  */
    /*   Print statistics.                                              */
    /*                                                                  */
    /*   We output the allocation statistics to the debug console.      */
    /*                                                                  */
    /********************************************************************/

VOID HEAP::PrintHeapStatistics( VOID )
	{
	REGISTER SBIT32 Count1;
	REGISTER SBIT32 Count2;
	REGISTER SBIT32 HighWater = 0;
	REGISTER SBIT32 Total = 0;
	
	//
	//   Claim a lock so that multiple threads have
	//   to wait to output any heap statistics.
	//
	if (!_Globallock)
		Failure("Unable to print heap statistics due lack of lock!",true);
	_Globallock->ClaimLock();

	//
	//
	//   Output the heap details to the debug
	//   console along with an explaination.
	//
	DebugPrint
		(
		"\n"
		"Heap at 0x%x\n"
		"\n"
		"A summary of all the requested sizes, "
		"supplied sizes and associated counts.\n"
		"A brief examination of this summary "
		"should provide a good understanding of\n"
		"an applications overall heap usage"
		"patterns.\n"
		"\n",
		this
		);

	//
	//   Output the titles for the summary table.
	//
	for ( Count1=0;Count1 < MaxSummaryColumns;Count1 ++ )
		{ DebugPrint( "   Actual  Supplied    Total            " ); }

	//
	//   Skip to a new line.
	//
	DebugPrint( "\n" );

	for ( Count1=0;Count1 < MaxSummaryColumns;Count1 ++ )
		{ DebugPrint( "    Size     Size      Calls            " ); }

	//
	//   Skip to a new line.
	//
	DebugPrint( "\n" );

	//
	//   Output the contents of the summary table.
	//
	for ( Count1=0;Count1 < MaxCacheSize;/* void */ )
		{
		//
		//   Output the each non-zero data point.
		//
		for 
				( 
				Count2=0;
				(Count1 < MaxCacheSize) && (Count2 < MaxSummaryColumns);
				Count1 ++ 
				)
			{
			REGISTER SBIT32 Hits = Statistics[ Count1 ];

			//
			//   When the nimber of hits is 
			//   non-zero we output the data.
			//
			if ( Hits > 0 )
				{
				REGISTER CACHE *Cache = FindCache( (Count1+1) );

				//
				//   Output the values.
				//
				DebugPrint
					(
					"%8d  %8d  %8d            ",
					(Count1 + 1),
					Cache -> GetAllocationSize(),
					Hits
					);

				//
				//   Update the column and totals.
				//
				Count2 ++;

				Total += Hits;
				}
			}

		//
		//   Skip to a new line.
		//
		DebugPrint( "\n" );
		}

	//
	//
	//   Output the cache details to the debug
	//   console along with an explaination.
	//
	DebugPrint
		(
		"\n"
		"A summary of the activity of all the "
		"heap caches.  A brief examination of\n"
		"this summary should provide a good "
		"understanding of how the heap dealt\n"
		"with the applications heap usage"
		"patterns.\n"
		);

	//
	//   Output the titles to the debug console.
	//
	DebugPrint
		( 
		"\n"
		"   Actual    Cache     Cache      High      High     Active\n"
		"    Size     Fills    Flushes     Tide      Water    In Use\n"
		);

	//
	//   Output details for every sample size.
	//
	for ( Count1=0;Count1 < CachesSize;Count1 ++ )
		{
		REGISTER CACHE *Cache = Caches[ Count1 ];

		//
		//   Skip this cache if it was not active
		//   as the statistics will be all zeros.
		//
		if ( Cache -> GetHighWater() > 0 )
			{
			//
			//   Output the cache details.
			//
			DebugPrint
				(
				"%8d  %8d  %8d  %8d  %8d  %8d\n",
				Cache -> GetAllocationSize(),
				Cache -> GetCacheFills(),
				Cache -> GetCacheFlushes(),
				Cache -> GetHighTide(),
				Cache -> GetHighWater(),
				Cache -> GetInUse()
				);
			
			//
			//   Compute the space high water mark.
			//
			HighWater += 
				(Cache -> GetHighWater() * Cache -> GetAllocationSize());
			}
		}

	//
	//   Print the private hash table statistics.
	//
	DebugPrint
		(
		"\n"
		"Private Hash Table Statistics\n"
		"-----------------------------\n"
		"\t*** Cache ***\n"
		"\tFills\t\t: %d\n"
		"\tHits\t\t: %d\n"
		"\tMisses\t\t: %d\n"
		"\t*** Table ***\n"
		"\tAverage\t\t: %d\n"
		"\tMax\t\t: %d\n"
		"\tScans\t\t: %d\n"
		"\tMax Hash\t: %d\n"
		"\tMax LookAside\t: %d\n"
		"\tUsage\t\t: %d%%\n",
		PrivateFind -> CacheFills(),
		PrivateFind -> CacheHits(),
		PrivateFind -> CacheMisses(),
		PrivateFind -> AverageHashLength(),
		PrivateFind -> MaxHashLength(),
		PrivateFind -> TotalScans(),
		PrivateFind -> MaxHashSize(),
		PrivateFind -> MaxLookAsideSize(),
		PrivateFind -> MaxUsage()
		);

	
	//
	//   Print the public hash table statistics
	//   (if applicable).
	//
	if ( PublicFind != NULL )
		{
		DebugPrint
			(
			"\n"
			"Public Hash Table Statistics\n"
			"----------------------------\n"
			"\t*** Cache ***\n"
			"\tFills\t\t: %d\n"
			"\tHits\t\t: %d\n"
			"\tMisses\t\t: %d\n"
			"\t*** Table ***\n"
			"\tAverage\t\t: %d\n"
			"\tMax\t\t: %d\n"
			"\tScans\t\t: %d\n"
			"\tMax Hash\t: %d\n"
			"\tMax LookAside\t: %d\n"
			"\tUsage\t\t: %d%%\n",
			PublicFind -> CacheFills(),
			PublicFind -> CacheHits(),
			PublicFind -> CacheMisses(),
			PublicFind -> AverageHashLength(),
			PublicFind -> MaxHashLength(),
			PublicFind -> TotalScans(),
			PublicFind -> MaxHashSize(),
			PublicFind -> MaxLookAsideSize(),
			PublicFind -> MaxUsage()
			);
		}

	//
	//   Print the reallocation statistics.
	//

	DebugPrint
		(
		"\n"
		"Oversize Statistics\n"
		"-------------------\n"
		"\tAverage Size\t: %d\n"
		"\tMax Size\t: %d\n"
		"\tMisses\t\t: %d\n",
		(TotalNewSize / ((NewMisses > 0) ? NewMisses : 1)),
		MaxNewSize,
		NewMisses
		);

	//
	//   Print the reallocation statistics.
	//
	DebugPrint
		(
		"\n"
		"Realloc Statistics\n"
		"------------------\n"
		"\tAverage Copy\t: %d\n"
		"\tCalls\t\t: %d\n"
		"\tMax Copy\t: %d\n"
		"\tTotal Copies\t: %d\n",
		(TotalCopySize / ((CopyMisses > 0) ? CopyMisses : 1)),
		Reallocations,
		MaxCopySize,
		CopyMisses
		);

	//
	//   Print the general statistics.
	//

	DebugPrint
		(
		"\n"
		"Summary Statistics\n"
		"------------------\n"
		"\tMax Heap\t: %d\n"
		"\tTotal Calls\t: %d\n",
		HighWater,
		Total
		);

	//
	//   Relesae the lock.
	//
	_Globallock->ReleaseLock();
	}
#endif

    /********************************************************************/
    /*                                                                  */
    /*   Memory reallocation.                                           */
    /*                                                                  */
    /*   We have been asked to reallocate some memory.  Hopefully,      */
    /*   we will be able to do this out of the cache.  If not we        */
    /*   will need to pass it along to the appropriate allocation       */
    /*   bucket, do a copy and free the orginal allocation.             */
    /*                                                                  */
    /********************************************************************/

VOID *HEAP::Resize
		( 
		VOID						  *Address,
		SBIT32						  NewSize,
		SBIT32						  Move,
		SBIT32						  *Space,
		BOOLEAN						  NoDelete,
		BOOLEAN						  Zero
		)
	{
	//
	//   Although normally a class is never called before
	//   its constructor.  The heap is subject to some strange
	//   behaviour so we check to make sure this is not the
	//   case.
	//
	if ( Active )
		{
		AUTO SBIT32 Size;
		AUTO SBIT32 NewSpace;

		//
		//   Find the details of the existing allocation.
		//   If there is no existing allocation then exit.
		//
		if ( Details( Address,NULL,& Size ) )
			{
			REGISTER VOID *NewMemory;
			REGISTER SBIT32 Smallest = ((Size < NewSize) ? Size : NewSize);

			//
			//   Make sure the sizes appear to make sense.
			//
			if ( Smallest > 0 )
				{
#ifdef ENABLE_HEAP_STATISTICS
				//
				//   Update the statistics.
				//
				Reallocations ++;

#endif
				//
				//   When the new allocation allocation is 
				//   standard heap allocation size we check 
				//   for various optimizations.
				//
				if ( NewSize <= MaxCacheSize )
					{
					REGISTER CACHE *Cache = (FindCache( NewSize ));
					REGISTER SBIT32 CacheSize = (Cache -> GetAllocationSize());
					REGISTER SBIT32 Delta = (CacheSize - Size);
					
					//
					//   We only need to reallocate if the new
					//   size is larger than the current bucket
					//   or the new size is smaller and we have 
					//   been given permission to move the 
					//   allocation.
					//
					if ( ResizeTest( Delta,Move ) )
						{
						//
						//   We need to allocate some more
						//   memory and copy the old data.
						//   into the new area.
						//
						NewMemory = (Cache -> New());
						NewSpace = CacheSize;
#ifdef ENABLE_HEAP_STATISTICS

						//
						//   Update the statistics.
						//
						Statistics[ (NewSize-1) ] ++;
#endif
						}
					else
						{
						//
						//   If the new size is unchanged or smaller
						//   then just return the current allocation.
						//   If the new size is larger then we must
						//   fail the call.
						//
						if ( Delta <= 0 )
							{
							//
							//   The amount of memory allocated for  
							//   this request is unchanged so return  
							//   the current size.
							//
							if ( Space != NULL )
								{ (*Space) = Size; }

							return Address; 
							}
						else
							{ return ((VOID*) AllocationFailure); }
						}
					}
				else
					{
					REGISTER SBIT32 Delta = (NewSize - Size);

					//
					//   We only need to reallocate if the new
					//   size is larger than the current bucket
					//   or the new size is smaller and we have 
					//   been given permission to move the 
					//   allocation.
					//
					if ( ResizeTest( Delta,Move ) )
						{
						//
						//   One of the sizes is not within the
						//   allocation range of the heap.  So
						//   I have to punt and reallocate.
						//   
						NewMemory = 
							(
							ExternalCache -> New
								( 
								False,
								(NewSpace = NewSize)
								)
							);
#ifdef ENABLE_HEAP_STATISTICS

						//
						//   Update the allocation statistics.
						//
						if ( NewSize > MaxNewSize )
							{ MaxNewSize = NewSize; }

						NewMisses ++;
						TotalNewSize += NewSize;
#endif
						}
					else
						{
						//
						//   If the new size is unchanged or smaller
						//   then just return the current allocation.
						//   If the new size is larger then we must
						//   fail the call.
						//
						if ( Delta <= 0 )
							{
							//
							//   The amount of memory allocated for  
							//   this request is unchanged so return  
							//   the current size.
							//
							if ( Space != NULL )
								{ (*Space) = Size; }

							return Address; 
							}
						else
							{ return ((VOID*) AllocationFailure); }
						}
					}
				
				//
				//   We need to make sure we were able to allocate
				//   the new memory otherwise the copy will fail.
				//
				if ( NewMemory != ((VOID*) AllocationFailure) )
					{
					//
					//   Copy the contents of the old allocation 
					//   to the new allocation.
					//
					MEMCPY
						( 
						((void*) NewMemory),
						((void*) Address),
						((int) Smallest) 
						);

					//
					//   If needed return the actual amount of  
					//   space allocated for this request.
					//
					if ( Space != NULL )
						{ (*Space) = NewSpace; }

					//
					//   Delete the old allocation unless we
					//   need to keep it around.
					//
					if ( ! NoDelete )
						{
						//
						//   Delete the old allocation.
						//
						if ( ! Delete( Address,Size ) )
							{ Failure( "Deleting allocation in Resize" ); }
						}

					//
					//   Zero the memory if the needed.
					//
					if ( Zero )
						{
						REGISTER SBIT32 Difference = (NewSpace - Smallest);

						//
						//   If the new size is larger than 
						//   old size then zero the end of the
						//   new allocation.
						//
						if ( Difference > 0 )
							{ 
							REGISTER CHAR *Array = ((CHAR*) NewMemory);

							ZEROMEMORY( & Array[ Smallest ],Difference ); 
							} 
						}	
#ifdef ENABLE_HEAP_STATISTICS

					//
					//   Update the allocation statistics.
					//
					if ( Smallest > MaxCopySize )
						{ MaxCopySize = Smallest; }

					CopyMisses ++;
					TotalCopySize += Smallest;
#endif
					}

				return NewMemory;
				}
			}
		}

	return ((VOID*) AllocationFailure);
	}

    /********************************************************************/
    /*                                                                  */
    /*   Truncate the heap.                                             */
    /*                                                                  */
    /*   We need to truncate the heap.  This pretty much a do nothing   */
    /*   as we do this automatically anyway.  The only thing we can     */
    /*   do is free any space the user suggested keeping earlier.       */
    /*                                                                  */
    /********************************************************************/

BOOLEAN HEAP::Truncate( SBIT32 MaxFreeSpace )
	{
	REGISTER BOOLEAN Result = True;

	//
	//   Although normally a class is never called before
	//   its constructor.  The heap is subject to some strange
	//   behaviour so we check to make sure this is not the
	//   case.
	//
	if ( Active )
		{
		REGISTER SBIT32 Count;

		//
		//   Flush all the caches and to free up
		//   as much space as possible.
		//
		for ( Count=0;Count < CachesSize;Count ++ )
			{
			if ( ! Caches[ Count ] -> Truncate() )
				{ Result = False; }
			}

		//
		//   We slave all available free space in the top
		//   bucket so force it to be released.
		//
		TopCache -> ReleaseSpace
			(
			(MaxFreeSpace / (TopCache -> GetAllocationSize()))
			);
		}

	return Result;
	}

    /********************************************************************/
    /*                                                                  */
    /*   Release all the heap locks.                                    */
    /*                                                                  */
    /*   We release the locks so others can use the heap.               */
    /*                                                                  */
    /********************************************************************/

VOID HEAP::UnlockAll( BOOLEAN Partial )
	{
	//
	//   Although normally a class is never called before
	//   its constructor.  The heap is subject to some strange
	//   behaviour so we check to make sure this is not the
	//   case.
	//
	if ( Active )
		{
		//
		//   We release the locks only if we have claimed 
		//   them earlier.
		//
		if ( ThreadSafe -> ReleaseGlobalLock( True ) )
			{
			//
			//   Now release all the heap locks we claimed
			//   earlier and unfreeze the heap.
			//
			NewPage -> ReleaseNewPageLock();

			PrivateFind -> ReleaseFindExclusiveLock();

			//
			//   When we destroy the heap we hold on
			//   to the cache locks to prevent errors.
			//
			if ( ! Partial )
				{
				REGISTER SBIT32 Count;

				//
				//   Now release all the cache locks we claimed
				//   earlier and unfreeze the cache.
				//
				for ( Count=0;Count < CachesSize;Count ++ )
					{ Caches[ Count ] -> ReleaseCacheLock(); }
				}
			}
		}
	}

    /********************************************************************/
    /*                                                                  */
    /*   Verify of a memory allocation.                                 */
    /*                                                                  */
    /*   We need to verify the details of a memory allocation.          */
    /*   All we have is an address.  We use this to find the largest    */
    /*   allocation page this address is contained in and then          */
    /*   navigate through the sub-divisions of this page until we       */
    /*   find the allocation.  Finally, we check that the element       */
    /*   is not in the cache waiting to be allocated or freed.          */
    /*                                                                  */
    /********************************************************************/

BOOLEAN HEAP::Verify( VOID *Address,SBIT32 *Size )
    {
	//
	//   Although normally a class is never called before
	//   its constructor.  The heap is subject to some strange
	//   behaviour so we check to make sure this is not the
	//   case.
	//
	if ( Active )
		{
		AUTO SEARCH_PAGE FullDetails;
		AUTO SBIT32 NewSize;

		//
		//   We extract the size of the allocation and  
		//   any associated allocation information.
		//   to see if it is present.
		//
		if ( Details( Address,& FullDetails,& NewSize ) )
			{
			//
			//   We need to be careful to make sure this 
			//   element is actually allocated.
			//
			if ( (*FullDetails.VectorWord) & FullDetails.AllocationMask )
				{
				//
				//   It is possible for a user to give an
				//   address that is in the middle of an
				//   allocation instead of at the beginning.
				//
				if ( FullDetails.Found )
					{
					//
					//   We know that the element appears to be 
					//   allocated but it may be in the cache
					//   somewhere so ensure this is not the case.
					//
					if ( (NewSize > 0) && (NewSize <= MaxCacheSize) )
						{
						if ( FullDetails.Cache -> SearchCache( Address ) )
							{ return False; }
						}

					//
					//   We have shown that the element is active
					//   so return the size if requested.
					//
					if ( Size != NULL )
						{ (*Size) = NewSize; }

					return True;
					}
				}
			}
		}

	return False;
	}

    /********************************************************************/
    /*                                                                  */
    /*   Walk the heap.                                                 */
    /*                                                                  */
    /*   We have been asked to walk the heap.  It is hard to know       */
    /*   whay anybody might want to do this given the rest of the       */
    /*   functionality available.  Nonetheless, we just do what is      */
    /*   required to keep everyone happy.                               */
    /*                                                                  */
    /********************************************************************/

BOOLEAN HEAP::Walk
		( 
		BOOLEAN						  *Active,
		VOID						  **Address,
		SBIT32						  *Size 
		)
    {
	//
	//   Although normally a class is never called before
	//   its constructor.  The heap is subject to some strange
	//   behaviour so we check to make sure this is not the
	//   case.
	//
	if ( Active )
		{
		//
		//   We walk the heap and find the next allocation
		//   along with some basic information.
		//
		if ( PrivateFind -> Walk( Active,Address,TopCache,Size ) )
			{
			//
			//   We know that the element appears to be 
			//   allocated but it may be in the cache
			//   somewhere so ensure this is not the case.
			//
			if ( ((*Size) > 0) && ((*Size) <= MaxCacheSize) )
				{
				if ( FindCache( (*Size) ) -> SearchCache( (*Address) ) )
					{ (*Active) = False; }
				}

			return True;
			}
		}

	return False;
    }

    /********************************************************************/
    /*                                                                  */
    /*   Class destructor.                                              */
    /*                                                                  */
    /*   We would like to destroy the heap at the end of the run        */
    /*   just to be tidy.  However, to do this we need to know that     */
    /*   all of the other destructors have been called and that the     */
    /*   application will not request more memory or use any existing   */
    /*   allocations.  We can't know this without help from the         */
    /*   compiler and OS.                                               */
    /*                                                                  */
    /********************************************************************/

HEAP::~HEAP( VOID )
	{
	REGISTER SBIT32 Count;

	//
	//   We mark the heap as inactive.
	//
	Active = False;

	//
	//   We claim all of the heap locks to freeze
	//   all new allocations or deletions.
	//
	LockAll();
#ifdef ENABLE_HEAP_STATISTICS

	//
	//   Deal with heap statistics.
	//
	if ( Statistics != NULL ) 
		{
		//
		//   Print all the statistics.
		//
		PrintHeapStatistics();

		//
		//   Deallocate the area.
		//
		RockallBackEnd -> DeleteArea
			( 
			((VOID*) Statistics),
			(MaxCacheSize * sizeof(SBIT32)),
			False
			); 
		}

		BYTE temp[sizeof(_LockMem)];
		MEMCPY(temp, _LockMem, sizeof(_LockMem));
		if (!AtomicDecrement(&_HeapCount))
		{
			PLACEMENT_DELETE((GLOBALLOCK*)temp,GLOBALLOCK);
		}
#endif

	//
	//   Now reset all the caches.
	//
	for ( Count=0;Count < CachesSize;Count ++ )
		{ Caches[ Count ] -> DeleteAll(); }

	//
	//   Delete the heap.
	//
	NewPage -> DeleteAll( False );

	//
	//   We release any of the shared locks we 
	//   cliamed earlier.
	//
	UnlockAll( True );

	//
	//   Delete the heap mapping tables.
	//
	if ( SizeToCache1 != NULL ) 
		{ 	
		//
		//   Deallocate the area.
		//
		RockallBackEnd -> DeleteArea
			( 
			((VOID*) SizeToCache1),
			(MaxTable1 + MaxTable2),
			False
			); 
		}
	}
