//                                        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 "InterfacePCH.hpp"

#include "Heap.hpp"
#include "RockallDebugBackEnd.hpp"
#include "RockallDebugFrontEnd.hpp"

    /********************************************************************/
    /*                                                                  */
    /*   Constants local to the class.                                  */
    /*                                                                  */
    /*   The constants supplied here try to make the layout of the      */
    /*   the caches easier to understand and update.                    */
    /*                                                                  */
    /********************************************************************/

CONST SBIT32 FindCacheSize			  = 2048;
CONST SBIT32 FindCacheThreshold		  = 0;
CONST SBIT32 FindSize				  = 1024;

    /********************************************************************/
    /*                                                                  */
    /*   The description bit vectors.                                   */
    /*                                                                  */
    /*   All heaps keep track of allocations using bit vectors.  An     */
    /*   allocation requires 2 bits to keep track of its state.  The    */
    /*   following array supplies the size of the available bit         */
    /*   vectors measured in 32 bit words.                              */
    /*                                                                  */
    /********************************************************************/

STATIC int NewPageSizes[] = { 1,4,0 };

    /********************************************************************/
    /*                                                                  */
    /*   Class constructor.                                             */
    /*                                                                  */
    /*   The overall structure and layout of the heap is controlled     */
    /*   by the various constants and calls made in this function.      */
    /*   There is a significant amount of flexibility available to      */
    /*   a heap which can lead to them having dramatically different    */
    /*   properties.                                                    */
    /*                                                                  */
    /********************************************************************/

ROCKALL_DEBUG_FRONT_END::ROCKALL_DEBUG_FRONT_END
		(
		CACHE_DETAILS				  *Caches1,
		CACHE_DETAILS				  *Caches2,
		int							  MaxFreeSpace,
		ROCKALL_BACK_END			  *RockallBackEnd,
		bool						  Recycle,
		bool						  SingleImage,
		int							  Stride1,
		int							  Stride2,
		bool						  ThreadSafe
		) :
		//
		//   Call the constructors for the contained classes.
		//
		ROCKALL_FRONT_END
			(
			Caches1,
			Caches2,
			FindCacheSize,
			FindCacheThreshold,
			FindSize,
			((MaxFreeSpace == 0) ? MaxFreeSpace : 0),
			NewPageSizes,
			RockallBackEnd,
			Recycle,
			SingleImage,
			Stride1,
			Stride2,
			ThreadSafe
			)
	{
	//
	//   We make much use of the guard value in the
	//   debug heap so here we try to claim the 
	//   address but not commit it so we will ensure
	//   an access violation if the program ever
	//   tries to access it.
	//
	VirtualAlloc
		( 
		((void*) GuardValue),
		GuardSize,
		MEM_RESERVE,
		PAGE_NOACCESS 
		);
	}

    /********************************************************************/
    /*                                                                  */
    /*   Memory deallocation.                                           */
    /*                                                                  */
    /*   We make sure the memory is allocated and that the guard        */
    /*   words have not been damanged.  If so we reset the contents     */
    /*   of the allocation and delete the allocation.                   */
    /*                                                                  */
    /********************************************************************/

bool ROCKALL_DEBUG_FRONT_END::Delete( void *Address,int Size )
    {
	//
	//   A well known practice is to try to delete
	//   a null pointer.  This is really a very poor  
	//   style but we support it in any case.
	//   
	if ( Address != ((void*) AllocationFailure) )
		{
		//
		//   Delete the user information by writing 
		//   guard words over the allocation.  This
		//   should cause the application to crash
		//   if the area is read and also allows us 
		//   to check to see if it is written later.
		//
		DeleteGuard( Address );

		return true;
		}
	else
		{ return false; }
	}

    /********************************************************************/
    /*                                                                  */
    /*   Delete all allocations.                                        */
    /*                                                                  */
    /*   We check to make sure the heap is not corrupt and force        */
    /*   the return of all heap space back to the operating system.     */
    /*                                                                  */
    /********************************************************************/

void ROCKALL_DEBUG_FRONT_END::DeleteAll( bool Recycle )
    {
	AUTO bool Active;
	AUTO void *Address = NULL;
	AUTO int Space;

	//
	//   Walk the heap to verify all the allocations
	//   so that we know that the heap is undamaged.
	//
	while ( WalkGuard( & Active,& Address,& Space ) );

	//
	//   Delete the heap and force all the allocated
	//   memory to be returned to the operating system
	//   regardless of what the user requested.  Any
	//   attempt to access the deallocated memory will 
	//   be trapped by the operating system.
	//
	ROCKALL_FRONT_END::DeleteAll( (Recycle && false) );
	}

    /********************************************************************/
    /*                                                                  */
    /*   Memory allocation details.                                     */
    /*                                                                  */
    /*   Extract information about a memory allocation and just for     */
    /*   good measure check the guard words at the same time.           */
    /*                                                                  */
    /********************************************************************/

bool ROCKALL_DEBUG_FRONT_END::Details( void *Address,int *Space )
	{ return Verify( Address,Space ); }

    /********************************************************************/
    /*                                                                  */
    /*   Exception processing.                                          */
    /*                                                                  */
    /*   Although it is very hard to make Rockall crash it is           */
    /*   technically possible.  When (or should I say if) this          */
    /*   we call the following function (which may be overloadded).     */
    /*                                                                  */
    /********************************************************************/

void ROCKALL_DEBUG_FRONT_END::Exception(CONST char *Message )
	{ 
	DebugPrint
		( 
		"EXCEPTION CAUGHT: %s\n" 
		"ROCKALL TOTAL HEAP FAILURE: You have toasted the heap - Wow !!!!\n",
		Message
		); 
	}

    /********************************************************************/
    /*                                                                  */
    /*   Multiple memory deallocations.                                 */
    /*                                                                  */
    /*   We make sure all the memory is allocated and that the guard    */
    /*   words have not been damaged.  If so we reset the contents      */
    /*   of the allocations and then delete the allocations.            */
    /*                                                                  */
    /********************************************************************/

bool ROCKALL_DEBUG_FRONT_END::MultipleDelete
		( 
		int							  Actual,
		void						  *Array[],
		int							  Size
		)
    {
	REGISTER bool Result = true;
	REGISTER SBIT32 Count;

	//
	//   We would realy like to use the multiple
	//   delete functionality of Rockall here but 
	//   it is too much effort.  So we simply call
	//   the standard debug delete on each entry
	//   in the array.  Although this is not as
	//   fast it does give more transparent results.
	//
	for ( Count=0;Count < Actual;Count ++ )
		{
		//
		//   Delete each memory allocation after
		//   carefully checking it.
		//
		if ( ! Delete( Array[ Count ],Size ) )
			{ Result = false; }
		}

	return Result;
	}

    /********************************************************************/
    /*                                                                  */
    /*   Multiple memory allocations.                                   */
    /*                                                                  */
    /*   Allocate a collection of memory elements and setup the         */
    /*   guard information so we can check they have not been           */
    /*   damaged later.                                                 */
    /*                                                                  */
    /********************************************************************/

bool ROCKALL_DEBUG_FRONT_END::MultipleNew
		( 
		int							  *Actual,
		void						  *Array[],
		int							  Requested,
		int							  Size,
		int							  *Space,
		bool						  Zero
		)
    {
	//
	//   We would realy like to use the multiple
	//   new functionality of Rockall here but 
	//   it is too much effort.  So we simply call
	//   the standard debug new on each entry
	//   in the array.  Although this is not as
	//   fast it does give more transparent results.
	//
	for ( (*Actual)=0;(*Actual) < Requested;(*Actual) ++ )
		{
		REGISTER void *Current = New( Size,Space,Zero );

		//
		//   We add each sucessful memory allocation to
		//   into the array.
		//
		if ( Current != ((void*) AllocationFailure) )
			{ Array[ (*Actual) ] = Current; }
		else
			{ break; }
		}

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

    /********************************************************************/
    /*                                                                  */
    /*   Memory allocation.                                             */
    /*                                                                  */
    /*   We add some space on to the original allocation size for       */
    /*   various information and then call the allocator.  We then      */
    /*   set the guard words so we can check for overruns.              */
    /*                                                                  */
    /********************************************************************/

void *ROCKALL_DEBUG_FRONT_END::New( int Size,int *Space,bool Zero )
    {
	AUTO void *Address = ((void*) AllocationFailure);

	//
	//   The size must be greater than or equal to zero.  
	//   We do not know how to allocate a negative amount
	//   of memory.
	//
	if ( Size >= 0 )
		{
		//
		//   We need to allocate some space plus an extra
		//   bit for the guard words so we can detect any
		//   corruption later.
		//
		if ( NewGuard( & Address,Size,Space ) ) 
			{
			//
			//   Zero the allocation if requested.  We do
			//   this based on whether we are returning the
			//   space information.  If not we only zero 
			//   size requested.  Otherwise we have to zero 
			//   the entire area.
			//
			if ( Zero )
				{ 
				ZEROMEMORY
					( 
					Address,
					((Space == NULL) ? Size : (*Space)) 
					); 
				} 
			}
		}
	else
		{ UserError( Address,NULL,"Allocation size can not be negative" ); }

	return Address;
	}

    /********************************************************************/
    /*                                                                  */
    /*   Memory reallocation.                                           */
    /*                                                                  */
    /*   We need to resize an allocation.  We ensure the original       */
    /*   allocation was undamaged and then expand it.  We also          */
    /*   update the guard words to reflect the changes.                 */
    /*                                                                  */
    /********************************************************************/

void *ROCKALL_DEBUG_FRONT_END::Resize
		( 
		void						  *Address,
		int							  NewSize,
		int							  Move,
		int							  *Space,
		bool						  NoDelete,
		bool						  Zero
		)
    {
	REGISTER void *NewAddress = ((void*) AllocationFailure);

	//
	//   A well known practice is to try to resize a null
	//   pointer.  This is really a very poor style but we 
	//   support it in any case.
	//   
	if ( Address != ((void*) AllocationFailure) )
		{
		//
		//   The new size must be greater than or equal to  
		//   zero.  We do not know how to allocate a negative 
		//   amount of memory.
		//
		if ( NewSize >= 0 )
			{
			AUTO int ActualSize;

			//
			//   Ask for the details of the allocation.  This 
			//   will fail if the memory is not allocated.
			//
			if ( VerifyGuard( Address,& ActualSize,Space ) )
				{
				//
				//   We always move an allocation if we are
				//   allowed to as this has the best chance
				//   of shaking out various types of bugs.
				//
				if ( Move != 0 )
					{
					//
					//   We need to make sure we were able 
					//   to allocate the new memory otherwise 
					//   the copy will fail.
					//
					if ( NewGuard( & NewAddress,NewSize,Space ) )
						{
						REGISTER SBIT32 Smallest = 
							((ActualSize < NewSize) ? ActualSize : NewSize);
						REGISTER SBIT32 Largest = 
							(((Space == NULL)) ? NewSize : (*Space));

						//
						//   Copy the contents of the old allocation 
						//   to the new allocation.
						//
						MEMCPY
							( 
							((void*) NewAddress),
							((void*) Address),
							((int) Smallest) 
							);

						//
						//   Zero the allocation if requested.  We do
						//   this based on whether we are returning the
						//   space information.  If not we only zero 
						//   size requested.  Otherwise we have to zero 
						//   the entire area.
						//
						if ( Zero )
							{ 
							ZEROMEMORY
								( 
								(((char*) NewAddress) + Smallest),
								(Largest - Smallest) 
								); 
							} 

						//
						//   Delete the existing memory allocation
						//   and clean up.
						//
						DeleteGuard( Address );
						}
					}
				}
			else
				{ UserError( Address,NULL,"Resize on unallocated address" ); }
			}
		else
			{ UserError( Address,NULL,"Allocation size must be positive" ); }
		}
	else
		{ NewAddress = New( NewSize,Space,Zero ); }

	return NewAddress;
	}

    /********************************************************************/
    /*                                                                  */
    /*   Verify memory allocation details.                              */
    /*                                                                  */
    /*   Extract information about a memory allocation and just for     */
    /*   good measure check the guard words at the same time.           */
    /*                                                                  */
    /********************************************************************/

bool ROCKALL_DEBUG_FRONT_END::Verify( void *Address,int *Space )
    {
	AUTO int Size;

	//
	//   Verify that the supplied address is an area
	//   of allocated memory.  If not just exit as this
	//   is only a request for information.
	//
	return VerifyGuard( Address,& Size,Space );
	}

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

bool ROCKALL_DEBUG_FRONT_END::Walk( bool *Active,void **Address,int *Space )
	{ 
	//
	//   Walk the heap. 
	//
	return WalkGuard( Active,Address,Space );
	}

    /********************************************************************/
    /*                                                                  */
    /*   Class destructor.                                              */
    /*                                                                  */
    /*   Destory the current instance of the class.                     */
    /*                                                                  */
    /********************************************************************/

ROCKALL_DEBUG_FRONT_END::~ROCKALL_DEBUG_FRONT_END( void )
	{ /* void */ }
