/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 Copyright (c) 1993-2000 Microsoft Corporation

 Module Name:

    ptrndr.hxx

 Abstract:

    Contains routines for the generation of the new NDR format strings for
    pointer types, and the new NDR marshalling and unmarshalling calls.

 Notes:


 History:

    DKays     Oct-1993     Created.
 ----------------------------------------------------------------------------*/

#include "becls.hxx"
#pragma hdrstop

static long StringToHex( char * str );
extern CMD_ARG          *   pCommand;
#define OUT_CORRELATION_DESC( x, y )    (x)->PushCorrelationFlagsShort(y)

BOOL
CG_POINTER::IsPointerToBaseType()
{
    BOOL fIsPointerToBaseType = FALSE;
    CG_NDR * pChild;

    if(GetCGID() == ID_CG_PTR)
        {
        pChild = (CG_NDR *)GetChild();

        if ( pChild->GetCGID() == ID_CG_GENERIC_HDL )
            pChild = (CG_NDR *)pChild->GetChild();

        if ( pChild->IsSimpleType() )
            fIsPointerToBaseType = TRUE;
        }

    return fIsPointerToBaseType;
}

BOOL
CG_POINTER::IsPointerToPointer()
{
    BOOL fIsPointerToPointer = FALSE;
    CG_NDR * pChild;

    if(GetCGID() == ID_CG_PTR)
        {
        pChild = (CG_NDR *)GetChild();

        if ( pChild->GetCGID() == ID_CG_GENERIC_HDL )
            pChild = (CG_NDR *)pChild->GetChild();

        if (pChild->IsPointer())
            fIsPointerToPointer = TRUE;
        }

    return fIsPointerToPointer;
}

BOOL
CG_POINTER::IsBasicRefPointer()
{
    short   Attributes;

    if ( ( IsInterfacePointer() ) ||
         (GetCGID() == ID_CG_BC_PTR) ||
         (GetCGID() == ID_CG_STRUCT_STRING_PTR) ||
         (GetPtrType() != PTR_REF) ||
         IsPointerToBaseType() )
        return FALSE;

    if ( ((GetCGID() == ID_CG_STRING_PTR) ||
          (GetCGID() == ID_CG_SIZE_STRING_PTR)) &&
         ((CG_STRING_POINTER *)this)->IsStringableStruct() )
        return FALSE;

    Attributes = GetAllocateDetails();

    if ( IsPointerToPointer() ||
         IS_ALLOCATE(Attributes, ALLOCATE_ALL_NODES) ||
         IS_ALLOCATE(Attributes, ALLOCATE_DONT_FREE) )
        return FALSE;

    return TRUE;
}

BOOL
CG_POINTER::IsMultiSize()
/*
    Count the sized pointers below a pointer.
    At least 2 are needed to come up with TRUE.
       size_is(a) and size_is(,b) should give false
       size_is(a,b) and size_is(,a,b) should give true
*/
{
    return (SizedDimensions() > 1);
}

long
CG_POINTER::SizedDimensions()
{
    CG_NDR *    pNdr;
    long        Dim;
    bool        SeenOne = false;

    // Accept (non-sized) pointers before a sized pointer but not after.
    // Checking for pointer breaks a recursion as well.

    Dim = 0;
    pNdr = this;

    while ( pNdr  &&  pNdr->IsPointer() )
        {
        if ( (pNdr->GetCGID() == ID_CG_SIZE_PTR) ||
             (pNdr->GetCGID() == ID_CG_SIZE_LENGTH_PTR) ||
             (pNdr->GetCGID() == ID_CG_SIZE_STRING_PTR) )
            {
            Dim++;
            SeenOne = true;
            }
        else
            {
            if ( SeenOne )
                break;
            }
        pNdr = (CG_NDR *) pNdr->GetChild();
        }

    return Dim;
}

long
CG_POINTER::FixedBufferSize( CCB * pCCB )
{
    long    BufSize;

    //
    // Must give up on string or sized pointers.
    //
    if ( GetCGID() != ID_CG_PTR )
        return -1;

    BufSize = ((CG_NDR *)GetChild())->FixedBufferSize( pCCB );

    if ( BufSize == -1 )
        return -1;

    BufSize += ( MAX_WIRE_ALIGNMENT + SIZEOF_WIRE_PTR());

    return BufSize;
}

BOOL
CG_POINTER::InterpreterAllocatesOnStack(
    CCB *       pCCB,
    CG_PARAM *  pMyParam,
    long *      pAllocationSize )
{
    CG_NDR *    pNdr;
    long        OutSize;

    if ( ! pMyParam )
        return FALSE;

    pNdr = (CG_NDR *) GetChild();

    // 
    // [cs_tag] params that are omitted because of a tag routine can't be on
    // the allocated on the stack because they don't exist!
    //
    if ( pNdr->GetCGID() == ID_CG_CS_TAG )
        {
        CG_PROC *pProc = (CG_PROC *) pCCB->GetCGNodeContext();

        if ( pProc->GetCSTagRoutine() )
            {
            *pAllocationSize = 0;
            return FALSE;
            }
        }

    if ( pNdr->GetCGID() == ID_CG_GENERIC_HDL )
        pNdr = (CG_NDR *) pNdr->GetChild();

    if ( pNdr->GetCGID() == ID_CG_CONTEXT_HDL )
        {
        //
        // These get the "allocated on stack" attribute but a size of 0 since
        // they are allocated by calling NDRSContextUnmarshall.
        //
        *pAllocationSize = 0;
        return TRUE;
        }

    //
    // Make sure this pointer is a top level parameter and doesn't have
    // any allocate attributes.
    //
    if ( (pMyParam->GetCGID() == ID_CG_RETURN) ||
         (pMyParam->GetChild() != this) ||
         pMyParam->IsForceAllocate()    ||
         IS_ALLOCATE( GetAllocateDetails(), ALLOCATE_ALL_NODES ) ||
         IS_ALLOCATE( GetAllocateDetails(), ALLOCATE_DONT_FREE ) )
        return FALSE;

    OutSize = pCCB->GetInterpreterOutSize();

    //
    // Watch for [out] only ref to ref pointers as they should be
    // handled by NdrOutInit in the new interpreter, not on stack.
    //

    if ( ! pMyParam->IsParamIn()  &&  IsRef()  &&
         IsPointerToPointer()  &&  ((CG_POINTER *)pNdr)->IsRef() )
        return FALSE;

    //
    // Look for pointer to pointer, [out] only pointer to base type, or
    // pointer to enum16 of any direction.
    //
    if ( IsPointerToPointer() ||
         (IsPointerToBaseType() && ! pMyParam->IsParamIn()) ||
         (IsPointerToBaseType() &&
          (((CG_BASETYPE *)pNdr)->GetFormatChar() == FC_ENUM16) &&
          pMyParam->IsParamIn()) )
        {
        if ( (OutSize + 8) <= MAX_INTERPRETER_OUT_SIZE )
            {
            *pAllocationSize = 8;
            pCCB->SetInterpreterOutSize( pCCB->GetInterpreterOutSize() + 8 );
            return TRUE;
            }
        }

    //
    // Finished with [in], [in,out] cases now.
    //
    if ( pMyParam->IsParamIn() )
        return FALSE;

    //
    // This covers [out] pointers to structs and unions.  We don't allow
    // any one parameter to eat up too much of the total stack space
    // the interpreter has set aside for this optimization.
    //
    if ( pNdr->GetMemorySize() <= MAX_INTERPRETER_PARAM_OUT_SIZE )
        {
        OutSize += (pNdr->GetMemorySize() + 7) & ~0x7;

        if ( OutSize <= MAX_INTERPRETER_OUT_SIZE )
            {
            *pAllocationSize = (pNdr->GetMemorySize() + 7) & ~0x7;
            pCCB->SetInterpreterOutSize(
                        pCCB->GetInterpreterOutSize() + *pAllocationSize );
            return TRUE;
            }
        }

    return FALSE;
}

void
CG_POINTER::GetTypeAndFlags( CCB *pCCB, NDR64_POINTER_FORMAT *format )
{
    CG_PARAM *          pParam;
    short               Attributes;

    pParam = pCCB->GetCurrentParam();

    BOOL IsNDR64 = pCommand->IsNDR64Run();

    //
    // Set the pointer type.
    //
    switch ( GetPtrType() )
        {
        case PTR_REF :
            format->FormatCode = (NDR64_FORMAT_CHAR)(IsNDR64 ? FC64_RP : FC_RP);
            break;

        case PTR_UNIQUE :
            //
            // Check if this is a unique pointer in an OLE interface, but
            // is not the top most pointer.
            //
            if ( pCCB->IsInObjectInterface() &&
                 pParam->IsParamOut() &&
                 pParam->GetChild() != this )
                {
                format->FormatCode = (NDR64_FORMAT_CHAR)(IsNDR64 ? FC64_OP : FC_OP);
                }
            else
                {
                format->FormatCode = (NDR64_FORMAT_CHAR)(IsNDR64 ? FC64_UP : FC_UP);
                }

            break;

        case PTR_FULL :
            format->FormatCode = (NDR64_FORMAT_CHAR)(IsNDR64 ? FC64_FP : FC_FP);
            break;
        }

    //
    // Now the attributes.
    //
    format->Flags = 0;

    Attributes = GetAllocateDetails();

    if ( IS_ALLOCATE(Attributes, ALLOCATE_ALL_NODES) )
        format->Flags |= FC_ALLOCATE_ALL_NODES;
    
    if ( IS_ALLOCATE(Attributes, ALLOCATE_DONT_FREE) )
        format->Flags |= FC_DONT_FREE;

    if ( GetCGID() == ID_CG_PTR )
        {
        //
        // Check if we are allocated on the stack by the stub.  Currently this
        // only works for top level pointers.
        //
        if ( ! ShouldPointerFree() &&
             ! (pCCB->GetOptimOption() & OPTIMIZE_INTERPRETER) )
            {
            format->Flags |= FC_ALLOCED_ON_STACK;
            }

        //
        // For the interpreter we set the alloced on stack attribute for
        // those [out] pointers which we will be able to "allocate" on the
        // server interpreter's stack.  We also do this for [in,out] double
        // pointers.
        // The version 1 interpreter simply looks for pointers to context
        // handles.
        //
        if ( pCCB->GetOptimOption() & OPTIMIZE_INTERPRETER )
            {
            if ( ! (pCCB->GetOptimOption() & OPTIMIZE_INTERPRETER_V2) )
                {
                if ( ((CG_NDR *)GetChild())->GetCGID() == ID_CG_CONTEXT_HDL )
                    format->Flags |= FC_ALLOCED_ON_STACK;
                }
            else
                {
                long    OutSize;

                if ( InterpreterAllocatesOnStack( pCCB, pParam, &OutSize ) )
                    format->Flags |= FC_ALLOCED_ON_STACK;
                }
            }
        }

    //
    // Check for a pointer to simple type, non-sized string, or pointer to
    // pointer.
    //
    if ( IsPointerToBaseType() || (GetCGID() == ID_CG_STRING_PTR) )
        format->Flags |= FC_SIMPLE_POINTER;

    if ( IsPointerToPointer() )
        format->Flags |= FC_POINTER_DEREF;

    SetFormatAttr( format->Flags );
}


void
CG_POINTER::GenNdrPointerType( CCB * pCCB )
/*++

Routine Description :

    Generates the first two bytes of a pointer's format string description.

Arguments :

    pCCB        - pointer to the code control block.

Return:

    Returns FALSE if the format string for the pointer type has already been
    generated, otherwise returns TRUE.

 --*/
{
    NDR64_POINTER_FORMAT    format;

    GetTypeAndFlags( pCCB, &format );

    pCCB->GetFormatString()->PushPointerFormatChar( (unsigned char) format.FormatCode );
    pCCB->GetFormatString()->PushByte( format.Flags );
}

void
CG_POINTER::RegisterRecPointerForFixup( 
    CCB * pCCB,
    long  OffsetAt )
/*++
    Register a simple pointer for fixup.
    Don't register qualified pointers, byte pointers etc.
--*/
{
    // This routine should be called only for ID_CG_PTR pointers, but check
    // just in case.

    if ( GetCGID() == ID_CG_PTR )
        {
        pCCB->RegisterRecPointerForFixup( (CG_NDR *) GetChild(), OffsetAt );
        }
} 


void
CG_POINTER::GenNdrFormat( CCB * pCCB )
/*++

Routine Description :

    Generates the format string description for a pointer to anything.

Arguments :

    pCCB        - pointer to the code control block.

 --*/
{

    FORMAT_STRING *     pFormatString = pCCB->GetFormatString();
    long                StartOffset   = pFormatString->GetCurrentOffset();
    
    if ( GetFormatStringOffset() != -1 )
        return;

    SetFormatStringOffset( StartOffset );
    SetFormatStringEndOffset( StartOffset + 4 );

    if ( GenNdrFormatAlways( pCCB ) == 0 )
        {
        // Don't optimize out when the pointee hasn't been generated.
        return;
        }

    // here, we assume all pointers generate 4 bytes
    pFormatString->OptimizeFragment( this );

}

long
CG_POINTER::GenNdrFormatAlways( CCB * pCCB )
/*++

Routine Description :

    Generates the format string description for a pointer to anything.

Arguments :

    pCCB        - pointer to the code control block.

 --*/
{
    FORMAT_STRING *     pFormatString;
    long                Offset;

    pFormatString = pCCB->GetFormatString();

    SetFormatStringOffset( pFormatString->GetCurrentOffset() );

    GenNdrPointerType( pCCB );

    //
    // If it's an unattributed pointer to a simple type then the format
    // string is optimized.
    //
    if ( IsPointerToBaseType() )
        {
        GenNdrFormatPointee( pCCB );

        // True simple types are represented as 1 byte (e.g. FC_LONG) and thus
        // subsequent stuff needs to be aligned.  Ranges (which are also 
        // considered simpe types) get a more standard, already aligned
        // representation

        if ( pFormatString->GetCurrentOffset() & 1 )
            pFormatString->PushFormatChar( FC_PAD );

        // return something non-zero to mark it ok for optimizing out.
        return GetFormatStringOffset();
        }

    // Get the current offset.
    Offset = pFormatString->GetCurrentOffset();

    // Push a short for the offset to be filled in later.
    pFormatString->PushShortOffset( 0 );

    // Generate the pointee's format string.
    GenNdrFormatPointee( pCCB );

    // Now fill in the offset field correctly.  
    // Register for fixup if the offset isn't known yet. Note that we cannot
    // use RegisterComplexEmbeddedForFixups because it uses a relative offset.

    if ( 0 == GetPointeeFormatStringOffset() )
        RegisterRecPointerForFixup( pCCB, GetFormatStringOffset() + 2 );
    
    pFormatString->PushShortOffset( GetPointeeFormatStringOffset() - Offset,
                                    Offset );

    return GetPointeeFormatStringOffset();
}

void
CG_POINTER::GenNdrParamOffline( CCB * pCCB )
{
    GenNdrFormat( pCCB );
}

void
CG_POINTER::GenNdrFormatPointee( CCB * pCCB )
/*++

Routine Description :

    Generates the format string description for the pointee of an
    unattributed pointer to anything.

Arguments :

    pCCB        - pointer to the code control block.

 --*/
{
    CG_NDR *    pChild;

    pChild = (CG_NDR *)GetChild();

    if (pChild->IsProc())
    {
        MIDL_ASSERT(0);
    } 
    else
    {
        //
        // For unattributed pointers (no size or length), this is simply a
        // call to the child's GenNdrFormat method.
        //
        pChild->GenNdrFormat( pCCB );

        SetPointeeFormatStringOffset( pChild->GetFormatStringOffset() );
    }
}

BOOL
CG_POINTER::ShouldFreeOffline()
{
    CG_NDR *    pNdr;

    //
    // The order here is very, very important.
    //

    if ( IsAllocateDontFree() )
        return FALSE;

    pNdr = (CG_NDR *) GetChild();

    //
    // Skip past generic handle nodes.
    //
    if ( pNdr->GetCGID() == ID_CG_GENERIC_HDL )
        pNdr = (CG_NDR *) pNdr->GetChild();

    //
    // Check for handles.
    //
    if ( (pNdr->GetCGID() == ID_CG_CONTEXT_HDL) ||
         (pNdr->GetCGID() == ID_CG_PRIMITIVE_HDL) )
        return FALSE;

    //
    // Offline full pointers.
    //
    if ( GetPtrType() == PTR_FULL )
        return TRUE;

    switch ( GetCGID() )
        {
        case ID_CG_PTR :
        case ID_CG_SIZE_PTR :
            break;

        case ID_CG_STRING_PTR :
        case ID_CG_STRUCT_STRING_PTR :
            return FALSE;

        case ID_CG_SIZE_LENGTH_PTR :
        case ID_CG_LENGTH_PTR :
        case ID_CG_SIZE_STRING_PTR :
        case ID_CG_BC_PTR :
            return TRUE;

        default :
            MIDL_ASSERT(0);
        }

    if ( pNdr->IsSimpleType() )
        return FALSE;

    if ( pNdr->IsStruct() )
        return ((CG_STRUCT *)pNdr)->ShouldFreeOffline();

    return TRUE;
}

void
CG_POINTER::GenFreeInline( CCB * pCCB )
{
    CG_PARAM *  pParam;
    CG_NDR *    pNdr;
    BOOL        fFree;

    if ( ShouldFreeOffline() || IsAllocateDontFree() )
        return;

    //
    // We use the buffer for these since they have to be [in] or [in,out].
    //
    if ( GetCGID() == ID_CG_STRING_PTR )
        return;

    fFree = FALSE;

    pParam = (CG_PARAM *) pCCB->GetLastPlaceholderClass();

    pNdr = (CG_NDR *) GetChild();

    //
    // Skip past generic handle nodes.
    //
    if ( pNdr->GetCGID() == ID_CG_GENERIC_HDL )
        pNdr = (CG_NDR *) pNdr->GetChild();

    //
    // Check for handles.
    //
    if ( (pNdr->GetCGID() == ID_CG_CONTEXT_HDL) ||
         (pNdr->GetCGID() == ID_CG_PRIMITIVE_HDL) )
        return;

    //
    // Free a pointer to simple type only if it's a pointer to enum16 or int3264.
    //
    if ( pNdr->IsSimpleType() )
        fFree = ((pNdr->GetCGID() == ID_CG_ENUM) &&  !((CG_ENUM *)pNdr)->IsEnumLong()) 
                ||pNdr->GetCGID() == ID_CG_INT3264;

    //
    // Out only pointer is freed if it wasn't allocated on the server's stack.
    // We overwrite any previous freeing descision.
    //
    if ( ! pParam->IsParamIn() )
        fFree = ShouldPointerFree();

    if ( fFree )
        {
        //
        // Always check if the pointer is not null before freeing.
        //
        Out_FreeParamInline( pCCB );
        }
}

void CG_SIZE_POINTER::GenNdrFormatPointee( CCB * pCCB )
/*++

Routine Description :

    Generates the format string description for the pointee of an
    sized pointer to anything.
    Since a sized pointer is really the same as a pointer to a conformant
    array in Ndr terms, we just create a CG_CONFORMANT_ARRAY class on the
    fly and tell it to generate it's code.

Arguments :

    pCCB        - pointer to the code control block.

 --*/
{
    CG_CONFORMANT_ARRAY *   pConformantArray;
    CG_QUALIFIED_POINTER *  pOldSizePtr;

    if ( GetPointeeFormatStringOffset() != -1 )
        return;

#if 0
    //
    // This fixes the case below but causes regressions in ds bvt's.  Pull it
    // for now.
    //

    if ( GetPointee() && GetPointee()->GetFormatStringOffset() != -1 )
    {
        // This happens when the sized pointer is recursively defined.
        // e.g. struct f {long s; [size_is(s)] struct f *p;};

        SetPointeeFormatStringOffset( GetPointee()->GetFormatStringOffset() );
        return;
    }
#endif

    if ( IsMultiSize() )
        {
        CG_NDR * pChild = (CG_NDR *) GetChild();

        SetIsInMultiSized( TRUE );

        if ( (pChild->GetCGID() == ID_CG_SIZE_PTR) ||
             (pChild->GetCGID() == ID_CG_SIZE_LENGTH_PTR) ||
             (pChild->GetCGID() == ID_CG_SIZE_STRING_PTR) )
            {
            ((CG_QUALIFIED_POINTER *)pChild)->SetIsInMultiSized( TRUE );
            ((CG_QUALIFIED_POINTER *)pChild)->SetDimension(GetDimension() + 1);
            }
        }

    pOldSizePtr = pCCB->GetCurrentSizePointer();
    pCCB->SetCurrentSizePointer( this );

    pConformantArray = new CG_CONFORMANT_ARRAY( this );

    SetPointee( pConformantArray );

    pConformantArray->SetPtrType( PTR_REF );

    pConformantArray->SetChild( GetChild() );

    pConformantArray->SetIsInMultiDim( IsInMultiSized() );

    if ( IsInMultiSized() && !pCCB->GetCGNodeContext()->IsProc() )
        {
        pConformantArray->ForceComplex();
        }

    pConformantArray->SetFormatStringOffset( -1 );

    pConformantArray->GenNdrFormat( pCCB );

    SetPointeeFormatStringOffset( pConformantArray->GetFormatStringOffset() );

    pCCB->SetCurrentSizePointer( pOldSizePtr );
}

void CG_SIZE_LENGTH_POINTER::GenNdrFormatPointee( CCB * pCCB )
/*++

Routine Description :

    Generates the format string description for the pointee of a
    size-length pointer to anything.
    Since a size-length pointer is really the same as a pointer to a conformant
    varying array in Ndr terms, we just create a CG_CONFORMANT_VARYING_ARRAY
    class on the fly and tell it to generate it's code.

Arguments :

    pCCB        - pointer to the code control block.

 --*/
{
    CG_CONFORMANT_VARYING_ARRAY *   pConfVaryArray;
    CG_QUALIFIED_POINTER *          pOldSizePtr;

    if ( GetPointeeFormatStringOffset() != -1 )
        return;

    if ( IsMultiSize() )
        {
        CG_NDR * pChild = (CG_NDR *) GetChild();

        SetIsInMultiSized( TRUE );

        if ( (pChild->GetCGID() == ID_CG_SIZE_PTR) ||
             (pChild->GetCGID() == ID_CG_SIZE_LENGTH_PTR) ||
             (pChild->GetCGID() == ID_CG_SIZE_STRING_PTR) )
            {
            ((CG_QUALIFIED_POINTER *)pChild)->SetIsInMultiSized( TRUE );
            ((CG_QUALIFIED_POINTER *)pChild)->SetDimension(GetDimension() + 1);
            }
        }

    pOldSizePtr = pCCB->GetCurrentSizePointer();
    pCCB->SetCurrentSizePointer( this );

    pConfVaryArray = new CG_CONFORMANT_VARYING_ARRAY( this );

    SetPointee( pConfVaryArray );

    pConfVaryArray->SetPtrType( PTR_REF );

    pConfVaryArray->SetChild( GetChild() );

    pConfVaryArray->SetIsInMultiDim( IsInMultiSized() );

    if ( IsInMultiSized() && !pCCB->GetCGNodeContext()->IsProc() )
        {
        pConfVaryArray->ForceComplex();
        }

    pConfVaryArray->SetFormatStringOffset( -1 );

    pConfVaryArray->GenNdrFormat( pCCB );

    SetPointeeFormatStringOffset( pConfVaryArray->GetFormatStringOffset() );

    pCCB->SetCurrentSizePointer( pOldSizePtr );
}

// --------------------------------------------------------------------------
// Strings
// --------------------------------------------------------------------------

void
CG_STRING_POINTER::GenNdrFormat( CCB * pCCB )
/*++

Routine Description :

    Generates the format string description for a string pointer.

Arguments :

    pCCB        - pointer to the code control block.

 --*/
{
    FORMAT_STRING * pFormatString = pCCB->GetFormatString();
    long            StartOffset   = pFormatString->GetCurrentOffset();
    
    if ( GetFormatStringOffset() != -1 )
        return;

    SetFormatStringOffset( StartOffset );
    SetFormatStringEndOffset( StartOffset + 4 );

    if ( GenNdrFormatAlways( pCCB ) == 0 )
        {
        // Don't optimize out if the pointee wasn't generated yet.
        return;
        }

    // here, we assume all pointers generate 4 bytes
    pFormatString->OptimizeFragment( this );
}

long
CG_STRING_POINTER::GenNdrFormatAlways( CCB * pCCB )
/*++

Routine Description :

    Generates the format string description for a string pointer.

Arguments :

    pCCB        - pointer to the code control block.

 --*/
{
    GenNdrPointerType( pCCB );

    if ( IsStringableStruct() )
        {
        FORMAT_STRING * pFormatString;
        long            Offset;

        pFormatString = pCCB->GetFormatString();

        //
        // For stringable struct's we must emit the offset to the pointee
        // description.  Regular string pointers have the actual description
        // immediately following.
        //

        Offset = pFormatString->GetCurrentOffset();
        pFormatString->PushShortOffset( 0 );

        GenNdrFormatPointee( pCCB );

        pFormatString->PushShortOffset( GetPointeeFormatStringOffset() - Offset,
                                        Offset );

        return GetPointeeFormatStringOffset();
        }

    GenNdrFormatPointee( pCCB );

    pCCB->GetFormatString()->PushFormatChar( FC_PAD );

    return GetPointeeFormatStringOffset();
}

void
CG_STRING_POINTER::GenNdrFormatPointee( CCB * pCCB )
/*++

Routine Description :

    Generate the format string of the actual string type without the
    pointer attributes.

Arguments :

    pCCB        - pointer to the code control block.

 --*/
{
    FORMAT_STRING * pFormatString;

    pFormatString = pCCB->GetFormatString();

    //
    // Check for stringable struct.
    //
    if ( IsStringableStruct() )
        {
        if ( GetPointeeFormatStringOffset() != -1 )
            return;

        SetPointeeFormatStringOffset( pFormatString->GetCurrentOffset() );

        pFormatString->PushFormatChar( FC_C_SSTRING );
        pFormatString->PushByte( ((CG_NDR *)GetChild())->GetWireSize() );

        return;
        }

    //
    // Always generate the format string.  The description of a non-sized
    // string pointer is not shared.
    //

    switch ( ((CG_BASETYPE *)GetChild())->GetFormatChar() )
        {
        case FC_CHAR :
        case FC_BYTE :
            pFormatString->PushFormatChar( FC_C_CSTRING );
            break;
        case FC_WCHAR :
            pFormatString->PushFormatChar( FC_C_WSTRING );
            break;
        default :
            MIDL_ASSERT(0);
        }
}

void
CG_SIZE_STRING_POINTER::GenNdrFormat( CCB * pCCB )
/*++

Routine Description :

    Generates the format string description for a sized string pointer.

Arguments :

    pCCB        - pointer to the code control block.

 --*/
{
    FORMAT_STRING * pFormatString = pCCB->GetFormatString();
    long            StartOffset   = pFormatString->GetCurrentOffset();
    
    if ( GetFormatStringOffset() != -1 )
        return;

    SetFormatStringOffset( StartOffset );
    SetFormatStringEndOffset( StartOffset + 4 );

    if ( GenNdrFormatAlways( pCCB ) == 0 )
        {
        // Don't optimize out if the pointee wasn't generated yet.
        return;
        }

    // here, we assume all pointers generate 4 bytes
    pFormatString->OptimizeFragment( this );
}

long
CG_SIZE_STRING_POINTER::GenNdrFormatAlways( CCB * pCCB )
/*++

Routine Description :

    Generates the format string description for a sized string pointer.

Arguments :

    pCCB        - pointer to the code control block.

 --*/
{
    FORMAT_STRING *     pFormatString;
    long                Offset;

    pFormatString = pCCB->GetFormatString();

    GenNdrPointerType( pCCB );

    // Get the current offset.
    Offset = pFormatString->GetCurrentOffset();

    // Push a short for the offset to be filled in later.
    pFormatString->PushShortOffset( 0 );

    // Generate the pointee's format string.
    GenNdrFormatPointee( pCCB );

    // Now fill in the offset field correctly.
    pFormatString->PushShortOffset( GetPointeeFormatStringOffset() - Offset,
                                    Offset );

    return GetPointeeFormatStringOffset();
}

void
CG_SIZE_STRING_POINTER::GenNdrFormatPointee( CCB * pCCB )
/*++

Routine Description :

    Generate the format string of the actual string type without the
    pointer attributes.

Arguments :

    pCCB        - pointer to the code control block.

 --*/
{
    FORMAT_STRING *         pFormatString;
    CG_QUALIFIED_POINTER *  pOldSizePtr;

    if ( GetPointeeFormatStringOffset() != -1 )
        return;

    pFormatString = pCCB->GetFormatString();

    SetPointeeFormatStringOffset( pFormatString->GetCurrentOffset() );

    pOldSizePtr = pCCB->GetCurrentSizePointer();
    pCCB->SetCurrentSizePointer( this );

    //
    // Check for stringable struct.
    //
    if ( IsStringableStruct() )
        {
        pFormatString->PushFormatChar( FC_C_SSTRING );
        pFormatString->PushByte( ((CG_NDR *)GetChild())->GetWireSize() );
        pFormatString->PushFormatChar( FC_STRING_SIZED );
        pFormatString->PushFormatChar( FC_PAD );

        GenFormatStringConformanceDescription( pCCB, TRUE, IsInMultiSized() );

        pCCB->SetCurrentSizePointer( pOldSizePtr );
        return;
        }

    switch ( ((CG_BASETYPE *)GetChild())->GetFormatChar() )
        {
        case FC_CHAR :
        case FC_BYTE :
            pFormatString->PushFormatChar( FC_C_CSTRING );
            break;
        case FC_WCHAR :
            pFormatString->PushFormatChar( FC_C_WSTRING );
            break;
        default :
            MIDL_ASSERT(0);
        }

    pFormatString->PushFormatChar( FC_STRING_SIZED );

    //
    // Set the IsPointer parameter to TRUE.
    //
    GenFormatStringConformanceDescription( pCCB, TRUE, IsInMultiSized() );

    pCCB->SetCurrentSizePointer( pOldSizePtr );
}

void
CG_BYTE_COUNT_POINTER::GenNdrFormat( CCB * pCCB )
/*++

Routine Description :

    Generates the format string description for a byte count pointer.

Arguments :

    pCCB        - pointer to the code control block.

 --*/
{
    CG_ITERATOR         Iterator;
    FORMAT_STRING *     pFormatString;
    CG_PROC *           pProc;
    CG_PARAM *          pParam;
    CG_NDR *            pChild;
    unsigned short      uConfFlags = 0;

    if ( GetFormatStringOffset() != -1 )
        return;

    pFormatString = pCCB->GetFormatString();

    pChild = (CG_NDR *) GetChild();

    if ( ! pChild->IsSimpleType() || pChild->GetRangeAttribute() )
        pChild->GenNdrFormat( pCCB );

    SetFormatStringOffset( pFormatString->GetCurrentOffset() );

    pFormatString->PushFormatChar( FC_BYTE_COUNT_POINTER );

    if ( pChild->IsSimpleType() && !pChild->GetRangeAttribute() )
        pChild->GenNdrFormat( pCCB );
    else
        pFormatString->PushFormatChar( FC_PAD );

    pProc = (CG_PROC *) pCCB->GetCGNodeContext();

    pProc->GetMembers( Iterator );

    bool      fThisIsFirst = false;

    while ( ITERATOR_GETNEXT( Iterator, pParam ) )
        {
        if ( GetByteCountParam() == pParam->GetType() )
            {
            break;
            }
        if ( pParam->GetChild() == this )
            {
            fThisIsFirst = true;
            }
        }

    uConfFlags |= fThisIsFirst ? 0 : FC_EARLY_CORRELATION;

    MIDL_ASSERT( ((CG_NDR *)pParam->GetChild())->IsSimpleType() );

    CG_BASETYPE *   pCount = (CG_BASETYPE *) pParam->GetChild();
    unsigned char   Type;

    Type = (unsigned char) pCount->GetFormatChar();
    Type |= FC_TOP_LEVEL_CONFORMANCE;

    // Byte count description, just do it here.
    pFormatString->PushByte( Type );
    pFormatString->PushByte( 0 );
    pFormatString->PushShortStackOffset(
                        pParam->GetStackOffset( pCCB, I386_STACK_SIZING ) );

    if ( pCommand->IsSwitchDefined( SWITCH_ROBUST ) ) 
        {
        OUT_CORRELATION_DESC( pFormatString, uConfFlags );
        }
    if ( !pChild->IsSimpleType() || pChild->GetRangeAttribute() )
        {
        pFormatString->PushShortOffset( pChild->GetFormatStringOffset() -
                                          pFormatString->GetCurrentOffset() );
        }
}

void
CG_INTERFACE_POINTER::GenNdrFormat( CCB * pCCB )
/*++

Routine Description :

    Generates the format string description for an interface pointer.

Arguments :

    pCCB        - pointer to the code control block.

 --*/
{
    FORMAT_STRING *     pFormatString;

    if ( GetFormatStringOffset() != -1 )
        return;

    pFormatString = pCCB->GetFormatString();

    SetFormatStringOffset( pFormatString->GetCurrentOffset() );

    //
    // There are two cases, the constant UUID and the [iid_is] expression.
    //
    // In the normal case, we will get the 16 byte UUID from the [uuid]
    // attribute on the interface node.  The 16 byte UUID is written to the
    // format string.  Note that the UUID in the format string is not aligned
    // in memory.  The UUID must be copied to a local structure before being
    // used.
    //

    // Get the interface node.
    pFormatString->PushFormatChar( FC_IP );

    //
    // Else handle a constant iid interface pointer.
    //

    MIDL_ASSERT( GetTheInterface()->NodeKind() == NODE_INTERFACE );

    pFormatString->PushFormatChar( FC_CONSTANT_IID );

    GenNdrFormatForGuid( pCCB );

    SetFormatStringEndOffset( pFormatString->GetCurrentOffset() );
    pFormatString->OptimizeFragment( this );

}

void
CG_INTERFACE_POINTER::GenNdrFormatForGuid( CCB * pCCB )
{
    node_interface  *pInterface    = GetTheInterface();
    FORMAT_STRING   *pFormatString = pCCB->GetFormatString();
    node_guid       *pGuid;
    char            *p1, *p2, *p3, *p4, *p5;

    // Get the [uuid] from the interface node.
    pGuid = (node_guid *)pInterface->GetAttribute( ATTR_GUID );

    MIDL_ASSERT( pGuid && "No UUID for interface pointer" );

    pGuid->GetStrs( &p1, &p2, &p3, &p4, &p5 );

    pFormatString->PushLong( StringToHex( p1 ) );
    pFormatString->PushShort( StringToHex( p2 ) );
    pFormatString->PushShort( StringToHex( p3 ) );

    char    Buffer[20];
    char    String[4];
    int     i;

    strcpy( Buffer, p4 );
    strcat( Buffer, p5 );

    for ( i = 0; i < 16; i += 2 )
        {
        String[0] = Buffer[i];
        String[1] = Buffer[i+1];
        String[2] = '\0';

        pFormatString->PushByte( StringToHex( String ) );
        }
}

long
CG_INTERFACE_POINTER::GenNdrFormatAlways( CCB * pCCB )
{
    long    OldOffset;

    OldOffset = GetFormatStringOffset();

    SetFormatStringOffset( -1 );

    GenNdrFormat( pCCB );

    SetFormatStringOffset( OldOffset );

    // The Always methods return the offset to pointee to watch for 0.
    // This does not apply to intf pointer, so just return the current offset.
    //
    return GetFormatStringOffset();
}

void
CG_IIDIS_INTERFACE_POINTER::GenNdrFormat( CCB * pCCB )
/*++

Routine Description :

    Generates the format string description for an interface pointer.

Arguments :

    pCCB        - pointer to the code control block.

 --*/
{
    FORMAT_STRING *     pFormatString;

    if ( GetFormatStringOffset() != -1 )
        return;

    pFormatString = pCCB->GetFormatString();

    SetFormatStringOffset( pFormatString->GetCurrentOffset() );

    //
    // There are two cases, the constant UUID and the [iid_is] expression.
    //
    // In the normal case, we will get the 16 byte UUID from the [uuid]
    // attribute on the interface node.  The 16 byte UUID is written to the
    // format string.  Note that the UUID in the format string is not aligned
    // in memory.  The UUID must be copied to a local structure before being
    // used.
    //

    // Get the interface node.
    pFormatString->PushFormatChar( FC_IP );

    MIDL_ASSERT( GetIIDExpr() );

        //
        // Interface pointer has [iid_is] applied to it.
        //
        pFormatString->PushFormatChar( FC_PAD );
        
        GenNdrFormatAttributeDescription( pCCB,
                                          0,
                                          GetIIDExpr(),
                                          TRUE,
                                          FALSE,
                                          FALSE,
                                          FALSE,
                                          pCommand->IsSwitchDefined( SWITCH_ROBUST ),
                                          FC_IID_CORRELATION
                                          );
        return;

}

long
CG_IIDIS_INTERFACE_POINTER::GenNdrFormatAlways( CCB * pCCB )
{
    long    OldOffset;

    OldOffset = GetFormatStringOffset();

    SetFormatStringOffset( -1 );

    GenNdrFormat( pCCB );

    SetFormatStringOffset( OldOffset );

    // The Always methods return the offset to pointee to watch for 0.
    // This does not apply to intf pointer, so just return the current offset.
    //
    return GetFormatStringOffset();
}

static long
StringToHex( char * str )
{
    long    l;

    l = 0;

    for ( ; *str ; str++ )
        {
        l *= 16;

        if ( ('0' <= *str) && (*str <= '9') )
            {
            l += *str - '0';
            continue;
            }

        if ( ('a' <= *str) && (*str <= 'f') )
            {
            l += 10 + *str - 'a';
            continue;
            }

        if ( ('A' <= *str) && (*str <= 'F') )
            {
            l += 10 + *str - 'A';
            continue;
            }

        MIDL_ASSERT(0);
        }

    return l;
}