/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Copyright (c) 2000 Microsoft Corporation

Module Name :

    pointerq.cxx

Abstract :

    This file contains the routines for the pointer queues
    
Author :

    Mike Zoran  mzoran   Jun 2000.

Revision History :

  ---------------------------------------------------------------------*/
#include "ndrp.h"
#include "hndl.h"
#include "ndrole.h"
#include "attack.h"
#include "pointerq.h"

void 
NDR32_POINTER_QUEUE_STATE::Free(
    NDR_POINTER_QUEUE_ELEMENT *pElement 
    ) 
{
    #if defined(DBG)
    memset( pElement, 0xBD, NdrMaxPointerQueueElement);
    #endif
    
    pElement->pNext = pFreeList;
    pFreeList = pElement;
}

NDR_POINTER_QUEUE_ELEMENT *
NDR32_POINTER_QUEUE_STATE::Allocate()
{
    NDR_POINTER_QUEUE_ELEMENT *pNewElement;
    if ( pFreeList )
        {
        pNewElement = pFreeList;
        pFreeList = pNewElement->pNext;
        }
    else
        {
        pNewElement = InternalAllocate();
        }
#if defined(DBG)
     memset( pNewElement, 0xDB, NdrMaxPointerQueueElement );
#endif
    return pNewElement;
}


void 
NDR32_POINTER_QUEUE_STATE::FreeAll() 
{
    while ( pAllocationList )
        {
        AllocationElement *pThisAlloc = pAllocationList;
        pAllocationList = pAllocationList->pNext;
        I_RpcFree( pThisAlloc );
        }    
}

NDR_POINTER_QUEUE_ELEMENT * 
NDR32_POINTER_QUEUE_STATE::InternalAllocate()
{
    if ( !pAllocationList || 
         (ItemsToAllocate == pAllocationList->ItemsAllocated ) )
        {

        struct AllocationElement *pElement =
            (struct AllocationElement *)I_RpcAllocate( sizeof(AllocationElement) );
        if ( !pElement )
            {
            RpcRaiseException( RPC_S_OUT_OF_MEMORY );
            }

        pElement->pNext          = pAllocationList;
        pElement->ItemsAllocated = 0;
        pAllocationList          = pElement;
        }

    return (NDR_POINTER_QUEUE_ELEMENT*)
        (NDR_POINTER_QUEUE_ELEMENT*)pAllocationList->Data[pAllocationList->ItemsAllocated++];
 
}

#if defined(BUILD_NDR64)

NDR_POINTER_QUEUE_ELEMENT *
NDR64_POINTER_QUEUE_STATE::Allocate()
{
    NDR_POINTER_QUEUE_ELEMENT *pNewElement;
    if ( pProcContext->pQueueFreeList )
        {
        pNewElement = pProcContext->pQueueFreeList;
        pProcContext->pQueueFreeList = pNewElement->pNext;
        }
    else
        {
        pNewElement = (NDR_POINTER_QUEUE_ELEMENT *)
            NdrpAlloca( &pProcContext->AllocateContext, 
                        NdrMaxPointerQueueElement );
        }
#if defined(DBG)
     memset( pNewElement, 0xDB, NdrMaxPointerQueueElement );
#endif

     return pNewElement;
}

void 
NDR64_POINTER_QUEUE_STATE::Free(
    NDR_POINTER_QUEUE_ELEMENT *pElement 
    ) 
{
    #if defined(DBG)
    memset( pElement, 0xBD, NdrMaxPointerQueueElement);
    #endif
    
    pElement->pNext = pProcContext->pQueueFreeList;
    pProcContext->pQueueFreeList = pElement;
}

#endif

inline NDR_POINTER_QUEUE::STORAGE::STORAGE( ) :
    pHead(NULL),
    pPrevHead(NULL),
    pPrevInsertPointer(NULL),
    pInsertPointer(&pHead)
{
}

inline void NDR_POINTER_QUEUE::STORAGE::MergeContext()
{
    // Add old list to end of this list.
    if ( pPrevHead )
        {
        // Append list
        *pInsertPointer = pPrevHead;
        // Set insert pointer to end of old list
        pInsertPointer = pPrevInsertPointer;
        }
    
    pPrevHead = NULL;
    pPrevInsertPointer = NULL;
}

inline void NDR_POINTER_QUEUE::STORAGE::NewContext()
{

    pPrevHead          = pHead;
    pPrevInsertPointer = pInsertPointer;

    pHead           = NULL;
    pInsertPointer  = &pHead;

}

inline void NDR_POINTER_QUEUE::STORAGE::InsertTail( NDR_POINTER_QUEUE_ELEMENT *pNewNode )
{
    *pInsertPointer = pNewNode;
    pNewNode->pNext = NULL;
    pInsertPointer  = &pNewNode->pNext;
}

inline NDR_POINTER_QUEUE_ELEMENT *NDR_POINTER_QUEUE::STORAGE::RemoveHead()
{

    NDR_POINTER_QUEUE_ELEMENT *pOldHead = pHead;

    if (!pHead)
        {
        return pHead;
        }

    if ( !pHead->pNext )
        {
        // Last item, reinitialize list. 
        pHead = NULL;
        pInsertPointer = &pHead->pNext;
        }
    else 
        {
        pHead = pHead->pNext;
        }

    return pOldHead;
}



#if defined(DBG)
ulong NdrPointerQueueLogLevel;
#endif

void NDR_POINTER_QUEUE::Enque( NDR_POINTER_QUEUE_ELEMENT * pElement )
{

    Storage.InsertTail( pElement );

#if defined(DBG)
    if ( NdrPointerQueueLogLevel )
        {
        DbgPrint( "Queing Element %p\n", pElement );
        pElement->Print();
        }
#endif

}

void NDR_POINTER_QUEUE::Dispatch()
{
    while ( 1 )
        {

        NDR_POINTER_QUEUE_ELEMENT *pHead = Storage.RemoveHead();

        if ( !pHead )
            return;

#if defined(DBG)
        if ( NdrPointerQueueLogLevel )
            {
            DbgPrint( "Dispatching Element: %p\n", pHead );
            pHead->Print();
            }
#endif
        
        Storage.NewContext();
        pHead->Dispatch( pStubMsg );
        pQueueState->Free( pHead );        
        Storage.MergeContext();

        }
}

NDR_POINTER_QUEUE::NDR_POINTER_QUEUE( PMIDL_STUB_MESSAGE pNewStubMsg,
                                      NDR_POINTER_QUEUE_STATE *pNewState ) :
    pStubMsg( pNewStubMsg ),
    Storage(),
    pQueueState(pNewState)
    {
    }