//#define WIN32_WORKAROUND


/*++

Copyright (c) 1991-1992  Microsoft Corporation

Module Name:

    MidlUser.c

Abstract:

    This file contains common functions and utilities that the API
    DLLs can use in making remote calls.  This includes the
    MIDL_USER_ALLOCATE functions.

    The following routines are called by MIDL-generated code and the
    NetApiBufferAllocate and NetApiBufferFree routines:

       MIDL_user_allocate
       MIDL_user_free

    The following routines are NOT called by MIDL-generated code; they are
    only called by the NetApiBufferReallocate and NetApiBufferSize routines:

       MIDL_user_reallocate
       MIDL_user_size

Author:

    Dan Lafferty    danl    06-Feb-1991

Environment:

    User Mode - Win32

Revision History:

    06-Feb-1991     danl
        Created
    25-Apr-1991 JohnRo
        Split out MIDL user (allocate,free) into seperate source file, so
        linker doesn't get confused.
    03-Dec-1991 JohnRo
        Added MIDL_user_reallocate and MIDL_user_size APIs.  (These are so we
        create the NetApiBufferAllocate, NetApiBufferReallocate, and
        NetApiBufferSize APIs.)
        Also check alignment of allocated data.
    12-Jan-1992 JohnRo
        Workaround a LocalReAlloc() bug where 2nd or 3rd realloc messes up.
        (See WIN32_WORKAROUND code below.)
    08-Jun-1992 JohnRo
        RAID 9258: return non-null pointer when allocating zero bytes.
        Also, SteveWo finally fixed LocalReAlloc() bug, so use it again.
        Avoid calling LocalFree() with null pointer, to avoid access viol.
    01-Dec-1992 JohnRo
        Fix MIDL_user_ func signatures.
        Avoid compiler warnings (const vs. volatile).


--*/

// These must be included first:

#include <windef.h>             // win32 typedefs
#include <rpc.h>                // rpc prototypes

// These may be included in any order:

#include <align.h>              // POINTER_IS_ALIGNED(), ALIGN_WORST.
#include <rpcutil.h>            // My prototypes.

#include <netdebug.h>           // NetpAssert().
#ifdef WIN32_WORKAROUND
#include <string.h>             // memcpy().
#endif // def WIN32_WORKAROUND
#include <stdarg.h>             // memcpy().
#include <winbase.h>            // LocalAlloc(), LMEM_ flags, etc.


#define LOCAL_ALLOCATION_FLAGS  LMEM_FIXED


void __RPC_FAR * __RPC_API
MIDL_user_allocate(
    IN size_t NumBytes
    )

/*++

Routine Description:

    Allocates storage for RPC transactions.  The RPC stubs will either call
    MIDL_user_allocate when it needs to un-marshall data into a buffer
    that the user must free.  RPC servers will use MIDL_user_allocate to
    allocate storage that the RPC server stub will free after marshalling
    the data.

Arguments:

    NumBytes - The number of bytes to allocate.  (Note that NetApiBufferAllocate
        depends on being able to request that zero bytes be allocated and get
        back a non-null pointer.)

Return Value:

    Pointer to the allocated memory.


--*/

{
    LPVOID NewPointer;

    NewPointer = (LPVOID) LocalAlloc(
            LOCAL_ALLOCATION_FLAGS,
            NumBytes);

    NetpAssert( POINTER_IS_ALIGNED( NewPointer, ALIGN_WORST) );

    return (NewPointer);

} // MIDL_user_allocate



void __RPC_API
MIDL_user_free(
    IN void __RPC_FAR *MemPointer
    )

/*++

Routine Description:

    Frees storage used in RPC transactions.  The RPC client can call this
    function to free buffer space that was allocated by the RPC client
    stub when un-marshalling data that is to be returned to the client.
    The Client calls MIDL_user_free when it is finished with the data and
    desires to free up the storage.
    The RPC server stub calls MIDL_user_free when it has completed
    marshalling server data that is to be passed back to the client.

Arguments:

    MemPointer - This points to the memory block that is to be released.

Return Value:

    none.


--*/
{
    NetpAssert( POINTER_IS_ALIGNED( MemPointer, ALIGN_WORST) );
    if (MemPointer != NULL) {
        (void) LocalFree(MemPointer);
    }

} // MIDL_user_free


void *
MIDL_user_reallocate(
    IN void * OldPointer OPTIONAL,
    IN unsigned long NewByteCount
    )
{
    LPVOID NewPointer;  // may be NULL.

    NetpAssert( POINTER_IS_ALIGNED( OldPointer, ALIGN_WORST) );


    // Special cases: something into nothing, or nothing into something.
    if (OldPointer == NULL) {

        NewPointer = (LPVOID) LocalAlloc(
                LOCAL_ALLOCATION_FLAGS,
                NewByteCount);

    } else if (NewByteCount == 0) {

        (void) LocalFree( OldPointer );
        NewPointer = NULL;

    } else {  // must be realloc of something to something else.

        HANDLE hOldMem;
        HANDLE hNewMem;                     // handle for new (may = old handle)

#ifdef WIN32_WORKAROUND

        //
        // BUGBUG: We would use LocalReAlloc here, but that seems to be
        // buggy the second or third time around.  So, let's do a workaround
        // which avoids calling LocalReAlloc.
        //
        DWORD OldSize;

        // Need size of old area to continue...

        hOldMem = LocalHandle( (LPSTR) OldPointer );
        NetpAssert( hOldMem != NULL );

        OldSize = LocalSize( hOldMem );

        NetpAssert( OldSize > 0 );

        if (OldSize == NewByteCount) {
            NewPointer = OldPointer;            // Another special case.
        } else {

            // Normal case (something into something else).  Alloc new area.
            hNewMem = LocalAlloc(
                        LOCAL_ALLOCATION_FLAGS,
                        NewByteCount);
            NewPointer = (LPVOID) hNewMem;

            if (NewPointer != NULL) {

                // Copy lesser of old and new sizes.
                (void) memcpy(
                        NewPointer,             // dest
                        OldPointer,             // src
                        (NewByteCount < OldSize) ? NewByteCount : OldSize);

                if (LocalFree(OldPointer) != NULL) {
                    NetpAssert(FALSE);
                }
            }
        }

#else // not WIN32_WORKAROUND

        hOldMem = LocalHandle( (LPSTR) OldPointer);
        NetpAssert(hOldMem != NULL);

        hNewMem = LocalReAlloc(
                hOldMem,                        // old handle
                NewByteCount,                   // new size in bytes
                LOCAL_ALLOCATION_FLAGS |        // flags
                    LMEM_MOVEABLE);             //  (motion okay)
        if (hNewMem == NULL) {
            // BUGBUG: call GetLastError, could be out of memory or error.
            return (NULL);
        }
        NewPointer = (LPVOID) hNewMem;

#endif // not WIN32_WORKAROUND

    } // must be realloc of something to something else

    NetpAssert( POINTER_IS_ALIGNED( NewPointer, ALIGN_WORST) );

    return (NewPointer);

} // MIDL_user_reallocate


unsigned long
MIDL_user_size(
    IN void * Pointer
    )
{
    DWORD ByteCount;
    HANDLE hMemory;

    NetpAssert( Pointer != NULL );
    NetpAssert( POINTER_IS_ALIGNED( Pointer, ALIGN_WORST ) );

    hMemory = LocalHandle( (LPSTR) Pointer );
    NetpAssert( hMemory != NULL );

    ByteCount = LocalSize( hMemory );

    NetpAssert( ByteCount > 0 );

    return (ByteCount);

} // MIDL_user_size
