//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 2000.
//
//  File:       clipformat.cxx
//
//  Contents:   Support for Windows/OLE data types for oleprx32.dll.
//              Used to be transmit_as routines, now user_marshal routines.
//
//              This file contains support for SNB.
//
//  Functions:  
//              SNB_UserSize
//              SNB_UserMarshal
//              SNB_UserUnmarshal
//              SNB_UserFree
//              SNB_UserSize64
//              SNB_UserMarshal64
//              SNB_UserUnmarshal64
//              SNB_UserFree64
//
//  History:    13-Dec-00   JohnDoty    Migrated from transmit.cxx
//
//--------------------------------------------------------------------------
#include "stdrpc.hxx"
#pragma hdrstop

#include <oleauto.h>
#include <objbase.h>
#include "transmit.hxx"
#include <rpcwdt.h>
#include <storext.h>
#include "widewrap.h"
#include <valid.h>
#include <obase.h>
#include <stream.hxx>

#include "carefulreader.hxx"

//+-------------------------------------------------------------------------
//
//  Function:   SNB_UserSize
//
//  Synopsis:   Sizes an SNB.
//
//  Derivation: An array of strings in one block of memory.
//
//  history:    June-95   Ryszardk      Created, based on SNB_*_xmit.
//
//--------------------------------------------------------------------------

unsigned long  __RPC_USER
SNB_UserSize (
    unsigned long * pFlags,
    unsigned long   Offset,
    SNB           * pSnb )
{
    if ( ! pSnb )
        return Offset;

    // calculate the number of strings and characters (with their terminators)

    ULONG ulCntStr = 0;
    ULONG ulCntChar = 0;

    if (pSnb && *pSnb)
        {
        SNB snb = *pSnb;

        WCHAR *psz = *snb;
        while (psz)
            {
            ulCntChar += lstrlenW(psz) + 1;
            ulCntStr++;
            snb++;
            psz = *snb;
            }
        }

    // The wire size is: conf size, 2 fields and the wchars.

    LENGTH_ALIGN( Offset, 3 );

    return ( Offset + 3 * sizeof(long) + ulCntChar * sizeof( WCHAR ) );
}

//+-------------------------------------------------------------------------
//
//  Function:   SNB_UserMarshall
//
//  Synopsis:   Marshalls an SNB into the RPC buffer.
//
//  Derivation: An array of strings in one block of memory.
//
//  history:    June-95   Ryszardk      Created, based on SNB_*_xmit.
//
//--------------------------------------------------------------------------

unsigned char __RPC_FAR * __RPC_USER
SNB_UserMarshal (
    unsigned long * pFlags,
    unsigned char * pBuffer,
    SNB           * pSnb )
{
    UserNdrDebugOut((UNDR_FORCE, "SNB_UserMarshal\n"));

    if ( ! pSnb )
        return pBuffer;

    // calculate the number of strings and characters (with their terminators)

    ULONG ulCntStr = 0;
    ULONG ulCntChar = 0;

    if (pSnb && *pSnb)
        {
        SNB snb = *pSnb;

        WCHAR *psz = *snb;
        while (psz)
            {
            ulCntChar += lstrlenW(psz) + 1;
            ulCntStr++;
            snb++;
            psz = *snb;
            }
        }

    // conformant size

    ALIGN( pBuffer, 3 );
    *( PULONG_LV_CAST pBuffer)++ = ulCntChar;

    // fields

    *( PULONG_LV_CAST pBuffer)++ = ulCntStr;
    *( PULONG_LV_CAST pBuffer)++ = ulCntChar;

    // actual strings only

    if ( pSnb  &&  *pSnb )
        {
        // There is a NULL string pointer to mark the end of the pointer array.
        // However, the strings don't have to follow tightly.
        // Hence, we have to copy one string at a time.

        SNB   snb = *pSnb;
        WCHAR *pszSrc;

        while (pszSrc = *snb++)
            {
            ULONG ulCopyLen = (lstrlenW(pszSrc) + 1) * sizeof(WCHAR);

            WdtpMemoryCopy( pBuffer, pszSrc, ulCopyLen );
            pBuffer += ulCopyLen;
            }
        }

    return pBuffer;
}

//+-------------------------------------------------------------------------
//
//  Function:   SNB_UserUnmarshall
//
//  Synopsis:   Unmarshalls an SNB from the RPC buffer.
//
//  Derivation: An array of strings in one block of memory.
//
//  history:    June-95   Ryszardk      Created, based on SNB_*_xmit.
//              Aug-99    JohnStra      Add consistency checks.
//
//--------------------------------------------------------------------------

unsigned char __RPC_FAR * __RPC_USER
SNB_UserUnmarshal (
    unsigned long * pFlags,
    unsigned char * pBuffer,
    SNB           * pSnb )
{
    UserNdrDebugOut((UNDR_FORCE, "SNB_UserUnmarshal\n"));

    // Initialize CUserMarshalInfo object and get the buffer
    // size and pointer to the start of the buffer.
    CUserMarshalInfo MarshalInfo( pFlags, pBuffer );
    ULONG_PTR BufferSize   = MarshalInfo.GetBufferSize();
    UCHAR*    pBufferStart = MarshalInfo.GetBuffer();

    // Align the buffer and save the size of the fixup.
    ALIGN( pBuffer, 3 );
    ULONG_PTR cbFixup = (ULONG_PTR)(pBuffer - pBufferStart);

    // Check for EOB before trying to get header.
    CHECK_BUFFER_SIZE( BufferSize, cbFixup + (3 * sizeof( ULONG )) );

    // Get the header from the buffer.
    ULONG ulCntChar = *( PULONG_LV_CAST pBuffer)++;
    ULONG ulCntStr = *( PULONG_LV_CAST pBuffer)++;
    ULONG ulCntCharDup = *(PULONG_LV_CAST pBuffer)++;

    // Verify that 2nd instance of count matches first.
    if ( ulCntCharDup != ulCntChar )
        RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );

    // No reusage of pSNB.
    if ( *pSnb )
        WdtpFree( pFlags, *pSnb );

    if ( ulCntStr == 0 )
        {
        // There are no strings.

        // NULL pSnb and return.
        *pSnb = NULL;
        return pBuffer;
        }

    // Validate the header:
    // Repeated char count must match first instance and char count must
    // not be less than the number of strings since that would mean at
    // least one of them doesn't isn't terminated.
    if ( (ulCntChar != ulCntCharDup) || (ulCntChar < ulCntStr) )
        RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );

    // Check for EOB before trying to get the strings.
    CHECK_BUFFER_SIZE(
        BufferSize,
        cbFixup + (3 * sizeof(ULONG)) + (ulCntChar * sizeof(WCHAR)) );

    // Last WCHAR in the buffer must be the UNICODE terminator.
    WCHAR* pszChars = (WCHAR*) pBuffer;
    if ( pszChars[ulCntChar - 1] != 0x0000 )
        RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );

    // construct the SNB.
    SNB Snb = (SNB) WdtpAllocate( pFlags,
                                  ( (ulCntStr + 1) * sizeof(WCHAR *) +
                                     ulCntChar * sizeof(WCHAR)) );
    *pSnb = Snb;

    if (Snb)
        {
        // create the pointer array within the SNB.  to do this, we go through
        // the buffer, and use strlen to find the end of the each string for
        // us.
        WCHAR *pszSrc = (WCHAR *) pBuffer;
        WCHAR *pszTgt = (WCHAR *) (Snb + ulCntStr + 1); // right behind array

        void* SnbStart = Snb;
        ULONG ulTotLen = 0;
        ULONG i;
        for (i = ulCntStr; (i > 0) && (ulTotLen < ulCntChar); i--)
            {
            *Snb++ = pszTgt;

            ULONG ulLen = lstrlenW(pszSrc) + 1;
            pszSrc += ulLen;
            pszTgt += ulLen;
            ulTotLen += ulLen;
            }

        *Snb++ = NULL;

        // Verify that the number of strings and the number of chars
        // in the buffer matches what is supposed to be there.
        if ( (i > 0) || (ulTotLen < ulCntChar) )
            {
            WdtpFree( pFlags, SnbStart );
            RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );
            }

        // Copy the actual strings.
        // We can do a block copy here as we packed them tight in the buffer.
        // Snb points right behind the lastarray of pointers within the SNB.
        WdtpMemoryCopy( Snb, pBuffer, ulCntChar * sizeof(WCHAR) );
        pBuffer += ulCntChar * sizeof(WCHAR);
        }

    return pBuffer;
}

//+-------------------------------------------------------------------------
//
//  Function:   SNB_UserFree
//
//  Synopsis:   Frees an SNB.
//
//  Derivation: An array of strings in one block of memory.
//
//  history:    June-95   Ryszardk      Created, based on SNB_*_xmit.
//
//--------------------------------------------------------------------------

void __RPC_USER
SNB_UserFree(
    unsigned long * pFlags,
    SNB           * pSnb )
{
    if ( pSnb && *pSnb )
        WdtpFree( pFlags, *pSnb );
}

#if defined(_WIN64)

//+-------------------------------------------------------------------------
//
//  Function:   SNB_UserSize64
//
//  Synopsis:   Sizes an SNB.
//
//  Derivation: An array of strings in one block of memory.
//
//  history:    Dec-00   JohnDoty      Created from 32bit function
//
//--------------------------------------------------------------------------

unsigned long  __RPC_USER
SNB_UserSize64 (
    unsigned long * pFlags,
    unsigned long   Offset,
    SNB           * pSnb )
{
    if ( ! pSnb )
        return Offset;

    // calculate the number of strings and characters (with their terminators)

    ULONG ulCntStr = 0;
    ULONG ulCntChar = 0;

    if (pSnb && *pSnb)
    {
        SNB snb = *pSnb;
        
        WCHAR *psz = *snb;
        while (psz)
        {
            ulCntChar += lstrlenW(psz) + 1;
            ulCntStr++;
            snb++;
            psz = *snb;
        }
    }
    
    // The wire size is: conf size, 2 fields and the wchars.
    LENGTH_ALIGN( Offset, 7 );    
    return ( Offset + 8 + (2 * sizeof(long)) + (ulCntChar * sizeof(WCHAR)) );
}

//+-------------------------------------------------------------------------
//
//  Function:   SNB_UserMarshal64
//
//  Synopsis:   Marshalls an SNB into the RPC buffer.
//
//  Derivation: An array of strings in one block of memory.
//
//  history:    Dec-00   JohnDoty      Created from 32bit function
//
//--------------------------------------------------------------------------
unsigned char __RPC_FAR * __RPC_USER
SNB_UserMarshal64 (
    unsigned long * pFlags,
    unsigned char * pBuffer,
    SNB           * pSnb )
{
    UserNdrDebugOut((UNDR_FORCE, "SNB_UserMarshal\n"));

    if ( ! pSnb )
        return pBuffer;

    // calculate the number of strings and characters (with their terminators)

    ULONG ulCntStr = 0;
    ULONG ulCntChar = 0;

    if (pSnb && *pSnb)
    {
        SNB snb = *pSnb;
        
        WCHAR *psz = *snb;
        while (psz)
        {
            ulCntChar += lstrlenW(psz) + 1;
            ulCntStr++;
            snb++;
            psz = *snb;
        }
    }

    // conformant size
    ALIGN( pBuffer, 7 );
    *( PHYPER_LV_CAST pBuffer)++ = ulCntChar;

    // fields
    *( PULONG_LV_CAST pBuffer)++ = ulCntStr;
    *( PULONG_LV_CAST pBuffer)++ = ulCntChar;

    // actual strings only

    if ( pSnb  &&  *pSnb )
    {
        // There is a NULL string pointer to mark the end of the pointer array.
        // However, the strings don't have to follow tightly.
        // Hence, we have to copy one string at a time.
        
        SNB   snb = *pSnb;
        WCHAR *pszSrc;
        
        while (pszSrc = *snb++)
        {
            ULONG ulCopyLen = (lstrlenW(pszSrc) + 1) * sizeof(WCHAR);
            
            WdtpMemoryCopy( pBuffer, pszSrc, ulCopyLen );
            pBuffer += ulCopyLen;
        }
    }

    return pBuffer;
}

//+-------------------------------------------------------------------------
//
//  Function:   SNB_UserUnmarshal64
//
//  Synopsis:   Unmarshalls an SNB from the RPC buffer.
//
//  Derivation: An array of strings in one block of memory.
//
//  history:    Dec-00   JohnDoty       Created from 32bit function
//
//--------------------------------------------------------------------------
unsigned char __RPC_FAR * __RPC_USER
SNB_UserUnmarshal64 (
    unsigned long * pFlags,
    unsigned char * pBuffer,
    SNB           * pSnb )
{
    UserNdrDebugOut((UNDR_FORCE, "SNB_UserUnmarshal\n"));

    // Initialize CUserMarshalInfo object and get the buffer
    // size and pointer to the start of the buffer.
    CUserMarshalInfo MarshalInfo( pFlags, pBuffer );
    CarefulBufferReader stream( pBuffer, MarshalInfo.GetBufferSize() );

    // Get the header from the buffer....  (ReadHYPER aligns on 8).
    ULONG ulCntChar    = (ULONG)stream.ReadHYPER();
    ULONG ulCntStr     = stream.ReadULONGNA();
    ULONG ulCntCharDup = stream.ReadULONGNA();

    // Verify that 2nd instance of count matches first.
    if ( ulCntCharDup != ulCntChar )
        RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );

    // No reusage of pSNB.
    if ( *pSnb )
    {
        WdtpFree( pFlags, *pSnb );
        *pSnb = NULL;
    }

    if ( ulCntStr == 0 )
    {
        // There are no strings.
        return stream.GetBuffer();
    }

    // Validate the header:
    // Repeated char count must match first instance and char count must
    // not be less than the number of strings since that would mean at
    // least one of them doesn't isn't terminated.
    if ( ulCntChar < ulCntStr )
        RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );

    // Check for EOB before trying to get the strings.
    stream.CheckSize(ulCntChar * sizeof(WCHAR));

    // Last WCHAR in the buffer must be the UNICODE terminator.
    WCHAR* pszChars = (WCHAR*)stream.GetBuffer();
    if ( pszChars[ulCntChar - 1] != L'\0' )
        RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );

    // construct the SNB.
    SNB Snb = (SNB) WdtpAllocate( pFlags,
                                  ( (ulCntStr + 1) * sizeof(WCHAR *) +
                                     ulCntChar * sizeof(WCHAR)) );
    *pSnb = Snb;

    if (Snb)
    {
        // create the pointer array within the SNB.  to do this, we go through
        // the buffer, and use strlen to find the end of the each string for
        // us.
        WCHAR *pszSrc = (WCHAR *) stream.GetBuffer();
        WCHAR *pszTgt = (WCHAR *) (Snb + ulCntStr + 1); // right behind array
        
        void* SnbStart = Snb;
        ULONG ulTotLen = 0;
        ULONG i;
        for (i = ulCntStr; (i > 0) && (ulTotLen < ulCntChar); i--)
        {
            *Snb++ = pszTgt;
            
            ULONG ulLen = lstrlenW(pszSrc) + 1;
            pszSrc += ulLen;
            pszTgt += ulLen;
            ulTotLen += ulLen;
        }
        
        *Snb++ = NULL;
        
        // Verify that the number of strings and the number of chars
        // in the buffer matches what is supposed to be there.
        if ( (i > 0) || (ulTotLen < ulCntChar) )
        {
            WdtpFree( pFlags, SnbStart );
            RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );
        }
        
        // Copy the actual strings.
        // We can do a block copy here as we packed them tight in the buffer.
        // Snb points right behind the lastarray of pointers within the SNB.
        WdtpMemoryCopy( Snb, stream.GetBuffer(), ulCntChar * sizeof(WCHAR) );
        stream.Advance(ulCntChar * sizeof(WCHAR));
    }

    return stream.GetBuffer();
}

//+-------------------------------------------------------------------------
//
//  Function:   SNB_UserFree64
//
//  Synopsis:   Frees an SNB.
//
//  Derivation: An array of strings in one block of memory.
//
//  history:    Dec-00    JohnDoty      Created from 32bit function
//
//--------------------------------------------------------------------------

void __RPC_USER
SNB_UserFree64 (
    unsigned long * pFlags,
    SNB           * pSnb )
{
    if ( pSnb && *pSnb )
        WdtpFree( pFlags, *pSnb );
}

#endif