/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 Copyright (c) 1989 Microsoft Corporation

 Module Name:
	
	ptrana.cxx

 Abstract:

 	Contains implementations of analysis routines for pointer types.

 Notes:


 History:

 	Oct-10-1993		VibhasC		Created

 ----------------------------------------------------------------------------*/

/****************************************************************************
 *	include files
 ***************************************************************************/
#include "allana.hxx"
#pragma hdrstop
/****************************************************************************
 *	local definitions
 ***************************************************************************/
/****************************************************************************
 *	local data
 ***************************************************************************/

/****************************************************************************
 *	externs
 ***************************************************************************/
/****************************************************************************/

CG_STATUS
CG_POINTER::UnMarshallAnalysis(
	ANALYSIS_INFO	*	pAna )
	{
	CG_STATUS	Status;

	// Unmarshall the pointer body first. If the pointer needs to be deferred,
	// then dont perform the pointee unmarshall analysis. Just indicate that
	// there is a pointee that needs to be unmarshalled later.

	Status = PtrUnMarshallAnalysis( pAna );

	if( pAna->IsPointeeDeferred() )
		{
		pAna->SetHasAtLeastOneDeferredPointee();
		}
	else
		Status	= PteUnMarshallAnalysis( pAna );

	return Status;
	}

CG_STATUS
CG_POINTER::PtrUnMarshallAnalysis(
	ANALYSIS_INFO	*	pAna )
	{
	RPC_BUF_SIZE_PROPERTY		BSizeProp		= BSIZE_FIXED;
	RPC_BUFFER_SIZE				BufIncr			= 0;
	unsigned short				EProp			= E_SIZING_POSSIBLE |
												  E_UNMARSHALL_POSSIBLE;
	long						MarshallWeight	= 0;
	ALIGNMENT_PROPERTY			CurAl			= pAna->GetCurAlignmentState();
	unsigned short				CurEmbedLevel	= pAna->GetCurrentEmbeddingLevel();
	BOOL						fTopLevelPtr	= (CurEmbedLevel == 0);

	// Perform the analysis for the pointer body. For a [ref] pointer, nothing
	// needs to be done.
	
	if( IsRef() && !pAna->IsMemoryAllocDone() )
		{
		SetUAction( RecommendUAction( pAna->GetCurrentSide(),
								 	  pAna->IsMemoryAllocDone(),	
								 	  pAna->IsRefAllocDone(),
								 	  FALSE,		// no buffer re-use for ref.
								 	  UAFLAGS_NONE
							   	 	));

		}
	else  // unique or ptr.
		{

		// Bump the marshalling pointer by the alignment of the pointer itself.

		CurAl	= pAna->Advance( GetWireAlignment(),
								 (STM_ACTION *)0,
								 &BSizeProp,
								 &BufIncr
							   );

		}

	pAna->SetEngineProperty( EProp );
	pAna->AddMarshallWeight( MarshallWeight );
	pAna->SetRpcBufSizeProperty( BSizeProp );
	pAna->SetCurAlignmentState( CurAl );

	return CG_OK;
	}

CG_STATUS
CG_POINTER::PteUnMarshallAnalysis(
	ANALYSIS_INFO	*	pAna )
	{
	CG_STATUS	Status;
	pAna->PushIndirectionLevel();

	Status = CorePteUnMarshallAnalysis( pAna );

	pAna->PopIndirectionLevel();

	pAna->IncrRpcBufferSize(pAna->Position(
								pAna->GetNextWireAlignment(),
								(STM_ACTION*)0 )
						   );

	return Status;
	}

CG_STATUS
CG_POINTER::MarshallAnalysis(
	ANALYSIS_INFO	*	pAna )
	{
	CG_STATUS	Status;
	short		EmbedLevel;

	// Marshall the pointer body first. If the pointee needs to be deferred,
	// dont perform marshall analysis on the pointee. Just indicate that a
	// pointee needs to be marshalled later.

	Status = PtrMarshallAnalysis( pAna );

	if( pAna->IsPointeeDeferred() )
		{
		pAna->SetHasAtLeastOneDeferredPointee();
		}
	else
		{
		EmbedLevel = pAna->GetCurrentEmbeddingLevel();
		pAna->ResetEmbeddingLevel();
		Status	= PteMarshallAnalysis( pAna );
		pAna->SetCurrentEmbeddingLevel( EmbedLevel );
		}

	return Status;
	}

CG_STATUS
CG_POINTER::PtrMarshallAnalysis(
	ANALYSIS_INFO	*	pAna )
	{
	ALIGNMENT_PROPERTY		CurAl		= pAna->GetCurAlignmentState();
	RPC_BUFFER_SIZE			BufIncr		= 0;
	RPC_BUF_SIZE_PROPERTY	BSizeProp	= BSIZE_FIXED;
	unsigned short			EProp		=
									 E_SIZING_POSSIBLE | E_MARSHALL_POSSIBLE;
	unsigned short			MarshallWeight= IsRef() ? 0 :
											IsUnique() ? MW_UNIQUE_PTR :
											MW_FULL_PTR;

	if( !IsRef() )
		{
		CurAl	= pAna->Advance( AL_4,
							 	(STM_ACTION *)0,
							 	&BSizeProp,
							 	&BufIncr
						   	);

		pAna->IncrRpcBufferSize( BufIncr + 4 );
		}

	pAna->SetCurAlignmentState( CurAl );
	pAna->SetRpcBufSizeProperty( BSizeProp );
	pAna->SetEngineProperty( EProp );
	pAna->AddMarshallWeight( MarshallWeight );

	// Set the alignment of the machine to be the next expected alignment
	// After the pointer or pointee is unmarshalled, the next alignment state
	// adjusted to.

	return CG_OK;
	}

CG_STATUS
CG_POINTER::PteMarshallAnalysis(
	ANALYSIS_INFO	*	pAna )
	{
	CG_STATUS			Status;

	pAna->PushIndirectionLevel();

	pAna->SetMemoryAllocDone();	// Needed ?
	Status = CorePteMarshallAnalysis( pAna );

	pAna->PopIndirectionLevel();

	return Status;
	}

CG_STATUS
CG_POINTER::FollowerMarshallAnalysis(
	ANALYSIS_INFO * pAna )
	{
	return PteMarshallAnalysis( pAna );
	}

CG_STATUS
CG_POINTER::FollowerUnMarshallAnalysis(
	ANALYSIS_INFO * pAna )
	{
	return PteUnMarshallAnalysis( pAna );
	}

CG_STATUS
CG_POINTER::S_OutLocalAnalysis(
	ANALYSIS_INFO	*	pAna )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

 	Perform analysis for [out] params that need to be allocated as locals
 	on server stub.

 Arguments:

 	pAna	- The analysis block.
	
 Return Value:
	
	CG_OK	if all is well
	error	otherwise.

 Notes:

----------------------------------------------------------------------------*/
{
	CG_STATUS		Status	= CG_OK;
	CG_NDR		*	pC		= (CG_NDR *)GetNonGenericHandleChild();

	if( !pAna->IsMemoryAllocDone() )
		{
		if( pAna->GetCurrentSide() != C_SIDE )
			{
			PNAME	pName	= pAna->GenTempResourceName( 0 );
			SetResource( pAna->AddLocalResource( pName,
									 	 	MakeIDNode( pName, GetType() )
								   	   	));
			}
		SetAllocatedOnStack( 1 );
		}

	// If it is a ref pointer, chase it further.

	if( IsRef() && !IsQualifiedPointer() )
		{
		pAna->ResetMemoryAllocDone();
		pAna->SetRefAllocDone();
		Status = pC->S_OutLocalAnalysis( pAna );
		}

	// If this is the server side, and the pointee is allocated on stack,
	// then dont free the pointee, else free it.

	if( pC->IsAllocatedOnStack() )
		{
		SetPointerShouldFree( 0 );
		}
	return Status;
}
CG_STATUS
CG_POINTER::InLocalAnalysis(
	ANALYSIS_INFO	*	pAna )
	{
	if( IsRef() )
		{
		((CG_NDR *)GetChild())->InLocalAnalysis( pAna );
		}

	return CG_OK;
	}

/****************************************************************************
 *	CG_QUALIFIED_POINTER 
 ***************************************************************************/

CG_STATUS
CG_QUALIFIED_POINTER::CorePteMarshallAnalysis(
	ANALYSIS_INFO	*	pAna )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

 	Perform the core pointee marshall analysis for a conformant string

 Arguments:
	
	pAna	- The analysis block pointer.

 Return Value:

 	CG_OK	if all is well, error otherwise.

 Notes:

	The ndr for a conformant string (eg typedef [string] char *p ) is:
		- MaxCount
		- Offset from start of the valid character
		- Actual Count.

	We need to declare a local variable which will hold the length of the
	string, so that the length can be used in the actual marshall for memcopy.
----------------------------------------------------------------------------*/
{
	node_skl			*	pType;
	ALIGNMENT_PROPERTY		Al;
	RPC_BUFFER_SIZE			BufIncr	= 0;
	PNAME					pResName;
	BOOL					fNeedsCount;
	BOOL					fNeedsFirstAndLength;
	RPC_BUFFER_SIZE			TotalHdrSize = 0;


	if( pAna->IsArrayContext() )
		{
		SetUsedInArray();
		}

	if( fNeedsCount = NeedsMaxCountMarshall() )
		{
		if( !GetSizeResource() )
			{
			GetBaseTypeNode( &pType, SIGN_UNSIGNED, SIZE_LONG, TYPE_INT );
			pResName	= pAna->GenTempResourceName(0);
			pType		= MakeIDNode( pResName, pType );
			SetSizeResource( pAna->AddTransientResource( pResName, pType ) );
			}
		else
			{
			pAna->AddTransientResource( GetSizeResource()->GetResourceName(),
										GetSizeResource()->GetType()
									  );
			}
		TotalHdrSize += 4;
		}

	if( fNeedsFirstAndLength = NeedsFirstAndLengthMarshall() )
		{
		if( !GetLengthResource() )
			{
			GetBaseTypeNode( &pType, SIGN_UNSIGNED, SIZE_LONG, TYPE_INT );
			pResName = pAna->GenTempResourceName(0);
			pType		= MakeIDNode( pResName, pType );
			SetLengthResource( pAna->AddTransientResource( pResName, pType ) );
			}
		else
			{
			pAna->AddTransientResource( GetLengthResource()->GetResourceName(),
										GetLengthResource()->GetType()
									  );
			}

		if( NeedsExplicitFirst() )
			{
			if( !GetFirstResource() )
				{
				GetBaseTypeNode( &pType, SIGN_UNSIGNED, SIZE_LONG, TYPE_INT );
				pResName = pAna->GenTempResourceName(0);
				pType	 = MakeIDNode( pResName, pType );
				SetFirstResource( pAna->AddTransientResource( pResName, pType ) );
				}
			else
				{
			  pAna->AddTransientResource(GetLengthResource()->GetResourceName(),
										 GetLengthResource()->GetType()
									  	);
				}
			}
		TotalHdrSize += (4 + 4);
		}
	
	// Adjust alignment for the first long. The size is the alignment 
	// increment, and 4 longs. The buffer size property is unknown since
	// the length of the string is unknown. Therefore the sizing code will
	// have to be generated for this.

	if( fNeedsCount )
		pAna->Advance( AL_4, 0, 0, 0 );

	if( fNeedsFirstAndLength )
		{
		pAna->Advance( AL_4, 0, 0, 0 );
		pAna->Advance( AL_4, 0, 0, 0 );
		}

	// Depending upon the type of the string, figure out the worst case
	// alignment after the string is marshalled.

	Al = ((CG_NDR *)GetChild())->GetWireAlignment();

	pAna->Advance( Al,
				   (STM_ACTION *)0,
				   (RPC_BUF_SIZE_PROPERTY *)0,
				   &BufIncr
				 );

	pAna->SetCurAlignmentState( MAKE_WC_ALIGNMENT( Al ) );

	pAna->SetRpcBufSizeProperty( BSIZE_UNKNOWN );
	pAna->IncrRpcBufferSize( BufIncr + TotalHdrSize );
	pAna->SetEngineProperty( E_SIZING_POSSIBLE | E_MARSHALL_POSSIBLE );
	pAna->AddMarshallWeight( MW_CONFORMANT_STRING );

	return CG_OK;
}

CG_STATUS
CG_QUALIFIED_POINTER::CorePteUnMarshallAnalysis(
	ANALYSIS_INFO	*	pAna )
	{
	RPC_BUFFER_SIZE		BufIncr	= 0;
	ALIGNMENT_PROPERTY	Al;
	node_skl		*	pType;
	PNAME				pResName;
	BOOL				fNeedsMaxCount;
	BOOL				fNeedsFirstAndLength;


	// If a resource for the length has not already been allocated, allocate
	// one.

	if( fNeedsMaxCount = NeedsMaxCountMarshall() )
		{
		if( !GetSizeResource() )
			{
			GetBaseTypeNode( &pType, SIGN_UNSIGNED, SIZE_LONG, TYPE_INT );
			pResName	= pAna->GenTempResourceName(0);
			pType		= MakeIDNode( pResName, pType );

			SetSizeResource( pAna->AddTransientResource( pResName, pType ) );
			}
		else
			pAna->AddTransientResource( GetSizeResource()->GetResourceName(),
										GetSizeResource()->GetType()
									  );
		pAna->Advance( AL_4, 0, 0, 0 );
		}

	if( fNeedsFirstAndLength = NeedsFirstAndLengthMarshall() )
		{
		if( !GetLengthResource() )
			{
			GetBaseTypeNode( &pType, SIGN_UNSIGNED, SIZE_LONG, TYPE_INT );
			pResName	= pAna->GenTempResourceName(0);
			pType		= MakeIDNode( pResName, pType );

			SetLengthResource( pAna->AddTransientResource( pResName, pType ) );
			}
		else
			pAna->AddTransientResource( GetLengthResource()->GetResourceName(),
										GetLengthResource()->GetType()
									  );

		if( NeedsExplicitFirst() )
			{
			if( !GetFirstResource() )
				{
				GetBaseTypeNode( &pType, SIGN_UNSIGNED, SIZE_LONG, TYPE_INT );
				pResName	= pAna->GenTempResourceName(0);
				pType		= MakeIDNode( pResName, pType );

				SetFirstResource( pAna->AddTransientResource( pResName, pType ) );
				}
			else
			  pAna->AddTransientResource(GetFirstResource()->GetResourceName(),
										 GetFirstResource()->GetType()
									  	);
			}
		pAna->Advance( AL_4, 0, 0, 0 );
		pAna->Advance( AL_4, 0, 0, 0 );
		}


	// Depending upon the type of the pointee, figure out the worst case
	// alignment after the string is marshalled.

	Al = ((CG_NDR *)GetChild())->GetWireAlignment();

	pAna->Advance( Al,
				   (STM_ACTION *)0,
				   (RPC_BUF_SIZE_PROPERTY * )0,
				   &BufIncr
				 );

	// Set up the rest of the properties.

	pAna->SetCurAlignmentState( MAKE_WC_ALIGNMENT( Al ) );
	pAna->SetRpcBufSizeProperty( BSIZE_UNKNOWN );
	pAna->SetEngineProperty( E_SIZING_POSSIBLE | E_MARSHALL_POSSIBLE );
	pAna->AddMarshallWeight( MW_CONFORMANT_STRING );

	return CG_OK;
	}

CG_STATUS
CG_INTERFACE_POINTER::S_OutLocalAnalysis(
    ANALYSIS_INFO   *   pAna )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

	Perform analysis for out params, allocated as locals on the server side.

 Arguments:
	
	pAna	- A pointer to the analysis block.

 Return Value:
	
	CG_OK if all is well
	error otherwise.

 Notes:

----------------------------------------------------------------------------*/
{
    if( pAna->IsRefAllocDone() )
        {
        if( pAna->GetCurrentSide() != C_SIDE )
            {
            PNAME       pName = pAna->GenTempResourceName( 0 );
            node_skl *  pType = GetType();

            node_skl *  pActualType;

            if ( pType->NodeKind() == NODE_DEF )
                pActualType = MakeIDNode( pName, pType );
            else
                pActualType = MakePtrIDNode( pName, pType );

            SetResource( pAna->AddLocalResource( pName,
                                                 pActualType ));
            }

        SetAllocatedOnStack( 1 );
        }
    return CG_OK;
}

CG_STATUS
CG_INTERFACE_POINTER::MarshallAnalysis(
    ANALYSIS_INFO   *   pAna )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

    Perform marshall analysis for interface ptr.

 Arguments:

    pCCB    - The code gen controller block.

 Return Value:

	CG_OK
	
 Notes:

----------------------------------------------------------------------------*/
{
    pAna->SetRpcBufSizeProperty( BSIZE_UNKNOWN );
    return CG_OK;
}

/****************************************************************************
 *	utility functions
 ***************************************************************************/
U_ACTION
CG_POINTER::RecommendUAction(
	SIDE		CurrentSide,
	BOOL		fMemoryAllocated,
	BOOL		fRefAllocated,
	BOOL		fBufferReUsePossible,
	UAFLAGS		AdditionalFlags )
{
	U_ACTION	UAction =  CG_NDR::RecommendUAction( CurrentSide,
									 					fMemoryAllocated,
									 					fRefAllocated,
									 					fBufferReUsePossible,
									 					AdditionalFlags );
	return UAction;

}

#define a0	ADD_0
#define a1	ADD_1
#define a2	ADD_2
#define a3	ADD_3
#define a4	ADD_4
#define a5	ADD_5
#define a6	ADD_6
#define a7	ADD_7

#define f2	FAL_2
#define f4	FAL_4
#define f8	FAL_8


// Action mask specifying what to do within if, within else and outside the if


#define am(i,e,o)	((((i << 4) | e ) << 4) | o)

#define GET_WITHIN_IF( a ) 		((a >> 8) & 0xf)
#define GET_WITHIN_ELSE( a )	((a >> 4) & 0xf)
#define GET_OUTSIDE_IF( a )		(a & 0xf)

// NOTE:: the 6th row and column entry here (index 5) is a dummy entry

unsigned short AdjustArray[11][11] = 
{
   {
   am(a0,a0,a0),am(a1,a0,a0),am(a2,a0,a0),am(a3,a0,a0),am(a4,a0,a0),am(a0,a0,a0)
  ,am(a6,a0,a0),am(a7,a0,a0),am(f2,a0,a0),am(f4,a0,a0),am(f8,a0,a0)
   }

 , {
   am(a0,a1,a0),am(a0,a0,a1),am(a1,a0,a1),am(a2,a0,a1),am(a3,a0,a2),am(a0,a0,a0)
  ,am(a5,a0,a1),am(a6,a0,a1),am(f2,a1,a0),am(f4,a1,a0),am(f8,a1,a0)
   }

 , {
   am(a0,a2,a0),am(a1,a2,a0),am(a0,a0,a2),am(a1,a0,a2),am(a2,a0,a2),am(a0,a0,a0)
  ,am(a4,a0,a2),am(a5,a0,a2),am(f2,a2,a0),am(f4,a2,a0),am(f8,a2,a0)
   }

 , {
   am(a0,a3,a0),am(a1,a3,a0),am(a2,a3,a0),am(a0,a0,a3),am(a1,a0,a3),am(a0,a0,a0)
  ,am(a3,a0,a3),am(a4,a0,a3),am(f2,a3,a0),am(f4,a3,a0),am(f8,a3,a0)
   }

 , {
   am(a0,a4,a0),am(a1,a4,a0),am(a2,a4,a0),am(a3,a4,a0),am(a0,a0,a4),am(a0,a0,a0)
  ,am(a2,a0,a4),am(a3,a0,a4),am(f2,a4,a0),am(f4,a4,a0),am(f8,a4,a0)
   }

 , {
   am(a0,a0,a0),am(a0,a0,a0),am(a0,a0,a0),am(a0,a0,a0),am(a0,a0,a0),am(a0,a0,a0)
  ,am(a0,a0,a0),am(a0,a0,a0),am(a0,a0,a0),am(a0,a0,a0),am(a0,a0,a0)
   }

 , {
   am(a0,a6,a0),am(a1,a6,a0),am(a2,a6,a0),am(a3,a6,a0),am(a4,a6,a0),am(a0,a0,a0)
  ,am(a0,a0,a6),am(a1,a0,a6),am(f2,a6,a0),am(f4,a6,a0),am(f8,a6,a0)
   }
 
 , {
   am(a0,a7,a0),am(a1,a7,a0),am(a2,a7,a0),am(a3,a7,a0),am(a4,a7,a0),am(a0,a0,a0)
  ,am(a6,a7,a0),am(a0,a0,a7),am(f2,a7,a0),am(f4,a7,a0),am(f8,a7,a0)
   }
 
 , {
   am(a0,f2,a0),am(a1,f2,a0),am(a2,f2,a0),am(a2,f2,a0),am(a4,f2,a0),am(a0,a0,a0)
  ,am(a6,f2,a0),am(a7,f2,a0),am(a0,a0,f2),am(f4,f2,a0),am(f8,f2,a0)
   }
 
 , {
   am(a0,f4,a0),am(a1,f4,a0),am(a2,f4,a0),am(a3,f4,a0),am(a4,f4,a0),am(a0,a0,a0)
  ,am(a6,f4,a0),am(a7,f4,a0),am(f2,f4,a0),am(a0,a0,f4),am(f8,f4,a0)
   }
 
 , {
   am(a0,f8,a0),am(a2,f8,a0),am(a2,f8,a0),am(a3,f8,a0),am(a4,f8,a0),am(a0,a0,a0)
  ,am(a6,f8,a0),am(a7,f8,a0),am(f2,f8,a0),am(f4,f8,a0),am(a0,a0,f8)
   }
};

unsigned short FAXlat[] = 
	{ 
	 5 // dummy
	,7 // FAL_2
	,8 // FAL_4
	,9 // FAL_8
	};

void
CG_POINTER::RecommendAlignmentAdjustment(
	ALIGNMENT_PROPERTY	AlBeforePteMarshall,
	ALIGNMENT_PROPERTY	AlAfterPteMarshall,
	ALIGNMENT_PROPERTY	TargetAlignment,
	STM_ACTION	*		ActionInIfClause,
	STM_ACTION	*		ActionInElseClause,
	STM_ACTION	*		ActionOutsideIfClause )
{
	ALSTMC				Al;
	STM_ACTION			ActionIfNoPteMarshall;
	STM_ACTION			ActionAfterPteMarshall;
	unsigned short		Index1,Index2;
	unsigned short		Entry;


	Al.SetCurrentState( AlBeforePteMarshall );

	Al.Position( TargetAlignment, &ActionIfNoPteMarshall );
	
	Al.SetCurrentState( AlAfterPteMarshall );

	Al.Position( TargetAlignment, &ActionAfterPteMarshall );
	

	if( IS_FORCED_ALIGNMENT_ACTION( ActionIfNoPteMarshall ) )
		Index1	= FAXlat[ (ActionIfNoPteMarshall & ~FAL_MASK ) ];
	else
		Index1	= ActionIfNoPteMarshall;

	if( IS_FORCED_ALIGNMENT_ACTION( ActionAfterPteMarshall ) )
		Index2	= FAXlat[ (ActionAfterPteMarshall & ~FAL_MASK ) ];
	else
		Index2	= ActionAfterPteMarshall;

	Entry	= AdjustArray[ Index1 ][ Index2 ];

	*ActionInIfClause		= GET_WITHIN_IF( Entry );
	*ActionInElseClause		= GET_WITHIN_ELSE( Entry );
	*ActionOutsideIfClause	= GET_OUTSIDE_IF( Entry );
}