/*
 * NDR data marshalling
 *
 * Copyright 2002 Greg Turner
 * Copyright 2003-2006 CodeWeavers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 *
 * TODO:
 *  - String structs
 *  - Byte count pointers
 *  - transmit_as/represent as
 *  - Multi-dimensional arrays
 *  - Conversion functions (NdrConvert)
 *  - Checks for integer addition overflow in user marshall functions
 */

#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <limits.h>

#include "windef.h"
#include "winbase.h"
#include "winerror.h"

#include "ndr_misc.h"
#include "rpcndr.h"

#include "wine/unicode.h"
#include "wine/rpcfc.h"

#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(ole);

#if defined(__i386__)
# define LITTLE_ENDIAN_UINT32_WRITE(pchar, uint32) \
    (*((UINT32 *)(pchar)) = (uint32))

# define LITTLE_ENDIAN_UINT32_READ(pchar) \
    (*((UINT32 *)(pchar)))
#else
  /* these would work for i386 too, but less efficient */
# define LITTLE_ENDIAN_UINT32_WRITE(pchar, uint32) \
    (*(pchar)     = LOBYTE(LOWORD(uint32)), \
     *((pchar)+1) = HIBYTE(LOWORD(uint32)), \
     *((pchar)+2) = LOBYTE(HIWORD(uint32)), \
     *((pchar)+3) = HIBYTE(HIWORD(uint32)), \
     (uint32)) /* allow as r-value */

# define LITTLE_ENDIAN_UINT32_READ(pchar) \
    (MAKELONG( \
      MAKEWORD(*(pchar), *((pchar)+1)), \
      MAKEWORD(*((pchar)+2), *((pchar)+3))))
#endif

#define BIG_ENDIAN_UINT32_WRITE(pchar, uint32) \
  (*((pchar)+3) = LOBYTE(LOWORD(uint32)), \
   *((pchar)+2) = HIBYTE(LOWORD(uint32)), \
   *((pchar)+1) = LOBYTE(HIWORD(uint32)), \
   *(pchar)     = HIBYTE(HIWORD(uint32)), \
   (uint32)) /* allow as r-value */

#define BIG_ENDIAN_UINT32_READ(pchar) \
  (MAKELONG( \
    MAKEWORD(*((pchar)+3), *((pchar)+2)), \
    MAKEWORD(*((pchar)+1), *(pchar))))

#ifdef NDR_LOCAL_IS_BIG_ENDIAN
# define NDR_LOCAL_UINT32_WRITE(pchar, uint32) \
    BIG_ENDIAN_UINT32_WRITE(pchar, uint32)
# define NDR_LOCAL_UINT32_READ(pchar) \
    BIG_ENDIAN_UINT32_READ(pchar)
#else
# define NDR_LOCAL_UINT32_WRITE(pchar, uint32) \
    LITTLE_ENDIAN_UINT32_WRITE(pchar, uint32)
# define NDR_LOCAL_UINT32_READ(pchar) \
    LITTLE_ENDIAN_UINT32_READ(pchar)
#endif

/* _Align must be the desired alignment,
 * e.g. ALIGN_LENGTH(len, 4) to align on a dword boundary. */
#define ALIGNED_LENGTH(_Len, _Align) (((_Len)+(_Align)-1)&~((_Align)-1))
#define ALIGNED_POINTER(_Ptr, _Align) ((LPVOID)ALIGNED_LENGTH((ULONG_PTR)(_Ptr), _Align))
#define ALIGN_LENGTH(_Len, _Align) _Len = ALIGNED_LENGTH(_Len, _Align)
#define ALIGN_POINTER(_Ptr, _Align) _Ptr = ALIGNED_POINTER(_Ptr, _Align)
#define ALIGN_POINTER_CLEAR(_Ptr, _Align) \
    do { \
        memset((_Ptr), 0, ((_Align) - (ULONG_PTR)(_Ptr)) & ((_Align) - 1)); \
        ALIGN_POINTER(_Ptr, _Align); \
    } while(0)

#define STD_OVERFLOW_CHECK(_Msg) do { \
    TRACE("buffer=%d/%d\n", _Msg->Buffer - (unsigned char *)_Msg->RpcMsg->Buffer, _Msg->BufferLength); \
    if (_Msg->Buffer > (unsigned char *)_Msg->RpcMsg->Buffer + _Msg->BufferLength) \
        ERR("buffer overflow %d bytes\n", _Msg->Buffer - ((unsigned char *)_Msg->RpcMsg->Buffer + _Msg->BufferLength)); \
  } while (0)

#define NDR_POINTER_ID_BASE 0x20000
#define NDR_POINTER_ID(pStubMsg) (NDR_POINTER_ID_BASE + ((pStubMsg)->UniquePtrCount++) * 4)
#define NDR_TABLE_SIZE 128
#define NDR_TABLE_MASK 127

static unsigned char *WINAPI NdrBaseTypeMarshall(PMIDL_STUB_MESSAGE, unsigned char *, PFORMAT_STRING);
static unsigned char *WINAPI NdrBaseTypeUnmarshall(PMIDL_STUB_MESSAGE, unsigned char **, PFORMAT_STRING, unsigned char);
static void WINAPI NdrBaseTypeBufferSize(PMIDL_STUB_MESSAGE, unsigned char *, PFORMAT_STRING);
static void WINAPI NdrBaseTypeFree(PMIDL_STUB_MESSAGE, unsigned char *, PFORMAT_STRING);
static ULONG WINAPI NdrBaseTypeMemorySize(PMIDL_STUB_MESSAGE, PFORMAT_STRING);

static unsigned char *WINAPI NdrContextHandleMarshall(PMIDL_STUB_MESSAGE, unsigned char *, PFORMAT_STRING);
static void WINAPI NdrContextHandleBufferSize(PMIDL_STUB_MESSAGE, unsigned char *, PFORMAT_STRING);
static unsigned char *WINAPI NdrContextHandleUnmarshall(PMIDL_STUB_MESSAGE, unsigned char **, PFORMAT_STRING, unsigned char);

const NDR_MARSHALL NdrMarshaller[NDR_TABLE_SIZE] = {
  0,
  NdrBaseTypeMarshall, NdrBaseTypeMarshall, NdrBaseTypeMarshall,
  NdrBaseTypeMarshall, NdrBaseTypeMarshall, NdrBaseTypeMarshall, NdrBaseTypeMarshall,
  NdrBaseTypeMarshall, NdrBaseTypeMarshall, NdrBaseTypeMarshall, NdrBaseTypeMarshall,
  NdrBaseTypeMarshall, NdrBaseTypeMarshall, NdrBaseTypeMarshall, NdrBaseTypeMarshall,
  /* 0x10 */
  NdrBaseTypeMarshall,
  /* 0x11 */
  NdrPointerMarshall, NdrPointerMarshall,
  NdrPointerMarshall, NdrPointerMarshall,
  /* 0x15 */
  NdrSimpleStructMarshall, NdrSimpleStructMarshall,
  NdrConformantStructMarshall, NdrConformantStructMarshall,
  NdrConformantVaryingStructMarshall,
  NdrComplexStructMarshall,
  /* 0x1b */
  NdrConformantArrayMarshall, 
  NdrConformantVaryingArrayMarshall,
  NdrFixedArrayMarshall, NdrFixedArrayMarshall,
  NdrVaryingArrayMarshall, NdrVaryingArrayMarshall,
  NdrComplexArrayMarshall,
  /* 0x22 */
  NdrConformantStringMarshall, 0, 0,
  NdrConformantStringMarshall,
  NdrNonConformantStringMarshall, 0, 0, 0,
  /* 0x2a */
  NdrEncapsulatedUnionMarshall,
  NdrNonEncapsulatedUnionMarshall,
  NdrByteCountPointerMarshall,
  NdrXmitOrRepAsMarshall, NdrXmitOrRepAsMarshall,
  /* 0x2f */
  NdrInterfacePointerMarshall,
  /* 0x30 */
  NdrContextHandleMarshall,
  /* 0xb1 */
  0, 0, 0,
  NdrUserMarshalMarshall,
  0, 0,
  /* 0xb7 */
  NdrRangeMarshall
};
const NDR_UNMARSHALL NdrUnmarshaller[NDR_TABLE_SIZE] = {
  0,
  NdrBaseTypeUnmarshall, NdrBaseTypeUnmarshall, NdrBaseTypeUnmarshall,
  NdrBaseTypeUnmarshall, NdrBaseTypeUnmarshall, NdrBaseTypeUnmarshall, NdrBaseTypeUnmarshall,
  NdrBaseTypeUnmarshall, NdrBaseTypeUnmarshall, NdrBaseTypeUnmarshall, NdrBaseTypeUnmarshall,
  NdrBaseTypeUnmarshall, NdrBaseTypeUnmarshall, NdrBaseTypeUnmarshall, NdrBaseTypeUnmarshall,
  /* 0x10 */
  NdrBaseTypeUnmarshall,
  /* 0x11 */
  NdrPointerUnmarshall, NdrPointerUnmarshall,
  NdrPointerUnmarshall, NdrPointerUnmarshall,
  /* 0x15 */
  NdrSimpleStructUnmarshall, NdrSimpleStructUnmarshall,
  NdrConformantStructUnmarshall, NdrConformantStructUnmarshall,
  NdrConformantVaryingStructUnmarshall,
  NdrComplexStructUnmarshall,
  /* 0x1b */
  NdrConformantArrayUnmarshall, 
  NdrConformantVaryingArrayUnmarshall,
  NdrFixedArrayUnmarshall, NdrFixedArrayUnmarshall,
  NdrVaryingArrayUnmarshall, NdrVaryingArrayUnmarshall,
  NdrComplexArrayUnmarshall,
  /* 0x22 */
  NdrConformantStringUnmarshall, 0, 0,
  NdrConformantStringUnmarshall,
  NdrNonConformantStringUnmarshall, 0, 0, 0,
  /* 0x2a */
  NdrEncapsulatedUnionUnmarshall,
  NdrNonEncapsulatedUnionUnmarshall,
  NdrByteCountPointerUnmarshall,
  NdrXmitOrRepAsUnmarshall, NdrXmitOrRepAsUnmarshall,
  /* 0x2f */
  NdrInterfacePointerUnmarshall,
  /* 0x30 */
  NdrContextHandleUnmarshall,
  /* 0xb1 */
  0, 0, 0,
  NdrUserMarshalUnmarshall,
  0, 0,
  /* 0xb7 */
  NdrRangeUnmarshall
};
const NDR_BUFFERSIZE NdrBufferSizer[NDR_TABLE_SIZE] = {
  0,
  NdrBaseTypeBufferSize, NdrBaseTypeBufferSize, NdrBaseTypeBufferSize,
  NdrBaseTypeBufferSize, NdrBaseTypeBufferSize, NdrBaseTypeBufferSize, NdrBaseTypeBufferSize,
  NdrBaseTypeBufferSize, NdrBaseTypeBufferSize, NdrBaseTypeBufferSize, NdrBaseTypeBufferSize,
  NdrBaseTypeBufferSize, NdrBaseTypeBufferSize, NdrBaseTypeBufferSize, NdrBaseTypeBufferSize,
  /* 0x10 */
  NdrBaseTypeBufferSize,
  /* 0x11 */
  NdrPointerBufferSize, NdrPointerBufferSize,
  NdrPointerBufferSize, NdrPointerBufferSize,
  /* 0x15 */
  NdrSimpleStructBufferSize, NdrSimpleStructBufferSize,
  NdrConformantStructBufferSize, NdrConformantStructBufferSize,
  NdrConformantVaryingStructBufferSize,
  NdrComplexStructBufferSize,
  /* 0x1b */
  NdrConformantArrayBufferSize, 
  NdrConformantVaryingArrayBufferSize,
  NdrFixedArrayBufferSize, NdrFixedArrayBufferSize,
  NdrVaryingArrayBufferSize, NdrVaryingArrayBufferSize,
  NdrComplexArrayBufferSize,
  /* 0x22 */
  NdrConformantStringBufferSize, 0, 0,
  NdrConformantStringBufferSize,
  NdrNonConformantStringBufferSize, 0, 0, 0,
  /* 0x2a */
  NdrEncapsulatedUnionBufferSize,
  NdrNonEncapsulatedUnionBufferSize,
  NdrByteCountPointerBufferSize,
  NdrXmitOrRepAsBufferSize, NdrXmitOrRepAsBufferSize,
  /* 0x2f */
  NdrInterfacePointerBufferSize,
  /* 0x30 */
  NdrContextHandleBufferSize,
  /* 0xb1 */
  0, 0, 0,
  NdrUserMarshalBufferSize,
  0, 0,
  /* 0xb7 */
  NdrRangeBufferSize
};
const NDR_MEMORYSIZE NdrMemorySizer[NDR_TABLE_SIZE] = {
  0,
  NdrBaseTypeMemorySize, NdrBaseTypeMemorySize, NdrBaseTypeMemorySize,
  NdrBaseTypeMemorySize, NdrBaseTypeMemorySize, NdrBaseTypeMemorySize, NdrBaseTypeMemorySize,
  NdrBaseTypeMemorySize, NdrBaseTypeMemorySize, NdrBaseTypeMemorySize, NdrBaseTypeMemorySize,
  NdrBaseTypeMemorySize, NdrBaseTypeMemorySize, NdrBaseTypeMemorySize, NdrBaseTypeMemorySize,
  /* 0x10 */
  NdrBaseTypeMemorySize,
  /* 0x11 */
  NdrPointerMemorySize, NdrPointerMemorySize,
  NdrPointerMemorySize, NdrPointerMemorySize,
  /* 0x15 */
  NdrSimpleStructMemorySize, NdrSimpleStructMemorySize,
  NdrConformantStructMemorySize, NdrConformantStructMemorySize,
  NdrConformantVaryingStructMemorySize,
  NdrComplexStructMemorySize,
  /* 0x1b */
  NdrConformantArrayMemorySize,
  NdrConformantVaryingArrayMemorySize,
  NdrFixedArrayMemorySize, NdrFixedArrayMemorySize,
  NdrVaryingArrayMemorySize, NdrVaryingArrayMemorySize,
  NdrComplexArrayMemorySize,
  /* 0x22 */
  NdrConformantStringMemorySize, 0, 0,
  NdrConformantStringMemorySize,
  NdrNonConformantStringMemorySize, 0, 0, 0,
  /* 0x2a */
  NdrEncapsulatedUnionMemorySize,
  NdrNonEncapsulatedUnionMemorySize,
  NdrByteCountPointerMemorySize,
  NdrXmitOrRepAsMemorySize, NdrXmitOrRepAsMemorySize,
  /* 0x2f */
  NdrInterfacePointerMemorySize,
  /* 0x30 */
  0,
  /* 0xb1 */
  0, 0, 0,
  NdrUserMarshalMemorySize,
  0, 0,
  /* 0xb7 */
  NdrRangeMemorySize
};
const NDR_FREE NdrFreer[NDR_TABLE_SIZE] = {
  0,
  NdrBaseTypeFree, NdrBaseTypeFree, NdrBaseTypeFree,
  NdrBaseTypeFree, NdrBaseTypeFree, NdrBaseTypeFree, NdrBaseTypeFree,
  NdrBaseTypeFree, NdrBaseTypeFree, NdrBaseTypeFree, NdrBaseTypeFree,
  NdrBaseTypeFree, NdrBaseTypeFree, NdrBaseTypeFree, NdrBaseTypeFree,
  /* 0x10 */
  NdrBaseTypeFree,
  /* 0x11 */
  NdrPointerFree, NdrPointerFree,
  NdrPointerFree, NdrPointerFree,
  /* 0x15 */
  NdrSimpleStructFree, NdrSimpleStructFree,
  NdrConformantStructFree, NdrConformantStructFree,
  NdrConformantVaryingStructFree,
  NdrComplexStructFree,
  /* 0x1b */
  NdrConformantArrayFree, 
  NdrConformantVaryingArrayFree,
  NdrFixedArrayFree, NdrFixedArrayFree,
  NdrVaryingArrayFree, NdrVaryingArrayFree,
  NdrComplexArrayFree,
  /* 0x22 */
  0, 0, 0,
  0, 0, 0, 0, 0,
  /* 0x2a */
  NdrEncapsulatedUnionFree,
  NdrNonEncapsulatedUnionFree,
  0,
  NdrXmitOrRepAsFree, NdrXmitOrRepAsFree,
  /* 0x2f */
  NdrInterfacePointerFree,
  /* 0x30 */
  0,
  /* 0xb1 */
  0, 0, 0,
  NdrUserMarshalFree,
  0, 0,
  /* 0xb7 */
  NdrRangeFree
};

typedef struct _NDR_MEMORY_LIST
{
    ULONG magic;
    ULONG size;
    ULONG reserved;
    struct _NDR_MEMORY_LIST *next;
} NDR_MEMORY_LIST;

#define MEML_MAGIC  ('M' << 24 | 'E' << 16 | 'M' << 8 | 'L')

/***********************************************************************
 *            NdrAllocate [RPCRT4.@]
 *
 * Allocates a block of memory using pStubMsg->pfnAllocate.
 *
 * PARAMS
 *  pStubMsg [I/O] MIDL_STUB_MESSAGE structure.
 *  len      [I]   Size of memory block to allocate.
 *
 * RETURNS
 *  The memory block of size len that was allocated.
 *
 * NOTES
 *  The memory block is always 8-byte aligned.
 *  If the function is unable to allocate memory an ERROR_OUTOFMEMORY
 *  exception is raised.
 */
void * WINAPI NdrAllocate(MIDL_STUB_MESSAGE *pStubMsg, size_t len)
{
    size_t aligned_len;
    size_t adjusted_len;
    void *p;
    NDR_MEMORY_LIST *mem_list;

    aligned_len = ALIGNED_LENGTH(len, 8);
    adjusted_len = aligned_len + sizeof(NDR_MEMORY_LIST);
    /* check for overflow */
    if (adjusted_len < len)
    {
        ERR("overflow of adjusted_len %d, len %d\n", adjusted_len, len);
        RpcRaiseException(RPC_X_BAD_STUB_DATA);
    }

    p = pStubMsg->pfnAllocate(adjusted_len);
    if (!p) RpcRaiseException(ERROR_OUTOFMEMORY);

    mem_list = (NDR_MEMORY_LIST *)((char *)p + aligned_len);
    mem_list->magic = MEML_MAGIC;
    mem_list->size = aligned_len;
    mem_list->reserved = 0;
    mem_list->next = pStubMsg->pMemoryList;
    pStubMsg->pMemoryList = mem_list;

    TRACE("-- %p\n", p);
    return p;
}

static void WINAPI NdrFree(MIDL_STUB_MESSAGE *pStubMsg, unsigned char *Pointer)
{
    TRACE("(%p, %p)\n", pStubMsg, Pointer);

    pStubMsg->pfnFree(Pointer);
}

static inline BOOL IsConformanceOrVariancePresent(PFORMAT_STRING pFormat)
{
    return (*(const ULONG *)pFormat != -1);
}

static PFORMAT_STRING ReadConformance(MIDL_STUB_MESSAGE *pStubMsg, PFORMAT_STRING pFormat)
{
  ALIGN_POINTER(pStubMsg->Buffer, 4);
  if (pStubMsg->Buffer + 4 > pStubMsg->BufferEnd)
      RpcRaiseException(RPC_X_BAD_STUB_DATA);
  pStubMsg->MaxCount = NDR_LOCAL_UINT32_READ(pStubMsg->Buffer);
  pStubMsg->Buffer += 4;
  TRACE("unmarshalled conformance is %ld\n", pStubMsg->MaxCount);
  if (pStubMsg->fHasNewCorrDesc)
    return pFormat+6;
  else
    return pFormat+4;
}

static inline PFORMAT_STRING ReadVariance(MIDL_STUB_MESSAGE *pStubMsg, PFORMAT_STRING pFormat, ULONG MaxValue)
{
  if (pFormat && !IsConformanceOrVariancePresent(pFormat))
  {
    pStubMsg->Offset = 0;
    pStubMsg->ActualCount = pStubMsg->MaxCount;
    goto done;
  }

  ALIGN_POINTER(pStubMsg->Buffer, 4);
  if (pStubMsg->Buffer + 8 > pStubMsg->BufferEnd)
    RpcRaiseException(RPC_X_BAD_STUB_DATA);
  pStubMsg->Offset      = NDR_LOCAL_UINT32_READ(pStubMsg->Buffer);
  pStubMsg->Buffer += 4;
  TRACE("offset is %d\n", pStubMsg->Offset);
  pStubMsg->ActualCount = NDR_LOCAL_UINT32_READ(pStubMsg->Buffer);
  pStubMsg->Buffer += 4;
  TRACE("variance is %d\n", pStubMsg->ActualCount);

  if ((pStubMsg->ActualCount > MaxValue) ||
      (pStubMsg->ActualCount + pStubMsg->Offset > MaxValue))
  {
    ERR("invalid array bound(s): ActualCount = %d, Offset = %d, MaxValue = %d\n",
        pStubMsg->ActualCount, pStubMsg->Offset, MaxValue);
    RpcRaiseException(RPC_S_INVALID_BOUND);
    return NULL;
  }

done:
  if (pStubMsg->fHasNewCorrDesc)
    return pFormat+6;
  else
    return pFormat+4;
}

/* writes the conformance value to the buffer */
static inline void WriteConformance(MIDL_STUB_MESSAGE *pStubMsg)
{
    ALIGN_POINTER_CLEAR(pStubMsg->Buffer, 4);
    if (pStubMsg->Buffer + 4 > (unsigned char *)pStubMsg->RpcMsg->Buffer + pStubMsg->BufferLength)
        RpcRaiseException(RPC_X_BAD_STUB_DATA);
    NDR_LOCAL_UINT32_WRITE(pStubMsg->Buffer, pStubMsg->MaxCount);
    pStubMsg->Buffer += 4;
}

/* writes the variance values to the buffer */
static inline void WriteVariance(MIDL_STUB_MESSAGE *pStubMsg)
{
    ALIGN_POINTER_CLEAR(pStubMsg->Buffer, 4);
    if (pStubMsg->Buffer + 8 > (unsigned char *)pStubMsg->RpcMsg->Buffer + pStubMsg->BufferLength)
        RpcRaiseException(RPC_X_BAD_STUB_DATA);
    NDR_LOCAL_UINT32_WRITE(pStubMsg->Buffer, pStubMsg->Offset);
    pStubMsg->Buffer += 4;
    NDR_LOCAL_UINT32_WRITE(pStubMsg->Buffer, pStubMsg->ActualCount);
    pStubMsg->Buffer += 4;
}

/* requests buffer space for the conformance value */
static inline void SizeConformance(MIDL_STUB_MESSAGE *pStubMsg)
{
    ALIGN_LENGTH(pStubMsg->BufferLength, 4);
    if (pStubMsg->BufferLength + 4 < pStubMsg->BufferLength)
        RpcRaiseException(RPC_X_BAD_STUB_DATA);
    pStubMsg->BufferLength += 4;
}

/* requests buffer space for the variance values */
static inline void SizeVariance(MIDL_STUB_MESSAGE *pStubMsg)
{
    ALIGN_LENGTH(pStubMsg->BufferLength, 4);
    if (pStubMsg->BufferLength + 8 < pStubMsg->BufferLength)
        RpcRaiseException(RPC_X_BAD_STUB_DATA);
    pStubMsg->BufferLength += 8;
}

PFORMAT_STRING ComputeConformanceOrVariance(
    MIDL_STUB_MESSAGE *pStubMsg, unsigned char *pMemory,
    PFORMAT_STRING pFormat, ULONG_PTR def, ULONG_PTR *pCount)
{
  BYTE dtype = pFormat[0] & 0xf;
  short ofs = *(const short *)&pFormat[2];
  LPVOID ptr = NULL;
  DWORD data = 0;

  if (!IsConformanceOrVariancePresent(pFormat)) {
    /* null descriptor */
    *pCount = def;
    goto finish_conf;
  }

  switch (pFormat[0] & 0xf0) {
  case RPC_FC_NORMAL_CONFORMANCE:
    TRACE("normal conformance, ofs=%d\n", ofs);
    ptr = pMemory;
    break;
  case RPC_FC_POINTER_CONFORMANCE:
    TRACE("pointer conformance, ofs=%d\n", ofs);
    ptr = pStubMsg->Memory;
    break;
  case RPC_FC_TOP_LEVEL_CONFORMANCE:
    TRACE("toplevel conformance, ofs=%d\n", ofs);
    if (pStubMsg->StackTop) {
      ptr = pStubMsg->StackTop;
    }
    else {
      /* -Os mode, *pCount is already set */
      goto finish_conf;
    }
    break;
  case RPC_FC_CONSTANT_CONFORMANCE:
    data = ofs | ((DWORD)pFormat[1] << 16);
    TRACE("constant conformance, val=%d\n", data);
    *pCount = data;
    goto finish_conf;
  case RPC_FC_TOP_LEVEL_MULTID_CONFORMANCE:
    FIXME("toplevel multidimensional conformance, ofs=%d\n", ofs);
    if (pStubMsg->StackTop) {
      ptr = pStubMsg->StackTop;
    }
    else {
      /* ? */
      goto done_conf_grab;
    }
    break;
  default:
    FIXME("unknown conformance type %x\n", pFormat[0] & 0xf0);
  }

  switch (pFormat[1]) {
  case RPC_FC_DEREFERENCE:
    ptr = *(LPVOID*)((char *)ptr + ofs);
    break;
  case RPC_FC_CALLBACK:
  {
    unsigned char *old_stack_top = pStubMsg->StackTop;
    pStubMsg->StackTop = ptr;

    /* ofs is index into StubDesc->apfnExprEval */
    TRACE("callback conformance into apfnExprEval[%d]\n", ofs);
    pStubMsg->StubDesc->apfnExprEval[ofs](pStubMsg);

    pStubMsg->StackTop = old_stack_top;

    /* the callback function always stores the computed value in MaxCount */
    *pCount = pStubMsg->MaxCount;
    goto finish_conf;
  }
  default:
    ptr = (char *)ptr + ofs;
    break;
  }

  switch (dtype) {
  case RPC_FC_LONG:
  case RPC_FC_ULONG:
    data = *(DWORD*)ptr;
    break;
  case RPC_FC_SHORT:
    data = *(SHORT*)ptr;
    break;
  case RPC_FC_USHORT:
    data = *(USHORT*)ptr;
    break;
  case RPC_FC_CHAR:
  case RPC_FC_SMALL:
    data = *(CHAR*)ptr;
    break;
  case RPC_FC_BYTE:
  case RPC_FC_USMALL:
    data = *(UCHAR*)ptr;
    break;
  default:
    FIXME("unknown conformance data type %x\n", dtype);
    goto done_conf_grab;
  }
  TRACE("dereferenced data type %x at %p, got %d\n", dtype, ptr, data);

done_conf_grab:
  switch (pFormat[1]) {
  case RPC_FC_DEREFERENCE: /* already handled */
  case 0: /* no op */
    *pCount = data;
    break;
  case RPC_FC_ADD_1:
    *pCount = data + 1;
    break;
  case RPC_FC_SUB_1:
    *pCount = data - 1;
    break;
  case RPC_FC_MULT_2:
    *pCount = data * 2;
    break;
  case RPC_FC_DIV_2:
    *pCount = data / 2;
    break;
  default:
    FIXME("unknown conformance op %d\n", pFormat[1]);
    goto finish_conf;
  }

finish_conf:
  TRACE("resulting conformance is %ld\n", *pCount);
  if (pStubMsg->fHasNewCorrDesc)
    return pFormat+6;
  else
    return pFormat+4;
}

/* multiply two numbers together, raising an RPC_S_INVALID_BOUND exception if
 * the result overflows 32-bits */
static inline ULONG safe_multiply(ULONG a, ULONG b)
{
    ULONGLONG ret = (ULONGLONG)a * b;
    if (ret > 0xffffffff)
    {
        RpcRaiseException(RPC_S_INVALID_BOUND);
        return 0;
    }
    return ret;
}

static inline void safe_buffer_increment(MIDL_STUB_MESSAGE *pStubMsg, ULONG size)
{
    if ((pStubMsg->Buffer + size < pStubMsg->Buffer) || /* integer overflow of pStubMsg->Buffer */
        (pStubMsg->Buffer + size > (unsigned char *)pStubMsg->RpcMsg->Buffer + pStubMsg->BufferLength))
        RpcRaiseException(RPC_X_BAD_STUB_DATA);
    pStubMsg->Buffer += size;
}

static inline void safe_buffer_length_increment(MIDL_STUB_MESSAGE *pStubMsg, ULONG size)
{
    if (pStubMsg->BufferLength + size < pStubMsg->BufferLength) /* integer overflow of pStubMsg->BufferSize */
    {
        ERR("buffer length overflow - BufferLength = %u, size = %u\n",
            pStubMsg->BufferLength, size);
        RpcRaiseException(RPC_X_BAD_STUB_DATA);
    }
    pStubMsg->BufferLength += size;
}

/* copies data from the buffer, checking that there is enough data in the buffer
 * to do so */
static inline void safe_copy_from_buffer(MIDL_STUB_MESSAGE *pStubMsg, void *p, ULONG size)
{
    if ((pStubMsg->Buffer + size < pStubMsg->Buffer) || /* integer overflow of pStubMsg->Buffer */
        (pStubMsg->Buffer + size > pStubMsg->BufferEnd))
    {
        ERR("buffer overflow - Buffer = %p, BufferEnd = %p, size = %u\n",
            pStubMsg->Buffer, pStubMsg->BufferEnd, size);
        RpcRaiseException(RPC_X_BAD_STUB_DATA);
    }
    if (p == pStubMsg->Buffer)
        ERR("pointer is the same as the buffer\n");
    memcpy(p, pStubMsg->Buffer, size);
    pStubMsg->Buffer += size;
}

/* copies data to the buffer, checking that there is enough space to do so */
static inline void safe_copy_to_buffer(MIDL_STUB_MESSAGE *pStubMsg, const void *p, ULONG size)
{
    if ((pStubMsg->Buffer + size < pStubMsg->Buffer) || /* integer overflow of pStubMsg->Buffer */
        (pStubMsg->Buffer + size > (unsigned char *)pStubMsg->RpcMsg->Buffer + pStubMsg->BufferLength))
    {
        ERR("buffer overflow - Buffer = %p, BufferEnd = %p, size = %u\n",
            pStubMsg->Buffer, (unsigned char *)pStubMsg->RpcMsg->Buffer + pStubMsg->BufferLength,
            size);
        RpcRaiseException(RPC_X_BAD_STUB_DATA);
    }
    memcpy(pStubMsg->Buffer, p, size);
    pStubMsg->Buffer += size;
}

/* verify that string data sitting in the buffer is valid and safe to
 * unmarshall */
static void validate_string_data(MIDL_STUB_MESSAGE *pStubMsg, ULONG bufsize, ULONG esize)
{
    ULONG i;

    /* verify the buffer is safe to access */
    if ((pStubMsg->Buffer + bufsize < pStubMsg->Buffer) ||
        (pStubMsg->Buffer + bufsize > pStubMsg->BufferEnd))
    {
        ERR("bufsize 0x%x exceeded buffer end %p of buffer %p\n", bufsize,
            pStubMsg->BufferEnd, pStubMsg->Buffer);
        RpcRaiseException(RPC_X_BAD_STUB_DATA);
    }

    /* strings must always have null terminating bytes */
    if (bufsize < esize)
    {
        ERR("invalid string length of %d\n", bufsize / esize);
        RpcRaiseException(RPC_S_INVALID_BOUND);
    }

    for (i = bufsize - esize; i < bufsize; i++)
        if (pStubMsg->Buffer[i] != 0)
        {
            ERR("string not null-terminated at byte position %d, data is 0x%x\n",
                i, pStubMsg->Buffer[i]);
            RpcRaiseException(RPC_S_INVALID_BOUND);
        }
}

/*
 * NdrConformantString:
 * 
 * What MS calls a ConformantString is, in DCE terminology,
 * a Varying-Conformant String.
 * [
 *   maxlen: DWORD (max # of CHARTYPE characters, inclusive of '\0')
 *   offset: DWORD (actual string data begins at (offset) CHARTYPE's
 *           into unmarshalled string) 
 *   length: DWORD (# of CHARTYPE characters, inclusive of '\0')
 *   [ 
 *     data: CHARTYPE[maxlen]
 *   ] 
 * ], where CHARTYPE is the appropriate character type (specified externally)
 *
 */

/***********************************************************************
 *            NdrConformantStringMarshall [RPCRT4.@]
 */
unsigned char *WINAPI NdrConformantStringMarshall(MIDL_STUB_MESSAGE *pStubMsg,
  unsigned char *pszMessage, PFORMAT_STRING pFormat)
{ 
  ULONG esize, size;

  TRACE("(pStubMsg == ^%p, pszMessage == ^%p, pFormat == ^%p)\n", pStubMsg, pszMessage, pFormat);
  
  if (*pFormat == RPC_FC_C_CSTRING) {
    TRACE("string=%s\n", debugstr_a((char*)pszMessage));
    pStubMsg->ActualCount = strlen((char*)pszMessage)+1;
    esize = 1;
  }
  else if (*pFormat == RPC_FC_C_WSTRING) {
    TRACE("string=%s\n", debugstr_w((LPWSTR)pszMessage));
    pStubMsg->ActualCount = strlenW((LPWSTR)pszMessage)+1;
    esize = 2;
  }
  else {
    ERR("Unhandled string type: %#x\n", *pFormat); 
    /* FIXME: raise an exception. */
    return NULL;
  }

  if (pFormat[1] == RPC_FC_STRING_SIZED)
    pFormat = ComputeConformance(pStubMsg, pszMessage, pFormat + 2, 0);
  else
    pStubMsg->MaxCount = pStubMsg->ActualCount;
  pStubMsg->Offset = 0;
  WriteConformance(pStubMsg);
  WriteVariance(pStubMsg);

  size = safe_multiply(esize, pStubMsg->ActualCount);
  safe_copy_to_buffer(pStubMsg, pszMessage, size); /* the string itself */

  /* success */
  return NULL; /* is this always right? */
}

/***********************************************************************
 *           NdrConformantStringBufferSize [RPCRT4.@]
 */
void WINAPI NdrConformantStringBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
  unsigned char* pMemory, PFORMAT_STRING pFormat)
{
  ULONG esize;

  TRACE("(pStubMsg == ^%p, pMemory == ^%p, pFormat == ^%p)\n", pStubMsg, pMemory, pFormat);

  SizeConformance(pStubMsg);
  SizeVariance(pStubMsg);

  if (*pFormat == RPC_FC_C_CSTRING) {
    TRACE("string=%s\n", debugstr_a((char*)pMemory));
    pStubMsg->ActualCount = strlen((char*)pMemory)+1;
    esize = 1;
  }
  else if (*pFormat == RPC_FC_C_WSTRING) {
    TRACE("string=%s\n", debugstr_w((LPWSTR)pMemory));
    pStubMsg->ActualCount = strlenW((LPWSTR)pMemory)+1;
    esize = 2;
  }
  else {
    ERR("Unhandled string type: %#x\n", *pFormat); 
    /* FIXME: raise an exception */
    return;
  }

  if (pFormat[1] == RPC_FC_STRING_SIZED)
    pFormat = ComputeConformance(pStubMsg, pMemory, pFormat + 2, 0);
  else
    pStubMsg->MaxCount = pStubMsg->ActualCount;

  safe_buffer_length_increment(pStubMsg, safe_multiply(esize, pStubMsg->ActualCount));
}

/************************************************************************
 *            NdrConformantStringMemorySize [RPCRT4.@]
 */
ULONG WINAPI NdrConformantStringMemorySize( PMIDL_STUB_MESSAGE pStubMsg,
  PFORMAT_STRING pFormat )
{
  ULONG bufsize, memsize, esize;

  TRACE("(pStubMsg == ^%p, pFormat == ^%p)\n", pStubMsg, pFormat);

  ReadConformance(pStubMsg, NULL);
  ReadVariance(pStubMsg, NULL, pStubMsg->MaxCount);

  if (pFormat[1] != RPC_FC_STRING_SIZED && (pStubMsg->MaxCount != pStubMsg->ActualCount))
  {
    ERR("buffer size %d must equal memory size %ld for non-sized conformant strings\n",
        pStubMsg->ActualCount, pStubMsg->MaxCount);
    RpcRaiseException(RPC_S_INVALID_BOUND);
  }
  if (pStubMsg->Offset)
  {
    ERR("conformant strings can't have Offset (%d)\n", pStubMsg->Offset);
    RpcRaiseException(RPC_S_INVALID_BOUND);
  }

  if (*pFormat == RPC_FC_C_CSTRING) esize = 1;
  else if (*pFormat == RPC_FC_C_WSTRING) esize = 2;
  else {
    ERR("Unhandled string type: %#x\n", *pFormat);
    /* FIXME: raise an exception */
    esize = 0;
  }

  memsize = safe_multiply(esize, pStubMsg->MaxCount);
  bufsize = safe_multiply(esize, pStubMsg->ActualCount);

  validate_string_data(pStubMsg, bufsize, esize);

  safe_buffer_increment(pStubMsg, bufsize);
  pStubMsg->MemorySize += memsize;

  return pStubMsg->MemorySize;
}

/************************************************************************
 *           NdrConformantStringUnmarshall [RPCRT4.@]
 */
unsigned char *WINAPI NdrConformantStringUnmarshall( PMIDL_STUB_MESSAGE pStubMsg,
  unsigned char** ppMemory, PFORMAT_STRING pFormat, unsigned char fMustAlloc )
{
  ULONG bufsize, memsize, esize;

  TRACE("(pStubMsg == ^%p, *pMemory == ^%p, pFormat == ^%p, fMustAlloc == %u)\n",
    pStubMsg, *ppMemory, pFormat, fMustAlloc);

  assert(pFormat && ppMemory && pStubMsg);

  ReadConformance(pStubMsg, NULL);
  ReadVariance(pStubMsg, NULL, pStubMsg->MaxCount);

  if (pFormat[1] != RPC_FC_STRING_SIZED && (pStubMsg->MaxCount != pStubMsg->ActualCount))
  {
    ERR("buffer size %d must equal memory size %ld for non-sized conformant strings\n",
        pStubMsg->ActualCount, pStubMsg->MaxCount);
    RpcRaiseException(RPC_S_INVALID_BOUND);
    return NULL;
  }
  if (pStubMsg->Offset)
  {
    ERR("conformant strings can't have Offset (%d)\n", pStubMsg->Offset);
    RpcRaiseException(RPC_S_INVALID_BOUND);
    return NULL;
  }

  if (*pFormat == RPC_FC_C_CSTRING) esize = 1;
  else if (*pFormat == RPC_FC_C_WSTRING) esize = 2;
  else {
    ERR("Unhandled string type: %#x\n", *pFormat);
    /* FIXME: raise an exception */
    esize = 0;
  }

  memsize = safe_multiply(esize, pStubMsg->MaxCount);
  bufsize = safe_multiply(esize, pStubMsg->ActualCount);

  validate_string_data(pStubMsg, bufsize, esize);

  if (fMustAlloc)
    *ppMemory = NdrAllocate(pStubMsg, memsize);
  else
  {
    if (!pStubMsg->IsClient && !*ppMemory && (pStubMsg->MaxCount == pStubMsg->ActualCount))
      /* if the data in the RPC buffer is big enough, we just point straight
       * into it */
      *ppMemory = pStubMsg->Buffer;
    else if (!*ppMemory)
      *ppMemory = NdrAllocate(pStubMsg, memsize);
  }

  if (*ppMemory == pStubMsg->Buffer)
    safe_buffer_increment(pStubMsg, bufsize);
  else
    safe_copy_from_buffer(pStubMsg, *ppMemory, bufsize);

  if (*pFormat == RPC_FC_C_CSTRING) {
    TRACE("string=%s\n", debugstr_a((char*)*ppMemory));
  }
  else if (*pFormat == RPC_FC_C_WSTRING) {
    TRACE("string=%s\n", debugstr_w((LPWSTR)*ppMemory));
  }

  return NULL; /* FIXME: is this always right? */
}

/***********************************************************************
 *           NdrNonConformantStringMarshall [RPCRT4.@]
 */
unsigned char *  WINAPI NdrNonConformantStringMarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
  ULONG esize, size, maxsize;

  TRACE("(pStubMsg == ^%p, pMemory == ^%p, pFormat == ^%p)\n", pStubMsg, pMemory, pFormat);

  maxsize = *(USHORT *)&pFormat[2];

  if (*pFormat == RPC_FC_CSTRING)
  {
    ULONG i;
    const char *str = (const char *)pMemory;
    for (i = 0; i < maxsize && *str; i++, str++)
        ;
    TRACE("string=%s\n", debugstr_an(str, i));
    pStubMsg->ActualCount = i + 1;
    esize = 1;
  }
  else if (*pFormat == RPC_FC_WSTRING)
  {
    ULONG i;
    const WCHAR *str = (const WCHAR *)pMemory;
    for (i = 0; i < maxsize && *str; i++, str++)
        ;
    TRACE("string=%s\n", debugstr_wn(str, i));
    pStubMsg->ActualCount = i + 1;
    esize = 2;
  }
  else
  {
    ERR("Unhandled string type: %#x\n", *pFormat);
    RpcRaiseException(RPC_X_BAD_STUB_DATA);
  }

  pStubMsg->Offset = 0;
  WriteVariance(pStubMsg);

  size = safe_multiply(esize, pStubMsg->ActualCount);
  safe_copy_to_buffer(pStubMsg, pMemory, size); /* the string itself */

  return NULL;
}

/***********************************************************************
 *           NdrNonConformantStringUnmarshall [RPCRT4.@]
 */
unsigned char *  WINAPI NdrNonConformantStringUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char **ppMemory,
                                PFORMAT_STRING pFormat,
                                unsigned char fMustAlloc)
{
  ULONG bufsize, memsize, esize, maxsize;

  TRACE("(pStubMsg == ^%p, *pMemory == ^%p, pFormat == ^%p, fMustAlloc == %u)\n",
    pStubMsg, *ppMemory, pFormat, fMustAlloc);

  maxsize = *(USHORT *)&pFormat[2];

  ReadVariance(pStubMsg, NULL, maxsize);
  if (pStubMsg->Offset)
  {
    ERR("non-conformant strings can't have Offset (%d)\n", pStubMsg->Offset);
    RpcRaiseException(RPC_S_INVALID_BOUND);
  }

  if (*pFormat == RPC_FC_CSTRING) esize = 1;
  else if (*pFormat == RPC_FC_WSTRING) esize = 2;
  else
  {
    ERR("Unhandled string type: %#x\n", *pFormat);
    RpcRaiseException(RPC_X_BAD_STUB_DATA);
  }

  memsize = esize * maxsize;
  bufsize = safe_multiply(esize, pStubMsg->ActualCount);

  validate_string_data(pStubMsg, bufsize, esize);

  if (fMustAlloc || !*ppMemory)
    *ppMemory = NdrAllocate(pStubMsg, memsize);

  safe_copy_from_buffer(pStubMsg, *ppMemory, bufsize);

  if (*pFormat == RPC_FC_CSTRING) {
    TRACE("string=%s\n", debugstr_an((char*)*ppMemory, pStubMsg->ActualCount));
  }
  else if (*pFormat == RPC_FC_WSTRING) {
    TRACE("string=%s\n", debugstr_wn((LPWSTR)*ppMemory, pStubMsg->ActualCount));
  }

  return NULL;
}

/***********************************************************************
 *           NdrNonConformantStringBufferSize [RPCRT4.@]
 */
void WINAPI NdrNonConformantStringBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
  ULONG esize, maxsize;

  TRACE("(pStubMsg == ^%p, pMemory == ^%p, pFormat == ^%p)\n", pStubMsg, pMemory, pFormat);

  maxsize = *(USHORT *)&pFormat[2];

  SizeVariance(pStubMsg);

  if (*pFormat == RPC_FC_CSTRING)
  {
    ULONG i;
    const char *str = (const char *)pMemory;
    for (i = 0; i < maxsize && *str; i++, str++)
        ;
    TRACE("string=%s\n", debugstr_an(str, i));
    pStubMsg->ActualCount = i + 1;
    esize = 1;
  }
  else if (*pFormat == RPC_FC_WSTRING)
  {
    ULONG i;
    const WCHAR *str = (const WCHAR *)pMemory;
    for (i = 0; i < maxsize && *str; i++, str++)
        ;
    TRACE("string=%s\n", debugstr_wn(str, i));
    pStubMsg->ActualCount = i + 1;
    esize = 2;
  }
  else
  {
    ERR("Unhandled string type: %#x\n", *pFormat);
    RpcRaiseException(RPC_X_BAD_STUB_DATA);
  }

  safe_buffer_length_increment(pStubMsg, safe_multiply(esize, pStubMsg->ActualCount));
}

/***********************************************************************
 *           NdrNonConformantStringMemorySize [RPCRT4.@]
 */
ULONG WINAPI NdrNonConformantStringMemorySize(PMIDL_STUB_MESSAGE pStubMsg,
                                PFORMAT_STRING pFormat)
{
  ULONG bufsize, memsize, esize, maxsize;

  TRACE("(pStubMsg == ^%p, pFormat == ^%p)\n", pStubMsg, pFormat);

  maxsize = *(USHORT *)&pFormat[2];

  ReadVariance(pStubMsg, NULL, maxsize);

  if (pStubMsg->Offset)
  {
    ERR("non-conformant strings can't have Offset (%d)\n", pStubMsg->Offset);
    RpcRaiseException(RPC_S_INVALID_BOUND);
  }

  if (*pFormat == RPC_FC_CSTRING) esize = 1;
  else if (*pFormat == RPC_FC_WSTRING) esize = 2;
  else
  {
    ERR("Unhandled string type: %#x\n", *pFormat);
    RpcRaiseException(RPC_X_BAD_STUB_DATA);
  }

  memsize = esize * maxsize;
  bufsize = safe_multiply(esize, pStubMsg->ActualCount);

  validate_string_data(pStubMsg, bufsize, esize);

  safe_buffer_increment(pStubMsg, bufsize);
  pStubMsg->MemorySize += memsize;

  return pStubMsg->MemorySize;
}

static inline void dump_pointer_attr(unsigned char attr)
{
    if (attr & RPC_FC_P_ALLOCALLNODES)
        TRACE(" RPC_FC_P_ALLOCALLNODES");
    if (attr & RPC_FC_P_DONTFREE)
        TRACE(" RPC_FC_P_DONTFREE");
    if (attr & RPC_FC_P_ONSTACK)
        TRACE(" RPC_FC_P_ONSTACK");
    if (attr & RPC_FC_P_SIMPLEPOINTER)
        TRACE(" RPC_FC_P_SIMPLEPOINTER");
    if (attr & RPC_FC_P_DEREF)
        TRACE(" RPC_FC_P_DEREF");
    TRACE("\n");
}

/***********************************************************************
 *           PointerMarshall [internal]
 */
static void PointerMarshall(PMIDL_STUB_MESSAGE pStubMsg,
                            unsigned char *Buffer,
                            unsigned char *Pointer,
                            PFORMAT_STRING pFormat)
{
  unsigned type = pFormat[0], attr = pFormat[1];
  PFORMAT_STRING desc;
  NDR_MARSHALL m;
  ULONG pointer_id;
  int pointer_needs_marshaling;

  TRACE("(%p,%p,%p,%p)\n", pStubMsg, Buffer, Pointer, pFormat);
  TRACE("type=0x%x, attr=", type); dump_pointer_attr(attr);
  pFormat += 2;
  if (attr & RPC_FC_P_SIMPLEPOINTER) desc = pFormat;
  else desc = pFormat + *(const SHORT*)pFormat;

  switch (type) {
  case RPC_FC_RP: /* ref pointer (always non-null) */
    if (!Pointer)
    {
      ERR("NULL ref pointer is not allowed\n");
      RpcRaiseException(RPC_X_NULL_REF_POINTER);
    }
    pointer_needs_marshaling = 1;
    break;
  case RPC_FC_UP: /* unique pointer */
  case RPC_FC_OP: /* object pointer - same as unique here */
    if (Pointer)
      pointer_needs_marshaling = 1;
    else
      pointer_needs_marshaling = 0;
    pointer_id = Pointer ? NDR_POINTER_ID(pStubMsg) : 0;
    TRACE("writing 0x%08x to buffer\n", pointer_id);
    NDR_LOCAL_UINT32_WRITE(Buffer, pointer_id);
    break;
  case RPC_FC_FP:
    pointer_needs_marshaling = !NdrFullPointerQueryPointer(
      pStubMsg->FullPtrXlatTables, Pointer, 1, &pointer_id);
    TRACE("writing 0x%08x to buffer\n", pointer_id);
    NDR_LOCAL_UINT32_WRITE(Buffer, pointer_id);
    break;
  default:
    FIXME("unhandled ptr type=%02x\n", type);
    RpcRaiseException(RPC_X_BAD_STUB_DATA);
    return;
  }

  TRACE("calling marshaller for type 0x%x\n", (int)*desc);

  if (pointer_needs_marshaling) {
    if (attr & RPC_FC_P_DEREF) {
      Pointer = *(unsigned char**)Pointer;
      TRACE("deref => %p\n", Pointer);
    }
    m = NdrMarshaller[*desc & NDR_TABLE_MASK];
    if (m) m(pStubMsg, Pointer, desc);
    else FIXME("no marshaller for data type=%02x\n", *desc);
  }

  STD_OVERFLOW_CHECK(pStubMsg);
}

/***********************************************************************
 *           PointerUnmarshall [internal]
 */
static void PointerUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
                              unsigned char *Buffer,
                              unsigned char **pPointer,
                              unsigned char *pSrcPointer,
                              PFORMAT_STRING pFormat,
                              unsigned char fMustAlloc)
{
  unsigned type = pFormat[0], attr = pFormat[1];
  PFORMAT_STRING desc;
  NDR_UNMARSHALL m;
  DWORD pointer_id = 0;
  int pointer_needs_unmarshaling;

  TRACE("(%p,%p,%p,%p,%p,%d)\n", pStubMsg, Buffer, pPointer, pSrcPointer, pFormat, fMustAlloc);
  TRACE("type=0x%x, attr=", type); dump_pointer_attr(attr);
  pFormat += 2;
  if (attr & RPC_FC_P_SIMPLEPOINTER) desc = pFormat;
  else desc = pFormat + *(const SHORT*)pFormat;

  switch (type) {
  case RPC_FC_RP: /* ref pointer (always non-null) */
    pointer_needs_unmarshaling = 1;
    break;
  case RPC_FC_UP: /* unique pointer */
    pointer_id = NDR_LOCAL_UINT32_READ(Buffer);
    TRACE("pointer_id is 0x%08x\n", pointer_id);
    if (pointer_id)
      pointer_needs_unmarshaling = 1;
    else {
      *pPointer = NULL;
      pointer_needs_unmarshaling = 0;
    }
    break;
  case RPC_FC_OP: /* object pointer - we must free data before overwriting it */
    pointer_id = NDR_LOCAL_UINT32_READ(Buffer);
    TRACE("pointer_id is 0x%08x\n", pointer_id);
    if (!fMustAlloc && pSrcPointer)
    {
        FIXME("free object pointer %p\n", pSrcPointer);
        fMustAlloc = TRUE;
    }
    if (pointer_id)
      pointer_needs_unmarshaling = 1;
    else
      pointer_needs_unmarshaling = 0;
    break;
  case RPC_FC_FP:
    pointer_id = NDR_LOCAL_UINT32_READ(Buffer);
    TRACE("pointer_id is 0x%08x\n", pointer_id);
    pointer_needs_unmarshaling = !NdrFullPointerQueryRefId(
      pStubMsg->FullPtrXlatTables, pointer_id, 1, (void **)pPointer);
    break;
  default:
    FIXME("unhandled ptr type=%02x\n", type);
    RpcRaiseException(RPC_X_BAD_STUB_DATA);
    return;
  }

  if (pointer_needs_unmarshaling) {
    unsigned char *base_ptr_val = *pPointer;
    unsigned char **current_ptr = pPointer;
    if (pStubMsg->IsClient) {
      TRACE("client\n");
      /* if we aren't forcing allocation of memory then try to use the existing
       * (source) pointer to unmarshall the data into so that [in,out]
       * parameters behave correctly. it doesn't matter if the parameter is
       * [out] only since in that case the pointer will be NULL. we force
       * allocation when the source pointer is NULL here instead of in the type
       * unmarshalling routine for the benefit of the deref code below */
      if (!fMustAlloc) {
        if (pSrcPointer) {
          TRACE("setting *pPointer to %p\n", pSrcPointer);
          *pPointer = base_ptr_val = pSrcPointer;
        } else
          fMustAlloc = TRUE;
      }
    } else {
      TRACE("server\n");
      /* the memory in a stub is never initialised, so we have to work out here
       * whether we have to initialise it so we can use the optimisation of
       * setting the pointer to the buffer, if possible, or set fMustAlloc to
       * TRUE. */
      if (attr & RPC_FC_P_DEREF) {
        fMustAlloc = TRUE;
      } else {
        base_ptr_val = NULL;
        *current_ptr = NULL;
      }
    }

    if (attr & RPC_FC_P_ALLOCALLNODES)
        FIXME("RPC_FC_P_ALLOCALLNODES not implemented\n");

    if (attr & RPC_FC_P_DEREF) {
      if (fMustAlloc) {
        base_ptr_val = NdrAllocate(pStubMsg, sizeof(void *));
        *pPointer = base_ptr_val;
        current_ptr = (unsigned char **)base_ptr_val;
      } else
        current_ptr = *(unsigned char***)current_ptr;
      TRACE("deref => %p\n", current_ptr);
      if (!fMustAlloc && !*current_ptr) fMustAlloc = TRUE;
    }
    m = NdrUnmarshaller[*desc & NDR_TABLE_MASK];
    if (m) m(pStubMsg, current_ptr, desc, fMustAlloc);
    else FIXME("no unmarshaller for data type=%02x\n", *desc);

    if (type == RPC_FC_FP)
      NdrFullPointerInsertRefId(pStubMsg->FullPtrXlatTables, pointer_id,
                                base_ptr_val);
  }

  TRACE("pointer=%p\n", *pPointer);
}

/***********************************************************************
 *           PointerBufferSize [internal]
 */
static void PointerBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
                              unsigned char *Pointer,
                              PFORMAT_STRING pFormat)
{
  unsigned type = pFormat[0], attr = pFormat[1];
  PFORMAT_STRING desc;
  NDR_BUFFERSIZE m;
  int pointer_needs_sizing;
  ULONG pointer_id;

  TRACE("(%p,%p,%p)\n", pStubMsg, Pointer, pFormat);
  TRACE("type=0x%x, attr=", type); dump_pointer_attr(attr);
  pFormat += 2;
  if (attr & RPC_FC_P_SIMPLEPOINTER) desc = pFormat;
  else desc = pFormat + *(const SHORT*)pFormat;

  switch (type) {
  case RPC_FC_RP: /* ref pointer (always non-null) */
    if (!Pointer)
    {
      ERR("NULL ref pointer is not allowed\n");
      RpcRaiseException(RPC_X_NULL_REF_POINTER);
    }
    break;
  case RPC_FC_OP:
  case RPC_FC_UP:
    /* NULL pointer has no further representation */
    if (!Pointer)
        return;
    break;
  case RPC_FC_FP:
    pointer_needs_sizing = !NdrFullPointerQueryPointer(
      pStubMsg->FullPtrXlatTables, Pointer, 0, &pointer_id);
    if (!pointer_needs_sizing)
      return;
    break;
  default:
    FIXME("unhandled ptr type=%02x\n", type);
    RpcRaiseException(RPC_X_BAD_STUB_DATA);
    return;
  }

  if (attr & RPC_FC_P_DEREF) {
    Pointer = *(unsigned char**)Pointer;
    TRACE("deref => %p\n", Pointer);
  }

  m = NdrBufferSizer[*desc & NDR_TABLE_MASK];
  if (m) m(pStubMsg, Pointer, desc);
  else FIXME("no buffersizer for data type=%02x\n", *desc);
}

/***********************************************************************
 *           PointerMemorySize [internal]
 */
static unsigned long PointerMemorySize(PMIDL_STUB_MESSAGE pStubMsg,
                                       unsigned char *Buffer,
                                       PFORMAT_STRING pFormat)
{
  unsigned type = pFormat[0], attr = pFormat[1];
  PFORMAT_STRING desc;
  NDR_MEMORYSIZE m;
  DWORD pointer_id = 0;
  int pointer_needs_sizing;

  TRACE("(%p,%p,%p)\n", pStubMsg, Buffer, pFormat);
  TRACE("type=0x%x, attr=", type); dump_pointer_attr(attr);
  pFormat += 2;
  if (attr & RPC_FC_P_SIMPLEPOINTER) desc = pFormat;
  else desc = pFormat + *(const SHORT*)pFormat;

  switch (type) {
  case RPC_FC_RP: /* ref pointer (always non-null) */
    pointer_needs_sizing = 1;
    break;
  case RPC_FC_UP: /* unique pointer */
  case RPC_FC_OP: /* object pointer - we must free data before overwriting it */
    pointer_id = NDR_LOCAL_UINT32_READ(Buffer);
    TRACE("pointer_id is 0x%08x\n", pointer_id);
    if (pointer_id)
      pointer_needs_sizing = 1;
    else
      pointer_needs_sizing = 0;
    break;
  case RPC_FC_FP:
  {
    void *pointer;
    pointer_id = NDR_LOCAL_UINT32_READ(Buffer);
    TRACE("pointer_id is 0x%08x\n", pointer_id);
    pointer_needs_sizing = !NdrFullPointerQueryRefId(
      pStubMsg->FullPtrXlatTables, pointer_id, 1, &pointer);
    break;
  }
  default:
    FIXME("unhandled ptr type=%02x\n", type);
    RpcRaiseException(RPC_X_BAD_STUB_DATA);
    return 0;
  }

  if (attr & RPC_FC_P_DEREF) {
    TRACE("deref\n");
  }

  if (pointer_needs_sizing) {
    m = NdrMemorySizer[*desc & NDR_TABLE_MASK];
    if (m) m(pStubMsg, desc);
    else FIXME("no memorysizer for data type=%02x\n", *desc);
  }

  return pStubMsg->MemorySize;
}

/***********************************************************************
 *           PointerFree [internal]
 */
static void PointerFree(PMIDL_STUB_MESSAGE pStubMsg,
                        unsigned char *Pointer,
                        PFORMAT_STRING pFormat)
{
  unsigned type = pFormat[0], attr = pFormat[1];
  PFORMAT_STRING desc;
  NDR_FREE m;
  unsigned char *current_pointer = Pointer;

  TRACE("(%p,%p,%p)\n", pStubMsg, Pointer, pFormat);
  TRACE("type=0x%x, attr=", type); dump_pointer_attr(attr);
  if (attr & RPC_FC_P_DONTFREE) return;
  pFormat += 2;
  if (attr & RPC_FC_P_SIMPLEPOINTER) desc = pFormat;
  else desc = pFormat + *(const SHORT*)pFormat;

  if (!Pointer) return;

  if (type == RPC_FC_FP) {
    int pointer_needs_freeing = NdrFullPointerFree(
      pStubMsg->FullPtrXlatTables, Pointer);
    if (!pointer_needs_freeing)
      return;
  }

  if (attr & RPC_FC_P_DEREF) {
    current_pointer = *(unsigned char**)Pointer;
    TRACE("deref => %p\n", current_pointer);
  }

  m = NdrFreer[*desc & NDR_TABLE_MASK];
  if (m) m(pStubMsg, current_pointer, desc);

  /* this check stops us from trying to free buffer memory. we don't have to
   * worry about clients, since they won't call this function.
   * we don't have to check for the buffer being reallocated because
   * BufferStart and BufferEnd won't be reset when allocating memory for
   * sending the response. we don't have to check for the new buffer here as
   * it won't be used a type memory, only for buffer memory */
  if (Pointer >= pStubMsg->BufferStart && Pointer < pStubMsg->BufferEnd)
      goto notfree;

  if (attr & RPC_FC_P_ONSTACK) {
    TRACE("not freeing stack ptr %p\n", Pointer);
    return;
  }
  TRACE("freeing %p\n", Pointer);
  NdrFree(pStubMsg, Pointer);
  return;
notfree:
  TRACE("not freeing %p\n", Pointer);
}

/***********************************************************************
 *           EmbeddedPointerMarshall
 */
static unsigned char * EmbeddedPointerMarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                               unsigned char *pMemory,
                                               PFORMAT_STRING pFormat)
{
  unsigned char *Mark = pStubMsg->BufferMark;
  unsigned rep, count, stride;
  unsigned i;
  unsigned char *saved_buffer = NULL;

  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);

  if (*pFormat != RPC_FC_PP) return NULL;
  pFormat += 2;

  if (pStubMsg->PointerBufferMark)
  {
    saved_buffer = pStubMsg->Buffer;
    pStubMsg->Buffer = pStubMsg->PointerBufferMark;
    pStubMsg->PointerBufferMark = NULL;
  }

  while (pFormat[0] != RPC_FC_END) {
    switch (pFormat[0]) {
    default:
      FIXME("unknown repeat type %d\n", pFormat[0]);
    case RPC_FC_NO_REPEAT:
      rep = 1;
      stride = 0;
      count = 1;
      pFormat += 2;
      break;
    case RPC_FC_FIXED_REPEAT:
      rep = *(const WORD*)&pFormat[2];
      stride = *(const WORD*)&pFormat[4];
      count = *(const WORD*)&pFormat[8];
      pFormat += 10;
      break;
    case RPC_FC_VARIABLE_REPEAT:
      rep = (pFormat[1] == RPC_FC_VARIABLE_OFFSET) ? pStubMsg->ActualCount : pStubMsg->MaxCount;
      stride = *(const WORD*)&pFormat[2];
      count = *(const WORD*)&pFormat[6];
      pFormat += 8;
      break;
    }
    for (i = 0; i < rep; i++) {
      PFORMAT_STRING info = pFormat;
      unsigned char *membase = pMemory + (i * stride);
      unsigned char *bufbase = Mark + (i * stride);
      unsigned u;

      for (u=0; u<count; u++,info+=8) {
        unsigned char *memptr = membase + *(const SHORT*)&info[0];
        unsigned char *bufptr = bufbase + *(const SHORT*)&info[2];
        unsigned char *saved_memory = pStubMsg->Memory;

        pStubMsg->Memory = pMemory;
        PointerMarshall(pStubMsg, bufptr, *(unsigned char**)memptr, info+4);
        pStubMsg->Memory = saved_memory;
      }
    }
    pFormat += 8 * count;
  }

  if (saved_buffer)
  {
    pStubMsg->PointerBufferMark = pStubMsg->Buffer;
    pStubMsg->Buffer = saved_buffer;
  }

  STD_OVERFLOW_CHECK(pStubMsg);

  return NULL;
}

/***********************************************************************
 *           EmbeddedPointerUnmarshall
 */
static unsigned char * EmbeddedPointerUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                                 unsigned char *pDstBuffer,
                                                 unsigned char *pSrcMemoryPtrs,
                                                 PFORMAT_STRING pFormat,
                                                 unsigned char fMustAlloc)
{
  unsigned char *Mark = pStubMsg->BufferMark;
  unsigned rep, count, stride;
  unsigned i;
  unsigned char *saved_buffer = NULL;

  TRACE("(%p,%p,%p,%p,%d)\n", pStubMsg, pDstBuffer, pSrcMemoryPtrs, pFormat, fMustAlloc);

  if (*pFormat != RPC_FC_PP) return NULL;
  pFormat += 2;

  if (pStubMsg->PointerBufferMark)
  {
    saved_buffer = pStubMsg->Buffer;
    pStubMsg->Buffer = pStubMsg->PointerBufferMark;
    pStubMsg->PointerBufferMark = NULL;
  }

  while (pFormat[0] != RPC_FC_END) {
    TRACE("pFormat[0] = 0x%x\n", pFormat[0]);
    switch (pFormat[0]) {
    default:
      FIXME("unknown repeat type %d\n", pFormat[0]);
    case RPC_FC_NO_REPEAT:
      rep = 1;
      stride = 0;
      count = 1;
      pFormat += 2;
      break;
    case RPC_FC_FIXED_REPEAT:
      rep = *(const WORD*)&pFormat[2];
      stride = *(const WORD*)&pFormat[4];
      count = *(const WORD*)&pFormat[8];
      pFormat += 10;
      break;
    case RPC_FC_VARIABLE_REPEAT:
      rep = (pFormat[1] == RPC_FC_VARIABLE_OFFSET) ? pStubMsg->ActualCount : pStubMsg->MaxCount;
      stride = *(const WORD*)&pFormat[2];
      count = *(const WORD*)&pFormat[6];
      pFormat += 8;
      break;
    }
    for (i = 0; i < rep; i++) {
      PFORMAT_STRING info = pFormat;
      unsigned char *bufdstbase = pDstBuffer + (i * stride);
      unsigned char *memsrcbase = pSrcMemoryPtrs + (i * stride);
      unsigned char *bufbase = Mark + (i * stride);
      unsigned u;

      for (u=0; u<count; u++,info+=8) {
        unsigned char **bufdstptr = (unsigned char **)(bufdstbase + *(const SHORT*)&info[2]);
        unsigned char **memsrcptr = (unsigned char **)(memsrcbase + *(const SHORT*)&info[0]);
        unsigned char *bufptr = bufbase + *(const SHORT*)&info[2];
        PointerUnmarshall(pStubMsg, bufptr, bufdstptr, *memsrcptr, info+4, fMustAlloc);
      }
    }
    pFormat += 8 * count;
  }

  if (saved_buffer)
  {
    pStubMsg->PointerBufferMark = pStubMsg->Buffer;
    pStubMsg->Buffer = saved_buffer;
  }

  return NULL;
}

/***********************************************************************
 *           EmbeddedPointerBufferSize
 */
static void EmbeddedPointerBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
                                      unsigned char *pMemory,
                                      PFORMAT_STRING pFormat)
{
  unsigned rep, count, stride;
  unsigned i;
  ULONG saved_buffer_length = 0;

  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);

  if (pStubMsg->IgnoreEmbeddedPointers) return;

  if (*pFormat != RPC_FC_PP) return;
  pFormat += 2;

  if (pStubMsg->PointerLength)
  {
    saved_buffer_length = pStubMsg->BufferLength;
    pStubMsg->BufferLength = pStubMsg->PointerLength;
    pStubMsg->PointerLength = 0;
  }

  while (pFormat[0] != RPC_FC_END) {
    switch (pFormat[0]) {
    default:
      FIXME("unknown repeat type %d\n", pFormat[0]);
    case RPC_FC_NO_REPEAT:
      rep = 1;
      stride = 0;
      count = 1;
      pFormat += 2;
      break;
    case RPC_FC_FIXED_REPEAT:
      rep = *(const WORD*)&pFormat[2];
      stride = *(const WORD*)&pFormat[4];
      count = *(const WORD*)&pFormat[8];
      pFormat += 10;
      break;
    case RPC_FC_VARIABLE_REPEAT:
      rep = (pFormat[1] == RPC_FC_VARIABLE_OFFSET) ? pStubMsg->ActualCount : pStubMsg->MaxCount;
      stride = *(const WORD*)&pFormat[2];
      count = *(const WORD*)&pFormat[6];
      pFormat += 8;
      break;
    }
    for (i = 0; i < rep; i++) {
      PFORMAT_STRING info = pFormat;
      unsigned char *membase = pMemory + (i * stride);
      unsigned u;

      for (u=0; u<count; u++,info+=8) {
        unsigned char *memptr = membase + *(const SHORT*)&info[0];
        unsigned char *saved_memory = pStubMsg->Memory;

        pStubMsg->Memory = pMemory;
        PointerBufferSize(pStubMsg, *(unsigned char**)memptr, info+4);
        pStubMsg->Memory = saved_memory;
      }
    }
    pFormat += 8 * count;
  }

  if (saved_buffer_length)
  {
    pStubMsg->PointerLength = pStubMsg->BufferLength;
    pStubMsg->BufferLength = saved_buffer_length;
  }
}

/***********************************************************************
 *           EmbeddedPointerMemorySize [internal]
 */
static unsigned long EmbeddedPointerMemorySize(PMIDL_STUB_MESSAGE pStubMsg,
                                               PFORMAT_STRING pFormat)
{
  unsigned char *Mark = pStubMsg->BufferMark;
  unsigned rep, count, stride;
  unsigned i;
  unsigned char *saved_buffer = NULL;

  TRACE("(%p,%p)\n", pStubMsg, pFormat);

  if (pStubMsg->IgnoreEmbeddedPointers) return 0;

  if (pStubMsg->PointerBufferMark)
  {
    saved_buffer = pStubMsg->Buffer;
    pStubMsg->Buffer = pStubMsg->PointerBufferMark;
    pStubMsg->PointerBufferMark = NULL;
  }

  if (*pFormat != RPC_FC_PP) return 0;
  pFormat += 2;

  while (pFormat[0] != RPC_FC_END) {
    switch (pFormat[0]) {
    default:
      FIXME("unknown repeat type %d\n", pFormat[0]);
    case RPC_FC_NO_REPEAT:
      rep = 1;
      stride = 0;
      count = 1;
      pFormat += 2;
      break;
    case RPC_FC_FIXED_REPEAT:
      rep = *(const WORD*)&pFormat[2];
      stride = *(const WORD*)&pFormat[4];
      count = *(const WORD*)&pFormat[8];
      pFormat += 10;
      break;
    case RPC_FC_VARIABLE_REPEAT:
      rep = (pFormat[1] == RPC_FC_VARIABLE_OFFSET) ? pStubMsg->ActualCount : pStubMsg->MaxCount;
      stride = *(const WORD*)&pFormat[2];
      count = *(const WORD*)&pFormat[6];
      pFormat += 8;
      break;
    }
    for (i = 0; i < rep; i++) {
      PFORMAT_STRING info = pFormat;
      unsigned char *bufbase = Mark + (i * stride);
      unsigned u;
      for (u=0; u<count; u++,info+=8) {
        unsigned char *bufptr = bufbase + *(const SHORT*)&info[2];
        PointerMemorySize(pStubMsg, bufptr, info+4);
      }
    }
    pFormat += 8 * count;
  }

  if (saved_buffer)
  {
    pStubMsg->PointerBufferMark = pStubMsg->Buffer;
    pStubMsg->Buffer = saved_buffer;
  }

  return 0;
}

/***********************************************************************
 *           EmbeddedPointerFree [internal]
 */
static void EmbeddedPointerFree(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
  unsigned rep, count, stride;
  unsigned i;

  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
  if (*pFormat != RPC_FC_PP) return;
  pFormat += 2;

  while (pFormat[0] != RPC_FC_END) {
    switch (pFormat[0]) {
    default:
      FIXME("unknown repeat type %d\n", pFormat[0]);
    case RPC_FC_NO_REPEAT:
      rep = 1;
      stride = 0;
      count = 1;
      pFormat += 2;
      break;
    case RPC_FC_FIXED_REPEAT:
      rep = *(const WORD*)&pFormat[2];
      stride = *(const WORD*)&pFormat[4];
      count = *(const WORD*)&pFormat[8];
      pFormat += 10;
      break;
    case RPC_FC_VARIABLE_REPEAT:
      rep = (pFormat[1] == RPC_FC_VARIABLE_OFFSET) ? pStubMsg->ActualCount : pStubMsg->MaxCount;
      stride = *(const WORD*)&pFormat[2];
      count = *(const WORD*)&pFormat[6];
      pFormat += 8;
      break;
    }
    for (i = 0; i < rep; i++) {
      PFORMAT_STRING info = pFormat;
      unsigned char *membase = pMemory + (i * stride);
      unsigned u;

      for (u=0; u<count; u++,info+=8) {
        unsigned char *memptr = membase + *(const SHORT*)&info[0];
        unsigned char *saved_memory = pStubMsg->Memory;

        pStubMsg->Memory = pMemory;
        PointerFree(pStubMsg, *(unsigned char**)memptr, info+4);
        pStubMsg->Memory = saved_memory;
      }
    }
    pFormat += 8 * count;
  }
}

/***********************************************************************
 *           NdrPointerMarshall [RPCRT4.@]
 */
unsigned char * WINAPI NdrPointerMarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                          unsigned char *pMemory,
                                          PFORMAT_STRING pFormat)
{
  unsigned char *Buffer;

  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);

  /* Increment the buffer here instead of in PointerMarshall,
   * as that is used by embedded pointers which already handle the incrementing
   * the buffer, and shouldn't write any additional pointer data to the wire */
  if (*pFormat != RPC_FC_RP)
  {
    ALIGN_POINTER_CLEAR(pStubMsg->Buffer, 4);
    Buffer = pStubMsg->Buffer;
    safe_buffer_increment(pStubMsg, 4);
  }
  else
    Buffer = pStubMsg->Buffer;

  PointerMarshall(pStubMsg, Buffer, pMemory, pFormat);

  return NULL;
}

/***********************************************************************
 *           NdrPointerUnmarshall [RPCRT4.@]
 */
unsigned char * WINAPI NdrPointerUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                            unsigned char **ppMemory,
                                            PFORMAT_STRING pFormat,
                                            unsigned char fMustAlloc)
{
  unsigned char *Buffer;

  TRACE("(%p,%p,%p,%d)\n", pStubMsg, ppMemory, pFormat, fMustAlloc);

  /* Increment the buffer here instead of in PointerUnmarshall,
   * as that is used by embedded pointers which already handle the incrementing
   * the buffer, and shouldn't read any additional pointer data from the
   * buffer */
  if (*pFormat != RPC_FC_RP)
  {
    ALIGN_POINTER(pStubMsg->Buffer, 4);
    Buffer = pStubMsg->Buffer;
    safe_buffer_increment(pStubMsg, 4);
  }
  else
    Buffer = pStubMsg->Buffer;

  PointerUnmarshall(pStubMsg, Buffer, ppMemory, *ppMemory, pFormat, fMustAlloc);

  return NULL;
}

/***********************************************************************
 *           NdrPointerBufferSize [RPCRT4.@]
 */
void WINAPI NdrPointerBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
                                      unsigned char *pMemory,
                                      PFORMAT_STRING pFormat)
{
  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);

  /* Increment the buffer length here instead of in PointerBufferSize,
   * as that is used by embedded pointers which already handle the buffer
   * length, and shouldn't write anything more to the wire */
  if (*pFormat != RPC_FC_RP)
  {
    ALIGN_LENGTH(pStubMsg->BufferLength, 4);
    safe_buffer_length_increment(pStubMsg, 4);
  }

  PointerBufferSize(pStubMsg, pMemory, pFormat);
}

/***********************************************************************
 *           NdrPointerMemorySize [RPCRT4.@]
 */
ULONG WINAPI NdrPointerMemorySize(PMIDL_STUB_MESSAGE pStubMsg,
                                  PFORMAT_STRING pFormat)
{
  /* unsigned size = *(LPWORD)(pFormat+2); */
  FIXME("(%p,%p): stub\n", pStubMsg, pFormat);
  PointerMemorySize(pStubMsg, pStubMsg->Buffer, pFormat);
  return 0;
}

/***********************************************************************
 *           NdrPointerFree [RPCRT4.@]
 */
void WINAPI NdrPointerFree(PMIDL_STUB_MESSAGE pStubMsg,
                           unsigned char *pMemory,
                           PFORMAT_STRING pFormat)
{
  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
  PointerFree(pStubMsg, pMemory, pFormat);
}

/***********************************************************************
 *           NdrSimpleTypeMarshall [RPCRT4.@]
 */
void WINAPI NdrSimpleTypeMarshall( PMIDL_STUB_MESSAGE pStubMsg, unsigned char* pMemory,
                                   unsigned char FormatChar )
{
    NdrBaseTypeMarshall(pStubMsg, pMemory, &FormatChar);
}

/***********************************************************************
 *           NdrSimpleTypeUnmarshall [RPCRT4.@]
 *
 * Unmarshall a base type.
 *
 * NOTES
 *  Doesn't check that the buffer is long enough before copying, so the caller
 * should do this.
 */
void WINAPI NdrSimpleTypeUnmarshall( PMIDL_STUB_MESSAGE pStubMsg, unsigned char* pMemory,
                                     unsigned char FormatChar )
{
#define BASE_TYPE_UNMARSHALL(type) \
        ALIGN_POINTER(pStubMsg->Buffer, sizeof(type)); \
	TRACE("pMemory: %p\n", pMemory); \
	*(type *)pMemory = *(type *)pStubMsg->Buffer; \
        pStubMsg->Buffer += sizeof(type);

    switch(FormatChar)
    {
    case RPC_FC_BYTE:
    case RPC_FC_CHAR:
    case RPC_FC_SMALL:
    case RPC_FC_USMALL:
        BASE_TYPE_UNMARSHALL(UCHAR);
        TRACE("value: 0x%02x\n", *pMemory);
        break;
    case RPC_FC_WCHAR:
    case RPC_FC_SHORT:
    case RPC_FC_USHORT:
        BASE_TYPE_UNMARSHALL(USHORT);
        TRACE("value: 0x%04x\n", *(USHORT *)pMemory);
        break;
    case RPC_FC_LONG:
    case RPC_FC_ULONG:
    case RPC_FC_ERROR_STATUS_T:
    case RPC_FC_ENUM32:
        BASE_TYPE_UNMARSHALL(ULONG);
        TRACE("value: 0x%08x\n", *(ULONG *)pMemory);
        break;
   case RPC_FC_FLOAT:
        BASE_TYPE_UNMARSHALL(float);
        TRACE("value: %f\n", *(float *)pMemory);
        break;
    case RPC_FC_DOUBLE:
        BASE_TYPE_UNMARSHALL(double);
        TRACE("value: %f\n", *(double *)pMemory);
        break;
    case RPC_FC_HYPER:
        BASE_TYPE_UNMARSHALL(ULONGLONG);
        TRACE("value: %s\n", wine_dbgstr_longlong(*(ULONGLONG *)pMemory));
        break;
    case RPC_FC_ENUM16:
        ALIGN_POINTER(pStubMsg->Buffer, sizeof(USHORT));
        TRACE("pMemory: %p\n", pMemory);
        /* 16-bits on the wire, but int in memory */
        *(UINT *)pMemory = *(USHORT *)pStubMsg->Buffer;
        pStubMsg->Buffer += sizeof(USHORT);
        TRACE("value: 0x%08x\n", *(UINT *)pMemory);
        break;
    case RPC_FC_IGNORE:
        break;
    default:
        FIXME("Unhandled base type: 0x%02x\n", FormatChar);
    }
#undef BASE_TYPE_UNMARSHALL
}

/***********************************************************************
 *           NdrSimpleStructMarshall [RPCRT4.@]
 */
unsigned char * WINAPI NdrSimpleStructMarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                               unsigned char *pMemory,
                                               PFORMAT_STRING pFormat)
{
  unsigned size = *(const WORD*)(pFormat+2);
  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);

  ALIGN_POINTER_CLEAR(pStubMsg->Buffer, pFormat[1] + 1);

  pStubMsg->BufferMark = pStubMsg->Buffer;
  safe_copy_to_buffer(pStubMsg, pMemory, size);

  if (pFormat[0] != RPC_FC_STRUCT)
    EmbeddedPointerMarshall(pStubMsg, pMemory, pFormat+4);

  return NULL;
}

/***********************************************************************
 *           NdrSimpleStructUnmarshall [RPCRT4.@]
 */
unsigned char * WINAPI NdrSimpleStructUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                                 unsigned char **ppMemory,
                                                 PFORMAT_STRING pFormat,
                                                 unsigned char fMustAlloc)
{
  unsigned size = *(const WORD*)(pFormat+2);
  unsigned char *saved_buffer;
  TRACE("(%p,%p,%p,%d)\n", pStubMsg, ppMemory, pFormat, fMustAlloc);

  ALIGN_POINTER(pStubMsg->Buffer, pFormat[1] + 1);

  if (fMustAlloc)
    *ppMemory = NdrAllocate(pStubMsg, size);
  else
  {
    if (!pStubMsg->IsClient && !*ppMemory)
      /* for servers, we just point straight into the RPC buffer */
      *ppMemory = pStubMsg->Buffer;
  }

  saved_buffer = pStubMsg->BufferMark = pStubMsg->Buffer;
  safe_buffer_increment(pStubMsg, size);
  if (pFormat[0] == RPC_FC_PSTRUCT)
      EmbeddedPointerUnmarshall(pStubMsg, saved_buffer, *ppMemory, pFormat+4, fMustAlloc);

  TRACE("copying %p to %p\n", saved_buffer, *ppMemory);
  if (*ppMemory != saved_buffer)
      memcpy(*ppMemory, saved_buffer, size);

  return NULL;
}

/***********************************************************************
 *           NdrSimpleStructBufferSize [RPCRT4.@]
 */
void WINAPI NdrSimpleStructBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
                                      unsigned char *pMemory,
                                      PFORMAT_STRING pFormat)
{
  unsigned size = *(const WORD*)(pFormat+2);
  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);

  ALIGN_LENGTH(pStubMsg->BufferLength, pFormat[1] + 1);

  safe_buffer_length_increment(pStubMsg, size);
  if (pFormat[0] != RPC_FC_STRUCT)
    EmbeddedPointerBufferSize(pStubMsg, pMemory, pFormat+4);
}

/***********************************************************************
 *           NdrSimpleStructMemorySize [RPCRT4.@]
 */
ULONG WINAPI NdrSimpleStructMemorySize(PMIDL_STUB_MESSAGE pStubMsg,
                                       PFORMAT_STRING pFormat)
{
  unsigned short size = *(const WORD *)(pFormat+2);

  TRACE("(%p,%p)\n", pStubMsg, pFormat);

  ALIGN_POINTER(pStubMsg->Buffer, pFormat[1] + 1);
  pStubMsg->MemorySize += size;
  safe_buffer_increment(pStubMsg, size);

  if (pFormat[0] != RPC_FC_STRUCT)
    EmbeddedPointerMemorySize(pStubMsg, pFormat+4);
  return pStubMsg->MemorySize;
}

/***********************************************************************
 *           NdrSimpleStructFree [RPCRT4.@]
 */
void WINAPI NdrSimpleStructFree(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
  if (pFormat[0] != RPC_FC_STRUCT)
    EmbeddedPointerFree(pStubMsg, pMemory, pFormat+4);
}


#include "pshpack1.h"
typedef struct
{
    unsigned char type;
    unsigned char flags_type; /* flags in upper nibble, type in lower nibble */
    ULONG low_value;
    ULONG high_value;
} NDR_RANGE;
#include "poppack.h"

static unsigned long EmbeddedComplexSize(MIDL_STUB_MESSAGE *pStubMsg,
                                         PFORMAT_STRING pFormat)
{
  switch (*pFormat) {
  case RPC_FC_STRUCT:
  case RPC_FC_PSTRUCT:
  case RPC_FC_CSTRUCT:
  case RPC_FC_BOGUS_STRUCT:
  case RPC_FC_SMFARRAY:
  case RPC_FC_SMVARRAY:
  case RPC_FC_CSTRING:
    return *(const WORD*)&pFormat[2];
  case RPC_FC_USER_MARSHAL:
    return *(const WORD*)&pFormat[4];
  case RPC_FC_RANGE: {
    switch (((const NDR_RANGE *)pFormat)->flags_type & 0xf) {
    case RPC_FC_BYTE:
    case RPC_FC_CHAR:
    case RPC_FC_SMALL:
    case RPC_FC_USMALL:
        return sizeof(UCHAR);
    case RPC_FC_WCHAR:
    case RPC_FC_SHORT:
    case RPC_FC_USHORT:
        return sizeof(USHORT);
    case RPC_FC_LONG:
    case RPC_FC_ULONG:
    case RPC_FC_ENUM32:
        return sizeof(ULONG);
    case RPC_FC_FLOAT:
        return sizeof(float);
    case RPC_FC_DOUBLE:
        return sizeof(double);
    case RPC_FC_HYPER:
        return sizeof(ULONGLONG);
    case RPC_FC_ERROR_STATUS_T:
        return sizeof(error_status_t);
    case RPC_FC_ENUM16:
        return sizeof(UINT);
    default:
        ERR("unknown type 0x%x\n", ((const NDR_RANGE *)pFormat)->flags_type & 0xf);
        RpcRaiseException(RPC_X_BAD_STUB_DATA);
    }
  }
  case RPC_FC_NON_ENCAPSULATED_UNION:
    pFormat += 2;
    if (pStubMsg->fHasNewCorrDesc)
        pFormat += 6;
    else
        pFormat += 4;

    pFormat += *(const SHORT*)pFormat;
    return *(const SHORT*)pFormat;
  case RPC_FC_IP:
    return sizeof(void *);
  case RPC_FC_WSTRING:
    return *(const WORD*)&pFormat[2] * 2;
  default:
    FIXME("unhandled embedded type %02x\n", *pFormat);
  }
  return 0;
}


static unsigned long EmbeddedComplexMemorySize(PMIDL_STUB_MESSAGE pStubMsg,
                                               PFORMAT_STRING pFormat)
{
  NDR_MEMORYSIZE m = NdrMemorySizer[*pFormat & NDR_TABLE_MASK];

  if (!m)
  {
    FIXME("no memorysizer for data type=%02x\n", *pFormat);
    return 0;
  }

  return m(pStubMsg, pFormat);
}


static unsigned char * ComplexMarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                       unsigned char *pMemory,
                                       PFORMAT_STRING pFormat,
                                       PFORMAT_STRING pPointer)
{
  PFORMAT_STRING desc;
  NDR_MARSHALL m;
  unsigned long size;

  while (*pFormat != RPC_FC_END) {
    switch (*pFormat) {
    case RPC_FC_BYTE:
    case RPC_FC_CHAR:
    case RPC_FC_SMALL:
    case RPC_FC_USMALL:
      TRACE("byte=%d <= %p\n", *(WORD*)pMemory, pMemory);
      safe_copy_to_buffer(pStubMsg, pMemory, 1);
      pMemory += 1;
      break;
    case RPC_FC_WCHAR:
    case RPC_FC_SHORT:
    case RPC_FC_USHORT:
      TRACE("short=%d <= %p\n", *(WORD*)pMemory, pMemory);
      safe_copy_to_buffer(pStubMsg, pMemory, 2);
      pMemory += 2;
      break;
    case RPC_FC_ENUM16:
      TRACE("enum16=%d <= %p\n", *(DWORD*)pMemory, pMemory);
      if (32767 < *(DWORD*)pMemory)
        RpcRaiseException(RPC_X_ENUM_VALUE_OUT_OF_RANGE);
      safe_copy_to_buffer(pStubMsg, pMemory, 2);
      pMemory += 4;
      break;
    case RPC_FC_LONG:
    case RPC_FC_ULONG:
    case RPC_FC_ENUM32:
      TRACE("long=%d <= %p\n", *(DWORD*)pMemory, pMemory);
      safe_copy_to_buffer(pStubMsg, pMemory, 4);
      pMemory += 4;
      break;
    case RPC_FC_HYPER:
      TRACE("longlong=%s <= %p\n", wine_dbgstr_longlong(*(ULONGLONG*)pMemory), pMemory);
      safe_copy_to_buffer(pStubMsg, pMemory, 8);
      pMemory += 8;
      break;
    case RPC_FC_POINTER:
    {
      unsigned char *saved_buffer;
      int pointer_buffer_mark_set = 0;
      TRACE("pointer=%p <= %p\n", *(unsigned char**)pMemory, pMemory);
      TRACE("pStubMsg->Buffer before %p\n", pStubMsg->Buffer);
      if (*pPointer != RPC_FC_RP)
        ALIGN_POINTER_CLEAR(pStubMsg->Buffer, 4);
      saved_buffer = pStubMsg->Buffer;
      if (pStubMsg->PointerBufferMark)
      {
        pStubMsg->Buffer = pStubMsg->PointerBufferMark;
        pStubMsg->PointerBufferMark = NULL;
        pointer_buffer_mark_set = 1;
      }
      else if (*pPointer != RPC_FC_RP)
        safe_buffer_increment(pStubMsg, 4); /* for pointer ID */
      PointerMarshall(pStubMsg, saved_buffer, *(unsigned char**)pMemory, pPointer);
      if (pointer_buffer_mark_set)
      {
        STD_OVERFLOW_CHECK(pStubMsg);
        pStubMsg->PointerBufferMark = pStubMsg->Buffer;
        pStubMsg->Buffer = saved_buffer;
        if (*pPointer != RPC_FC_RP)
          safe_buffer_increment(pStubMsg, 4); /* for pointer ID */
      }
      TRACE("pStubMsg->Buffer after %p\n", pStubMsg->Buffer);
      pPointer += 4;
      pMemory += 4;
      break;
    }
    case RPC_FC_ALIGNM4:
      ALIGN_POINTER(pMemory, 4);
      break;
    case RPC_FC_ALIGNM8:
      ALIGN_POINTER(pMemory, 8);
      break;
    case RPC_FC_STRUCTPAD1:
    case RPC_FC_STRUCTPAD2:
    case RPC_FC_STRUCTPAD3:
    case RPC_FC_STRUCTPAD4:
    case RPC_FC_STRUCTPAD5:
    case RPC_FC_STRUCTPAD6:
    case RPC_FC_STRUCTPAD7:
      pMemory += *pFormat - RPC_FC_STRUCTPAD1 + 1;
      break;
    case RPC_FC_EMBEDDED_COMPLEX:
      pMemory += pFormat[1];
      pFormat += 2;
      desc = pFormat + *(const SHORT*)pFormat;
      size = EmbeddedComplexSize(pStubMsg, desc);
      TRACE("embedded complex (size=%ld) <= %p\n", size, pMemory);
      m = NdrMarshaller[*desc & NDR_TABLE_MASK];
      if (m)
      {
        /* for some reason interface pointers aren't generated as
         * RPC_FC_POINTER, but instead as RPC_FC_EMBEDDED_COMPLEX, yet
         * they still need the derefencing treatment that pointers are
         * given */
        if (*desc == RPC_FC_IP)
          m(pStubMsg, *(unsigned char **)pMemory, desc);
        else
          m(pStubMsg, pMemory, desc);
      }
      else FIXME("no marshaller for embedded type %02x\n", *desc);
      pMemory += size;
      pFormat += 2;
      continue;
    case RPC_FC_PAD:
      break;
    default:
      FIXME("unhandled format 0x%02x\n", *pFormat);
    }
    pFormat++;
  }

  return pMemory;
}

static unsigned char * ComplexUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                         unsigned char *pMemory,
                                         PFORMAT_STRING pFormat,
                                         PFORMAT_STRING pPointer,
                                         unsigned char fMustAlloc)
{
  PFORMAT_STRING desc;
  NDR_UNMARSHALL m;
  unsigned long size;

  while (*pFormat != RPC_FC_END) {
    switch (*pFormat) {
    case RPC_FC_BYTE:
    case RPC_FC_CHAR:
    case RPC_FC_SMALL:
    case RPC_FC_USMALL:
      safe_copy_from_buffer(pStubMsg, pMemory, 1);
      TRACE("byte=%d => %p\n", *(WORD*)pMemory, pMemory);
      pMemory += 1;
      break;
    case RPC_FC_WCHAR:
    case RPC_FC_SHORT:
    case RPC_FC_USHORT:
      safe_copy_from_buffer(pStubMsg, pMemory, 2);
      TRACE("short=%d => %p\n", *(WORD*)pMemory, pMemory);
      pMemory += 2;
      break;
    case RPC_FC_ENUM16:
      safe_copy_from_buffer(pStubMsg, pMemory, 2);
      *(DWORD*)pMemory &= 0xffff;
      TRACE("enum16=%d => %p\n", *(DWORD*)pMemory, pMemory);
      if (32767 < *(DWORD*)pMemory)
        RpcRaiseException(RPC_X_ENUM_VALUE_OUT_OF_RANGE);
      pMemory += 4;
      break;
    case RPC_FC_LONG:
    case RPC_FC_ULONG:
    case RPC_FC_ENUM32:
      safe_copy_from_buffer(pStubMsg, pMemory, 4);
      TRACE("long=%d => %p\n", *(DWORD*)pMemory, pMemory);
      pMemory += 4;
      break;
    case RPC_FC_HYPER:
      safe_copy_from_buffer(pStubMsg, pMemory, 8);
      TRACE("longlong=%s => %p\n", wine_dbgstr_longlong(*(ULONGLONG*)pMemory), pMemory);
      pMemory += 8;
      break;
    case RPC_FC_POINTER:
    {
      unsigned char *saved_buffer;
      int pointer_buffer_mark_set = 0;
      TRACE("pointer => %p\n", pMemory);
      if (*pPointer != RPC_FC_RP)
        ALIGN_POINTER(pStubMsg->Buffer, 4);
      saved_buffer = pStubMsg->Buffer;
      if (pStubMsg->PointerBufferMark)
      {
        pStubMsg->Buffer = pStubMsg->PointerBufferMark;
        pStubMsg->PointerBufferMark = NULL;
        pointer_buffer_mark_set = 1;
      }
      else if (*pPointer != RPC_FC_RP)
        safe_buffer_increment(pStubMsg, 4); /* for pointer ID */

      PointerUnmarshall(pStubMsg, saved_buffer, (unsigned char**)pMemory, *(unsigned char**)pMemory, pPointer, fMustAlloc);
      if (pointer_buffer_mark_set)
      {
        STD_OVERFLOW_CHECK(pStubMsg);
        pStubMsg->PointerBufferMark = pStubMsg->Buffer;
        pStubMsg->Buffer = saved_buffer;
        if (*pPointer != RPC_FC_RP)
          safe_buffer_increment(pStubMsg, 4); /* for pointer ID */
      }
      pPointer += 4;
      pMemory += 4;
      break;
    }
    case RPC_FC_ALIGNM4:
      ALIGN_POINTER_CLEAR(pMemory, 4);
      break;
    case RPC_FC_ALIGNM8:
      ALIGN_POINTER_CLEAR(pMemory, 8);
      break;
    case RPC_FC_STRUCTPAD1:
    case RPC_FC_STRUCTPAD2:
    case RPC_FC_STRUCTPAD3:
    case RPC_FC_STRUCTPAD4:
    case RPC_FC_STRUCTPAD5:
    case RPC_FC_STRUCTPAD6:
    case RPC_FC_STRUCTPAD7:
      memset(pMemory, 0, *pFormat - RPC_FC_STRUCTPAD1 + 1);
      pMemory += *pFormat - RPC_FC_STRUCTPAD1 + 1;
      break;
    case RPC_FC_EMBEDDED_COMPLEX:
      pMemory += pFormat[1];
      pFormat += 2;
      desc = pFormat + *(const SHORT*)pFormat;
      size = EmbeddedComplexSize(pStubMsg, desc);
      TRACE("embedded complex (size=%ld) => %p\n", size, pMemory);
      if (fMustAlloc)
        /* we can't pass fMustAlloc=TRUE into the marshaller for this type
         * since the type is part of the memory block that is encompassed by
         * the whole complex type. Memory is forced to allocate when pointers
         * are set to NULL, so we emulate that part of fMustAlloc=TRUE by
         * clearing the memory we pass in to the unmarshaller */
        memset(pMemory, 0, size);
      m = NdrUnmarshaller[*desc & NDR_TABLE_MASK];
      if (m)
      {
        /* for some reason interface pointers aren't generated as
         * RPC_FC_POINTER, but instead as RPC_FC_EMBEDDED_COMPLEX, yet
         * they still need the derefencing treatment that pointers are
         * given */
        if (*desc == RPC_FC_IP)
          m(pStubMsg, (unsigned char **)pMemory, desc, FALSE);
        else
          m(pStubMsg, &pMemory, desc, FALSE);
      }
      else FIXME("no unmarshaller for embedded type %02x\n", *desc);
      pMemory += size;
      pFormat += 2;
      continue;
    case RPC_FC_PAD:
      break;
    default:
      FIXME("unhandled format %d\n", *pFormat);
    }
    pFormat++;
  }

  return pMemory;
}

static unsigned char * ComplexBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
                                         unsigned char *pMemory,
                                         PFORMAT_STRING pFormat,
                                         PFORMAT_STRING pPointer)
{
  PFORMAT_STRING desc;
  NDR_BUFFERSIZE m;
  unsigned long size;

  while (*pFormat != RPC_FC_END) {
    switch (*pFormat) {
    case RPC_FC_BYTE:
    case RPC_FC_CHAR:
    case RPC_FC_SMALL:
    case RPC_FC_USMALL:
      safe_buffer_length_increment(pStubMsg, 1);
      pMemory += 1;
      break;
    case RPC_FC_WCHAR:
    case RPC_FC_SHORT:
    case RPC_FC_USHORT:
      safe_buffer_length_increment(pStubMsg, 2);
      pMemory += 2;
      break;
    case RPC_FC_ENUM16:
      safe_buffer_length_increment(pStubMsg, 2);
      pMemory += 4;
      break;
    case RPC_FC_LONG:
    case RPC_FC_ULONG:
    case RPC_FC_ENUM32:
      safe_buffer_length_increment(pStubMsg, 4);
      pMemory += 4;
      break;
    case RPC_FC_HYPER:
      safe_buffer_length_increment(pStubMsg, 8);
      pMemory += 8;
      break;
    case RPC_FC_POINTER:
      if (!pStubMsg->IgnoreEmbeddedPointers)
      {
        int saved_buffer_length = pStubMsg->BufferLength;
        pStubMsg->BufferLength = pStubMsg->PointerLength;
        pStubMsg->PointerLength = 0;
        if(!pStubMsg->BufferLength)
          ERR("BufferLength == 0??\n");
        PointerBufferSize(pStubMsg, *(unsigned char**)pMemory, pPointer);
        pStubMsg->PointerLength = pStubMsg->BufferLength;
        pStubMsg->BufferLength = saved_buffer_length;
      }
      if (*pPointer != RPC_FC_RP)
      {
        ALIGN_LENGTH(pStubMsg->BufferLength, 4);
        safe_buffer_length_increment(pStubMsg, 4);
      }
      pPointer += 4;
      pMemory += 4;
      break;
    case RPC_FC_ALIGNM4:
      ALIGN_POINTER(pMemory, 4);
      break;
    case RPC_FC_ALIGNM8:
      ALIGN_POINTER(pMemory, 8);
      break;
    case RPC_FC_STRUCTPAD1:
    case RPC_FC_STRUCTPAD2:
    case RPC_FC_STRUCTPAD3:
    case RPC_FC_STRUCTPAD4:
    case RPC_FC_STRUCTPAD5:
    case RPC_FC_STRUCTPAD6:
    case RPC_FC_STRUCTPAD7:
      pMemory += *pFormat - RPC_FC_STRUCTPAD1 + 1;
      break;
    case RPC_FC_EMBEDDED_COMPLEX:
      pMemory += pFormat[1];
      pFormat += 2;
      desc = pFormat + *(const SHORT*)pFormat;
      size = EmbeddedComplexSize(pStubMsg, desc);
      m = NdrBufferSizer[*desc & NDR_TABLE_MASK];
      if (m)
      {
        /* for some reason interface pointers aren't generated as
         * RPC_FC_POINTER, but instead as RPC_FC_EMBEDDED_COMPLEX, yet
         * they still need the derefencing treatment that pointers are
         * given */
        if (*desc == RPC_FC_IP)
          m(pStubMsg, *(unsigned char **)pMemory, desc);
        else
          m(pStubMsg, pMemory, desc);
      }
      else FIXME("no buffersizer for embedded type %02x\n", *desc);
      pMemory += size;
      pFormat += 2;
      continue;
    case RPC_FC_PAD:
      break;
    default:
      FIXME("unhandled format 0x%02x\n", *pFormat);
    }
    pFormat++;
  }

  return pMemory;
}

static unsigned char * ComplexFree(PMIDL_STUB_MESSAGE pStubMsg,
                                   unsigned char *pMemory,
                                   PFORMAT_STRING pFormat,
                                   PFORMAT_STRING pPointer)
{
  PFORMAT_STRING desc;
  NDR_FREE m;
  unsigned long size;

  while (*pFormat != RPC_FC_END) {
    switch (*pFormat) {
    case RPC_FC_BYTE:
    case RPC_FC_CHAR:
    case RPC_FC_SMALL:
    case RPC_FC_USMALL:
      pMemory += 1;
      break;
    case RPC_FC_WCHAR:
    case RPC_FC_SHORT:
    case RPC_FC_USHORT:
      pMemory += 2;
      break;
    case RPC_FC_LONG:
    case RPC_FC_ULONG:
    case RPC_FC_ENUM16:
    case RPC_FC_ENUM32:
      pMemory += 4;
      break;
    case RPC_FC_HYPER:
      pMemory += 8;
      break;
    case RPC_FC_POINTER:
      NdrPointerFree(pStubMsg, *(unsigned char**)pMemory, pPointer);
      pPointer += 4;
      pMemory += 4;
      break;
    case RPC_FC_ALIGNM4:
      ALIGN_POINTER(pMemory, 4);
      break;
    case RPC_FC_ALIGNM8:
      ALIGN_POINTER(pMemory, 8);
      break;
    case RPC_FC_STRUCTPAD1:
    case RPC_FC_STRUCTPAD2:
    case RPC_FC_STRUCTPAD3:
    case RPC_FC_STRUCTPAD4:
    case RPC_FC_STRUCTPAD5:
    case RPC_FC_STRUCTPAD6:
    case RPC_FC_STRUCTPAD7:
      pMemory += *pFormat - RPC_FC_STRUCTPAD1 + 1;
      break;
    case RPC_FC_EMBEDDED_COMPLEX:
      pMemory += pFormat[1];
      pFormat += 2;
      desc = pFormat + *(const SHORT*)pFormat;
      size = EmbeddedComplexSize(pStubMsg, desc);
      m = NdrFreer[*desc & NDR_TABLE_MASK];
      if (m)
      {
        /* for some reason interface pointers aren't generated as
         * RPC_FC_POINTER, but instead as RPC_FC_EMBEDDED_COMPLEX, yet
         * they still need the derefencing treatment that pointers are
         * given */
        if (*desc == RPC_FC_IP)
          m(pStubMsg, *(unsigned char **)pMemory, desc);
        else
          m(pStubMsg, pMemory, desc);
      }
      pMemory += size;
      pFormat += 2;
      continue;
    case RPC_FC_PAD:
      break;
    default:
      FIXME("unhandled format 0x%02x\n", *pFormat);
    }
    pFormat++;
  }

  return pMemory;
}

static unsigned long ComplexStructMemorySize(PMIDL_STUB_MESSAGE pStubMsg,
                                             PFORMAT_STRING pFormat,
                                             PFORMAT_STRING pPointer)
{
  PFORMAT_STRING desc;
  unsigned long size = 0;

  while (*pFormat != RPC_FC_END) {
    switch (*pFormat) {
    case RPC_FC_BYTE:
    case RPC_FC_CHAR:
    case RPC_FC_SMALL:
    case RPC_FC_USMALL:
      size += 1;
      safe_buffer_increment(pStubMsg, 1);
      break;
    case RPC_FC_WCHAR:
    case RPC_FC_SHORT:
    case RPC_FC_USHORT:
      size += 2;
      safe_buffer_increment(pStubMsg, 2);
      break;
    case RPC_FC_ENUM16:
      size += 4;
      safe_buffer_increment(pStubMsg, 2);
      break;
    case RPC_FC_LONG:
    case RPC_FC_ULONG:
    case RPC_FC_ENUM32:
      size += 4;
      safe_buffer_increment(pStubMsg, 4);
      break;
    case RPC_FC_HYPER:
      size += 8;
      safe_buffer_increment(pStubMsg, 8);
      break;
    case RPC_FC_POINTER:
    {
      unsigned char *saved_buffer;
      int pointer_buffer_mark_set = 0;
      if (*pPointer != RPC_FC_RP)
        ALIGN_POINTER(pStubMsg->Buffer, 4);
      saved_buffer = pStubMsg->Buffer;
      if (pStubMsg->PointerBufferMark)
      {
        pStubMsg->Buffer = pStubMsg->PointerBufferMark;
        pStubMsg->PointerBufferMark = NULL;
        pointer_buffer_mark_set = 1;
      }
      else if (*pPointer != RPC_FC_RP)
        safe_buffer_increment(pStubMsg, 4); /* for pointer ID */

      if (!pStubMsg->IgnoreEmbeddedPointers)
        PointerMemorySize(pStubMsg, saved_buffer, pPointer);
      if (pointer_buffer_mark_set)
      {
        STD_OVERFLOW_CHECK(pStubMsg);
        pStubMsg->PointerBufferMark = pStubMsg->Buffer;
        pStubMsg->Buffer = saved_buffer;
        if (*pPointer != RPC_FC_RP)
          safe_buffer_increment(pStubMsg, 4); /* for pointer ID */
      }
      pPointer += 4;
      size += 4;
      break;
    }
    case RPC_FC_ALIGNM4:
      ALIGN_LENGTH(size, 4);
      ALIGN_POINTER(pStubMsg->Buffer, 4);
      break;
    case RPC_FC_ALIGNM8:
      ALIGN_LENGTH(size, 8);
      ALIGN_POINTER(pStubMsg->Buffer, 8);
      break;
    case RPC_FC_STRUCTPAD1:
    case RPC_FC_STRUCTPAD2:
    case RPC_FC_STRUCTPAD3:
    case RPC_FC_STRUCTPAD4:
    case RPC_FC_STRUCTPAD5:
    case RPC_FC_STRUCTPAD6:
    case RPC_FC_STRUCTPAD7:
      size += *pFormat - RPC_FC_STRUCTPAD1 + 1;
      break;
    case RPC_FC_EMBEDDED_COMPLEX:
      size += pFormat[1];
      pFormat += 2;
      desc = pFormat + *(const SHORT*)pFormat;
      size += EmbeddedComplexMemorySize(pStubMsg, desc);
      pFormat += 2;
      continue;
    case RPC_FC_PAD:
      break;
    default:
      FIXME("unhandled format 0x%02x\n", *pFormat);
    }
    pFormat++;
  }

  return size;
}

unsigned long ComplexStructSize(PMIDL_STUB_MESSAGE pStubMsg,
                                PFORMAT_STRING pFormat)
{
  PFORMAT_STRING desc;
  unsigned long size = 0;

  while (*pFormat != RPC_FC_END) {
    switch (*pFormat) {
    case RPC_FC_BYTE:
    case RPC_FC_CHAR:
    case RPC_FC_SMALL:
    case RPC_FC_USMALL:
      size += 1;
      break;
    case RPC_FC_WCHAR:
    case RPC_FC_SHORT:
    case RPC_FC_USHORT:
      size += 2;
      break;
    case RPC_FC_LONG:
    case RPC_FC_ULONG:
    case RPC_FC_ENUM16:
    case RPC_FC_ENUM32:
      size += 4;
      break;
    case RPC_FC_HYPER:
      size += 8;
      break;
    case RPC_FC_POINTER:
      size += sizeof(void *);
      break;
    case RPC_FC_ALIGNM4:
      ALIGN_LENGTH(size, 4);
      break;
    case RPC_FC_ALIGNM8:
      ALIGN_LENGTH(size, 8);
      break;
    case RPC_FC_STRUCTPAD1:
    case RPC_FC_STRUCTPAD2:
    case RPC_FC_STRUCTPAD3:
    case RPC_FC_STRUCTPAD4:
    case RPC_FC_STRUCTPAD5:
    case RPC_FC_STRUCTPAD6:
    case RPC_FC_STRUCTPAD7:
      size += *pFormat - RPC_FC_STRUCTPAD1 + 1;
      break;
    case RPC_FC_EMBEDDED_COMPLEX:
      size += pFormat[1];
      pFormat += 2;
      desc = pFormat + *(const SHORT*)pFormat;
      size += EmbeddedComplexSize(pStubMsg, desc);
      pFormat += 2;
      continue;
    case RPC_FC_PAD:
      break;
    default:
      FIXME("unhandled format 0x%02x\n", *pFormat);
    }
    pFormat++;
  }

  return size;
}

/***********************************************************************
 *           NdrComplexStructMarshall [RPCRT4.@]
 */
unsigned char * WINAPI NdrComplexStructMarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                                unsigned char *pMemory,
                                                PFORMAT_STRING pFormat)
{
  PFORMAT_STRING conf_array = NULL;
  PFORMAT_STRING pointer_desc = NULL;
  unsigned char *OldMemory = pStubMsg->Memory;
  int pointer_buffer_mark_set = 0;

  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);

  if (!pStubMsg->PointerBufferMark)
  {
    int saved_ignore_embedded = pStubMsg->IgnoreEmbeddedPointers;
    /* save buffer length */
    unsigned long saved_buffer_length = pStubMsg->BufferLength;

    /* get the buffer pointer after complex array data, but before
     * pointer data */
    pStubMsg->BufferLength = pStubMsg->Buffer - pStubMsg->BufferStart;
    pStubMsg->IgnoreEmbeddedPointers = 1;
    NdrComplexStructBufferSize(pStubMsg, pMemory, pFormat);
    pStubMsg->IgnoreEmbeddedPointers = saved_ignore_embedded;

    /* save it for use by embedded pointer code later */
    pStubMsg->PointerBufferMark = pStubMsg->BufferStart + pStubMsg->BufferLength;
    TRACE("difference = 0x%x\n", pStubMsg->PointerBufferMark - pStubMsg->Buffer);
    pointer_buffer_mark_set = 1;

    /* restore the original buffer length */
    pStubMsg->BufferLength = saved_buffer_length;
  }

  ALIGN_POINTER_CLEAR(pStubMsg->Buffer, pFormat[1] + 1);

  pFormat += 4;
  if (*(const SHORT*)pFormat) conf_array = pFormat + *(const SHORT*)pFormat;
  pFormat += 2;
  if (*(const WORD*)pFormat) pointer_desc = pFormat + *(const WORD*)pFormat;
  pFormat += 2;

  pStubMsg->Memory = pMemory;

  pMemory = ComplexMarshall(pStubMsg, pMemory, pFormat, pointer_desc);

  if (conf_array)
    NdrConformantArrayMarshall(pStubMsg, pMemory, conf_array);

  pStubMsg->Memory = OldMemory;

  if (pointer_buffer_mark_set)
  {
    pStubMsg->Buffer = pStubMsg->PointerBufferMark;
    pStubMsg->PointerBufferMark = NULL;
  }

  STD_OVERFLOW_CHECK(pStubMsg);

  return NULL;
}

/***********************************************************************
 *           NdrComplexStructUnmarshall [RPCRT4.@]
 */
unsigned char * WINAPI NdrComplexStructUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                                  unsigned char **ppMemory,
                                                  PFORMAT_STRING pFormat,
                                                  unsigned char fMustAlloc)
{
  unsigned size = *(const WORD*)(pFormat+2);
  PFORMAT_STRING conf_array = NULL;
  PFORMAT_STRING pointer_desc = NULL;
  unsigned char *pMemory;
  int pointer_buffer_mark_set = 0;

  TRACE("(%p,%p,%p,%d)\n", pStubMsg, ppMemory, pFormat, fMustAlloc);

  if (!pStubMsg->PointerBufferMark)
  {
    int saved_ignore_embedded = pStubMsg->IgnoreEmbeddedPointers;
    /* save buffer pointer */
    unsigned char *saved_buffer = pStubMsg->Buffer;

    /* get the buffer pointer after complex array data, but before
     * pointer data */
    pStubMsg->IgnoreEmbeddedPointers = 1;
    NdrComplexStructMemorySize(pStubMsg, pFormat);
    pStubMsg->IgnoreEmbeddedPointers = saved_ignore_embedded;

    /* save it for use by embedded pointer code later */
    pStubMsg->PointerBufferMark = pStubMsg->Buffer;
    TRACE("difference = 0x%lx\n", (unsigned long)(pStubMsg->PointerBufferMark - saved_buffer));
    pointer_buffer_mark_set = 1;

    /* restore the original buffer */
    pStubMsg->Buffer = saved_buffer;
  }

  ALIGN_POINTER(pStubMsg->Buffer, pFormat[1] + 1);

  if (fMustAlloc || !*ppMemory)
    *ppMemory = NdrAllocate(pStubMsg, size);

  pFormat += 4;
  if (*(const SHORT*)pFormat) conf_array = pFormat + *(const SHORT*)pFormat;
  pFormat += 2;
  if (*(const WORD*)pFormat) pointer_desc = pFormat + *(const WORD*)pFormat;
  pFormat += 2;

  pMemory = ComplexUnmarshall(pStubMsg, *ppMemory, pFormat, pointer_desc, fMustAlloc);

  if (conf_array)
    NdrConformantArrayUnmarshall(pStubMsg, &pMemory, conf_array, fMustAlloc);

  if (pointer_buffer_mark_set)
  {
    pStubMsg->Buffer = pStubMsg->PointerBufferMark;
    pStubMsg->PointerBufferMark = NULL;
  }

  return NULL;
}

/***********************************************************************
 *           NdrComplexStructBufferSize [RPCRT4.@]
 */
void WINAPI NdrComplexStructBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
                                       unsigned char *pMemory,
                                       PFORMAT_STRING pFormat)
{
  PFORMAT_STRING conf_array = NULL;
  PFORMAT_STRING pointer_desc = NULL;
  unsigned char *OldMemory = pStubMsg->Memory;
  int pointer_length_set = 0;

  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);

  ALIGN_LENGTH(pStubMsg->BufferLength, pFormat[1] + 1);

  if(!pStubMsg->IgnoreEmbeddedPointers && !pStubMsg->PointerLength)
  {
    int saved_ignore_embedded = pStubMsg->IgnoreEmbeddedPointers;
    unsigned long saved_buffer_length = pStubMsg->BufferLength;

    /* get the buffer length after complex struct data, but before
     * pointer data */
    pStubMsg->IgnoreEmbeddedPointers = 1;
    NdrComplexStructBufferSize(pStubMsg, pMemory, pFormat);
    pStubMsg->IgnoreEmbeddedPointers = saved_ignore_embedded;

    /* save it for use by embedded pointer code later */
    pStubMsg->PointerLength = pStubMsg->BufferLength;
    pointer_length_set = 1;
    TRACE("difference = 0x%lx\n", pStubMsg->PointerLength - saved_buffer_length);

    /* restore the original buffer length */
    pStubMsg->BufferLength = saved_buffer_length;
  }

  pFormat += 4;
  if (*(const SHORT*)pFormat) conf_array = pFormat + *(const SHORT*)pFormat;
  pFormat += 2;
  if (*(const WORD*)pFormat) pointer_desc = pFormat + *(const WORD*)pFormat;
  pFormat += 2;

  pStubMsg->Memory = pMemory;

  pMemory = ComplexBufferSize(pStubMsg, pMemory, pFormat, pointer_desc);

  if (conf_array)
    NdrConformantArrayBufferSize(pStubMsg, pMemory, conf_array);

  pStubMsg->Memory = OldMemory;

  if(pointer_length_set)
  {
    pStubMsg->BufferLength = pStubMsg->PointerLength;
    pStubMsg->PointerLength = 0;
  }

}

/***********************************************************************
 *           NdrComplexStructMemorySize [RPCRT4.@]
 */
ULONG WINAPI NdrComplexStructMemorySize(PMIDL_STUB_MESSAGE pStubMsg,
                                        PFORMAT_STRING pFormat)
{
  unsigned size = *(const WORD*)(pFormat+2);
  PFORMAT_STRING conf_array = NULL;
  PFORMAT_STRING pointer_desc = NULL;

  TRACE("(%p,%p)\n", pStubMsg, pFormat);

  ALIGN_POINTER(pStubMsg->Buffer, pFormat[1] + 1);

  pFormat += 4;
  if (*(const SHORT*)pFormat) conf_array = pFormat + *(const SHORT*)pFormat;
  pFormat += 2;
  if (*(const WORD*)pFormat) pointer_desc = pFormat + *(const WORD*)pFormat;
  pFormat += 2;

  ComplexStructMemorySize(pStubMsg, pFormat, pointer_desc);

  if (conf_array)
    NdrConformantArrayMemorySize(pStubMsg, conf_array);

  return size;
}

/***********************************************************************
 *           NdrComplexStructFree [RPCRT4.@]
 */
void WINAPI NdrComplexStructFree(PMIDL_STUB_MESSAGE pStubMsg,
                                 unsigned char *pMemory,
                                 PFORMAT_STRING pFormat)
{
  PFORMAT_STRING conf_array = NULL;
  PFORMAT_STRING pointer_desc = NULL;
  unsigned char *OldMemory = pStubMsg->Memory;

  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);

  pFormat += 4;
  if (*(const SHORT*)pFormat) conf_array = pFormat + *(const SHORT*)pFormat;
  pFormat += 2;
  if (*(const WORD*)pFormat) pointer_desc = pFormat + *(const WORD*)pFormat;
  pFormat += 2;

  pStubMsg->Memory = pMemory;

  pMemory = ComplexFree(pStubMsg, pMemory, pFormat, pointer_desc);

  if (conf_array)
    NdrConformantArrayFree(pStubMsg, pMemory, conf_array);

  pStubMsg->Memory = OldMemory;
}

/***********************************************************************
 *           NdrConformantArrayMarshall [RPCRT4.@]
 */
unsigned char * WINAPI NdrConformantArrayMarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                                  unsigned char *pMemory,
                                                  PFORMAT_STRING pFormat)
{
  DWORD size = 0, esize = *(const WORD*)(pFormat+2);
  unsigned char alignment = pFormat[1] + 1;

  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
  if (pFormat[0] != RPC_FC_CARRAY) FIXME("format=%d\n", pFormat[0]);

  pFormat = ComputeConformance(pStubMsg, pMemory, pFormat+4, 0);

  WriteConformance(pStubMsg);

  ALIGN_POINTER_CLEAR(pStubMsg->Buffer, alignment);

  size = safe_multiply(esize, pStubMsg->MaxCount);
  pStubMsg->BufferMark = pStubMsg->Buffer;
  safe_copy_to_buffer(pStubMsg, pMemory, size);

  EmbeddedPointerMarshall(pStubMsg, pMemory, pFormat);

  return NULL;
}

/***********************************************************************
 *           NdrConformantArrayUnmarshall [RPCRT4.@]
 */
unsigned char * WINAPI NdrConformantArrayUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                                    unsigned char **ppMemory,
                                                    PFORMAT_STRING pFormat,
                                                    unsigned char fMustAlloc)
{
  DWORD size, esize = *(const WORD*)(pFormat+2);
  unsigned char alignment = pFormat[1] + 1;
  unsigned char *saved_buffer;

  TRACE("(%p,%p,%p,%d)\n", pStubMsg, ppMemory, pFormat, fMustAlloc);
  if (pFormat[0] != RPC_FC_CARRAY) FIXME("format=%d\n", pFormat[0]);

  pFormat = ReadConformance(pStubMsg, pFormat+4);

  size = safe_multiply(esize, pStubMsg->MaxCount);
  ALIGN_POINTER(pStubMsg->Buffer, alignment);

  if (fMustAlloc)
    *ppMemory = NdrAllocate(pStubMsg, size);
  else
  {
    if (!pStubMsg->IsClient && !*ppMemory)
      /* for servers, we just point straight into the RPC buffer */
      *ppMemory = pStubMsg->Buffer;
  }

  saved_buffer = pStubMsg->BufferMark = pStubMsg->Buffer;
  safe_buffer_increment(pStubMsg, size);
  EmbeddedPointerUnmarshall(pStubMsg, saved_buffer, *ppMemory, pFormat, fMustAlloc);

  TRACE("copying %p to %p\n", saved_buffer, *ppMemory);
  if (*ppMemory != saved_buffer)
      memcpy(*ppMemory, saved_buffer, size);

  return NULL;
}

/***********************************************************************
 *           NdrConformantArrayBufferSize [RPCRT4.@]
 */
void WINAPI NdrConformantArrayBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
                                         unsigned char *pMemory,
                                         PFORMAT_STRING pFormat)
{
  DWORD size, esize = *(const WORD*)(pFormat+2);
  unsigned char alignment = pFormat[1] + 1;

  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
  if (pFormat[0] != RPC_FC_CARRAY) FIXME("format=%d\n", pFormat[0]);

  pFormat = ComputeConformance(pStubMsg, pMemory, pFormat+4, 0);

  SizeConformance(pStubMsg);

  ALIGN_LENGTH(pStubMsg->BufferLength, alignment);

  size = safe_multiply(esize, pStubMsg->MaxCount);
  /* conformance value plus array */
  safe_buffer_length_increment(pStubMsg, size);

  EmbeddedPointerBufferSize(pStubMsg, pMemory, pFormat);
}

/***********************************************************************
 *           NdrConformantArrayMemorySize [RPCRT4.@]
 */
ULONG WINAPI NdrConformantArrayMemorySize(PMIDL_STUB_MESSAGE pStubMsg,
                                          PFORMAT_STRING pFormat)
{
  DWORD size = 0, esize = *(const WORD*)(pFormat+2);
  unsigned char alignment = pFormat[1] + 1;

  TRACE("(%p,%p)\n", pStubMsg, pFormat);
  if (pFormat[0] != RPC_FC_CARRAY) FIXME("format=%d\n", pFormat[0]);

  pFormat = ReadConformance(pStubMsg, pFormat+4);
  size = safe_multiply(esize, pStubMsg->MaxCount);
  pStubMsg->MemorySize += size;

  ALIGN_POINTER(pStubMsg->Buffer, alignment);
  pStubMsg->BufferMark = pStubMsg->Buffer;
  safe_buffer_increment(pStubMsg, size);

  EmbeddedPointerMemorySize(pStubMsg, pFormat);

  return pStubMsg->MemorySize;
}

/***********************************************************************
 *           NdrConformantArrayFree [RPCRT4.@]
 */
void WINAPI NdrConformantArrayFree(PMIDL_STUB_MESSAGE pStubMsg,
                                   unsigned char *pMemory,
                                   PFORMAT_STRING pFormat)
{
  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
  if (pFormat[0] != RPC_FC_CARRAY) FIXME("format=%d\n", pFormat[0]);

  pFormat = ComputeConformance(pStubMsg, pMemory, pFormat+4, 0);

  EmbeddedPointerFree(pStubMsg, pMemory, pFormat);
}


/***********************************************************************
 *           NdrConformantVaryingArrayMarshall  [RPCRT4.@]
 */
unsigned char* WINAPI NdrConformantVaryingArrayMarshall( PMIDL_STUB_MESSAGE pStubMsg,
                                                         unsigned char* pMemory,
                                                         PFORMAT_STRING pFormat )
{
    ULONG bufsize;
    unsigned char alignment = pFormat[1] + 1;
    DWORD esize = *(const WORD*)(pFormat+2);

    TRACE("(%p, %p, %p)\n", pStubMsg, pMemory, pFormat);

    if (pFormat[0] != RPC_FC_CVARRAY)
    {
        ERR("invalid format type %x\n", pFormat[0]);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return NULL;
    }

    pFormat = ComputeConformance(pStubMsg, pMemory, pFormat+4, 0);
    pFormat = ComputeVariance(pStubMsg, pMemory, pFormat, 0);

    WriteConformance(pStubMsg);
    WriteVariance(pStubMsg);

    ALIGN_POINTER_CLEAR(pStubMsg->Buffer, alignment);

    bufsize = safe_multiply(esize, pStubMsg->ActualCount);

    pStubMsg->BufferMark = pStubMsg->Buffer;
    safe_copy_to_buffer(pStubMsg, pMemory + pStubMsg->Offset, bufsize);

    EmbeddedPointerMarshall(pStubMsg, pMemory, pFormat);

    return NULL;
}


/***********************************************************************
 *           NdrConformantVaryingArrayUnmarshall  [RPCRT4.@]
 */
unsigned char* WINAPI NdrConformantVaryingArrayUnmarshall( PMIDL_STUB_MESSAGE pStubMsg,
                                                           unsigned char** ppMemory,
                                                           PFORMAT_STRING pFormat,
                                                           unsigned char fMustAlloc )
{
    ULONG bufsize, memsize;
    unsigned char alignment = pFormat[1] + 1;
    DWORD esize = *(const WORD*)(pFormat+2);
    unsigned char *saved_buffer;
    ULONG offset;

    TRACE("(%p, %p, %p, %d)\n", pStubMsg, ppMemory, pFormat, fMustAlloc);

    if (pFormat[0] != RPC_FC_CVARRAY)
    {
        ERR("invalid format type %x\n", pFormat[0]);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return NULL;
    }

    pFormat = ReadConformance(pStubMsg, pFormat+4);
    pFormat = ReadVariance(pStubMsg, pFormat, pStubMsg->MaxCount);

    ALIGN_POINTER(pStubMsg->Buffer, alignment);

    bufsize = safe_multiply(esize, pStubMsg->ActualCount);
    memsize = safe_multiply(esize, pStubMsg->MaxCount);
    offset = pStubMsg->Offset;

    if (!*ppMemory || fMustAlloc)
        *ppMemory = NdrAllocate(pStubMsg, memsize);
    saved_buffer = pStubMsg->BufferMark = pStubMsg->Buffer;
    safe_buffer_increment(pStubMsg, bufsize);

    EmbeddedPointerUnmarshall(pStubMsg, saved_buffer, *ppMemory, pFormat, fMustAlloc);

    memcpy(*ppMemory + offset, saved_buffer, bufsize);

    return NULL;
}


/***********************************************************************
 *           NdrConformantVaryingArrayFree  [RPCRT4.@]
 */
void WINAPI NdrConformantVaryingArrayFree( PMIDL_STUB_MESSAGE pStubMsg,
                                           unsigned char* pMemory,
                                           PFORMAT_STRING pFormat )
{
    TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);

    if (pFormat[0] != RPC_FC_CVARRAY)
    {
        ERR("invalid format type %x\n", pFormat[0]);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return;
    }

    pFormat = ComputeConformance(pStubMsg, pMemory, pFormat+4, 0);
    pFormat = ComputeVariance(pStubMsg, pMemory, pFormat, 0);

    EmbeddedPointerFree(pStubMsg, pMemory, pFormat);
}


/***********************************************************************
 *           NdrConformantVaryingArrayBufferSize  [RPCRT4.@]
 */
void WINAPI NdrConformantVaryingArrayBufferSize( PMIDL_STUB_MESSAGE pStubMsg,
                                                 unsigned char* pMemory, PFORMAT_STRING pFormat )
{
    unsigned char alignment = pFormat[1] + 1;
    DWORD esize = *(const WORD*)(pFormat+2);

    TRACE("(%p, %p, %p)\n", pStubMsg, pMemory, pFormat);

    if (pFormat[0] != RPC_FC_CVARRAY)
    {
        ERR("invalid format type %x\n", pFormat[0]);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return;
    }

    /* compute size */
    pFormat = ComputeConformance(pStubMsg, pMemory, pFormat+4, 0);
    /* compute length */
    pFormat = ComputeVariance(pStubMsg, pMemory, pFormat, 0);

    SizeConformance(pStubMsg);
    SizeVariance(pStubMsg);

    ALIGN_LENGTH(pStubMsg->BufferLength, alignment);

    safe_buffer_length_increment(pStubMsg, safe_multiply(esize, pStubMsg->ActualCount));

    EmbeddedPointerBufferSize(pStubMsg, pMemory, pFormat);
}


/***********************************************************************
 *           NdrConformantVaryingArrayMemorySize  [RPCRT4.@]
 */
ULONG WINAPI NdrConformantVaryingArrayMemorySize( PMIDL_STUB_MESSAGE pStubMsg,
                                                  PFORMAT_STRING pFormat )
{
    ULONG bufsize, memsize;
    unsigned char alignment = pFormat[1] + 1;
    DWORD esize = *(const WORD*)(pFormat+2);

    TRACE("(%p, %p)\n", pStubMsg, pFormat);

    if (pFormat[0] != RPC_FC_CVARRAY)
    {
        ERR("invalid format type %x\n", pFormat[0]);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return pStubMsg->MemorySize;
    }

    pFormat = ReadConformance(pStubMsg, pFormat+4);
    pFormat = ReadVariance(pStubMsg, pFormat, pStubMsg->MaxCount);

    ALIGN_POINTER(pStubMsg->Buffer, alignment);

    bufsize = safe_multiply(esize, pStubMsg->ActualCount);
    memsize = safe_multiply(esize, pStubMsg->MaxCount);

    safe_buffer_increment(pStubMsg, bufsize);
    pStubMsg->MemorySize += memsize;

    EmbeddedPointerMemorySize(pStubMsg, pFormat);

    return pStubMsg->MemorySize;
}


/***********************************************************************
 *           NdrComplexArrayMarshall [RPCRT4.@]
 */
unsigned char * WINAPI NdrComplexArrayMarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                               unsigned char *pMemory,
                                               PFORMAT_STRING pFormat)
{
  ULONG i, count, def;
  BOOL variance_present;
  unsigned char alignment;
  int pointer_buffer_mark_set = 0;

  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);

  if (pFormat[0] != RPC_FC_BOGUS_ARRAY)
  {
      ERR("invalid format type %x\n", pFormat[0]);
      RpcRaiseException(RPC_S_INTERNAL_ERROR);
      return NULL;
  }

  alignment = pFormat[1] + 1;

  if (!pStubMsg->PointerBufferMark)
  {
    /* save buffer fields that may be changed by buffer sizer functions
     * and that may be needed later on */
    int saved_ignore_embedded = pStubMsg->IgnoreEmbeddedPointers;
    unsigned long saved_buffer_length = pStubMsg->BufferLength;
    unsigned long saved_max_count = pStubMsg->MaxCount;
    unsigned long saved_offset = pStubMsg->Offset;
    unsigned long saved_actual_count = pStubMsg->ActualCount;

    /* get the buffer pointer after complex array data, but before
     * pointer data */
    pStubMsg->BufferLength = pStubMsg->Buffer - pStubMsg->BufferStart;
    pStubMsg->IgnoreEmbeddedPointers = 1;
    NdrComplexArrayBufferSize(pStubMsg, pMemory, pFormat);
    pStubMsg->IgnoreEmbeddedPointers = saved_ignore_embedded;

    /* save it for use by embedded pointer code later */
    pStubMsg->PointerBufferMark = pStubMsg->BufferStart + pStubMsg->BufferLength;
    TRACE("difference = 0x%x\n", pStubMsg->Buffer - pStubMsg->BufferStart);
    pointer_buffer_mark_set = 1;

    /* restore fields */
    pStubMsg->ActualCount = saved_actual_count;
    pStubMsg->Offset = saved_offset;
    pStubMsg->MaxCount = saved_max_count;
    pStubMsg->BufferLength = saved_buffer_length;
  }

  def = *(const WORD*)&pFormat[2];
  pFormat += 4;

  pFormat = ComputeConformance(pStubMsg, pMemory, pFormat, def);
  TRACE("conformance = %ld\n", pStubMsg->MaxCount);

  variance_present = IsConformanceOrVariancePresent(pFormat);
  pFormat = ComputeVariance(pStubMsg, pMemory, pFormat, pStubMsg->MaxCount);
  TRACE("variance = %d\n", pStubMsg->ActualCount);

  WriteConformance(pStubMsg);
  if (variance_present)
    WriteVariance(pStubMsg);

  ALIGN_POINTER_CLEAR(pStubMsg->Buffer, alignment);

  count = pStubMsg->ActualCount;
  for (i = 0; i < count; i++)
    pMemory = ComplexMarshall(pStubMsg, pMemory, pFormat, NULL);

  STD_OVERFLOW_CHECK(pStubMsg);

  if (pointer_buffer_mark_set)
  {
    pStubMsg->Buffer = pStubMsg->PointerBufferMark;
    pStubMsg->PointerBufferMark = NULL;
  }

  return NULL;
}

/***********************************************************************
 *           NdrComplexArrayUnmarshall [RPCRT4.@]
 */
unsigned char * WINAPI NdrComplexArrayUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                                 unsigned char **ppMemory,
                                                 PFORMAT_STRING pFormat,
                                                 unsigned char fMustAlloc)
{
  ULONG i, count, size;
  unsigned char alignment;
  unsigned char *pMemory;
  unsigned char *saved_buffer;
  int pointer_buffer_mark_set = 0;
  int saved_ignore_embedded;

  TRACE("(%p,%p,%p,%d)\n", pStubMsg, ppMemory, pFormat, fMustAlloc);

  if (pFormat[0] != RPC_FC_BOGUS_ARRAY)
  {
      ERR("invalid format type %x\n", pFormat[0]);
      RpcRaiseException(RPC_S_INTERNAL_ERROR);
      return NULL;
  }

  alignment = pFormat[1] + 1;

  saved_ignore_embedded = pStubMsg->IgnoreEmbeddedPointers;
  /* save buffer pointer */
  saved_buffer = pStubMsg->Buffer;
  /* get the buffer pointer after complex array data, but before
   * pointer data */
  pStubMsg->IgnoreEmbeddedPointers = 1;
  pStubMsg->MemorySize = 0;
  NdrComplexArrayMemorySize(pStubMsg, pFormat);
  size = pStubMsg->MemorySize;
  pStubMsg->IgnoreEmbeddedPointers = saved_ignore_embedded;

  TRACE("difference = 0x%lx\n", (unsigned long)(pStubMsg->Buffer - saved_buffer));
  if (!pStubMsg->PointerBufferMark)
  {
    /* save it for use by embedded pointer code later */
    pStubMsg->PointerBufferMark = pStubMsg->Buffer;
    pointer_buffer_mark_set = 1;
  }
  /* restore the original buffer */
  pStubMsg->Buffer = saved_buffer;

  pFormat += 4;

  pFormat = ReadConformance(pStubMsg, pFormat);
  pFormat = ReadVariance(pStubMsg, pFormat, pStubMsg->MaxCount);

  if (fMustAlloc || !*ppMemory)
    *ppMemory = NdrAllocate(pStubMsg, size);

  ALIGN_POINTER(pStubMsg->Buffer, alignment);

  pMemory = *ppMemory;
  count = pStubMsg->ActualCount;
  for (i = 0; i < count; i++)
    pMemory = ComplexUnmarshall(pStubMsg, pMemory, pFormat, NULL, fMustAlloc);

  if (pointer_buffer_mark_set)
  {
    pStubMsg->Buffer = pStubMsg->PointerBufferMark;
    pStubMsg->PointerBufferMark = NULL;
  }

  return NULL;
}

/***********************************************************************
 *           NdrComplexArrayBufferSize [RPCRT4.@]
 */
void WINAPI NdrComplexArrayBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
                                      unsigned char *pMemory,
                                      PFORMAT_STRING pFormat)
{
  ULONG i, count, def;
  unsigned char alignment;
  BOOL variance_present;
  int pointer_length_set = 0;

  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);

  if (pFormat[0] != RPC_FC_BOGUS_ARRAY)
  {
      ERR("invalid format type %x\n", pFormat[0]);
      RpcRaiseException(RPC_S_INTERNAL_ERROR);
      return;
  }

  alignment = pFormat[1] + 1;

  if (!pStubMsg->IgnoreEmbeddedPointers && !pStubMsg->PointerLength)
  {
    /* save buffer fields that may be changed by buffer sizer functions
     * and that may be needed later on */
    int saved_ignore_embedded = pStubMsg->IgnoreEmbeddedPointers;
    unsigned long saved_buffer_length = pStubMsg->BufferLength;
    unsigned long saved_max_count = pStubMsg->MaxCount;
    unsigned long saved_offset = pStubMsg->Offset;
    unsigned long saved_actual_count = pStubMsg->ActualCount;

    /* get the buffer pointer after complex array data, but before
     * pointer data */
    pStubMsg->IgnoreEmbeddedPointers = 1;
    NdrComplexArrayBufferSize(pStubMsg, pMemory, pFormat);
    pStubMsg->IgnoreEmbeddedPointers = saved_ignore_embedded;

    /* save it for use by embedded pointer code later */
    pStubMsg->PointerLength = pStubMsg->BufferLength;
    pointer_length_set = 1;

    /* restore fields */
    pStubMsg->ActualCount = saved_actual_count;
    pStubMsg->Offset = saved_offset;
    pStubMsg->MaxCount = saved_max_count;
    pStubMsg->BufferLength = saved_buffer_length;
  }
  def = *(const WORD*)&pFormat[2];
  pFormat += 4;

  pFormat = ComputeConformance(pStubMsg, pMemory, pFormat, def);
  TRACE("conformance = %ld\n", pStubMsg->MaxCount);
  SizeConformance(pStubMsg);

  variance_present = IsConformanceOrVariancePresent(pFormat);
  pFormat = ComputeVariance(pStubMsg, pMemory, pFormat, pStubMsg->MaxCount);
  TRACE("variance = %d\n", pStubMsg->ActualCount);

  if (variance_present)
    SizeVariance(pStubMsg);

  ALIGN_LENGTH(pStubMsg->BufferLength, alignment);

  count = pStubMsg->ActualCount;
  for (i = 0; i < count; i++)
    pMemory = ComplexBufferSize(pStubMsg, pMemory, pFormat, NULL);

  if(pointer_length_set)
  {
    pStubMsg->BufferLength = pStubMsg->PointerLength;
    pStubMsg->PointerLength = 0;
  }
}

/***********************************************************************
 *           NdrComplexArrayMemorySize [RPCRT4.@]
 */
ULONG WINAPI NdrComplexArrayMemorySize(PMIDL_STUB_MESSAGE pStubMsg,
                                       PFORMAT_STRING pFormat)
{
  ULONG i, count, esize, SavedMemorySize, MemorySize;
  unsigned char alignment;

  TRACE("(%p,%p)\n", pStubMsg, pFormat);

  if (pFormat[0] != RPC_FC_BOGUS_ARRAY)
  {
      ERR("invalid format type %x\n", pFormat[0]);
      RpcRaiseException(RPC_S_INTERNAL_ERROR);
      return 0;
  }

  alignment = pFormat[1] + 1;

  pFormat += 4;

  pFormat = ReadConformance(pStubMsg, pFormat);
  pFormat = ReadVariance(pStubMsg, pFormat, pStubMsg->MaxCount);

  ALIGN_POINTER(pStubMsg->Buffer, alignment);

  SavedMemorySize = pStubMsg->MemorySize;

  esize = ComplexStructSize(pStubMsg, pFormat);

  MemorySize = safe_multiply(pStubMsg->MaxCount, esize);

  count = pStubMsg->ActualCount;
  for (i = 0; i < count; i++)
    ComplexStructMemorySize(pStubMsg, pFormat, NULL);

  pStubMsg->MemorySize = SavedMemorySize;

  pStubMsg->MemorySize += MemorySize;
  return MemorySize;
}

/***********************************************************************
 *           NdrComplexArrayFree [RPCRT4.@]
 */
void WINAPI NdrComplexArrayFree(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
  ULONG i, count, def;

  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);

  if (pFormat[0] != RPC_FC_BOGUS_ARRAY)
  {
      ERR("invalid format type %x\n", pFormat[0]);
      RpcRaiseException(RPC_S_INTERNAL_ERROR);
      return;
  }

  def = *(const WORD*)&pFormat[2];
  pFormat += 4;

  pFormat = ComputeConformance(pStubMsg, pMemory, pFormat, def);
  TRACE("conformance = %ld\n", pStubMsg->MaxCount);

  pFormat = ComputeVariance(pStubMsg, pMemory, pFormat, pStubMsg->MaxCount);
  TRACE("variance = %d\n", pStubMsg->ActualCount);

  count = pStubMsg->ActualCount;
  for (i = 0; i < count; i++)
    pMemory = ComplexFree(pStubMsg, pMemory, pFormat, NULL);
}

static void UserMarshalCB(PMIDL_STUB_MESSAGE pStubMsg,
                          USER_MARSHAL_CB_TYPE cbtype, PFORMAT_STRING pFormat,
                          USER_MARSHAL_CB *umcb)
{
  umcb->Flags = MAKELONG(pStubMsg->dwDestContext,
                         pStubMsg->RpcMsg->DataRepresentation);
  umcb->pStubMsg = pStubMsg;
  umcb->pReserve = NULL;
  umcb->Signature = USER_MARSHAL_CB_SIGNATURE;
  umcb->CBType = cbtype;
  umcb->pFormat = pFormat;
  umcb->pTypeFormat = NULL /* FIXME */;
}

#define USER_MARSHAL_PTR_PREFIX \
        ( (DWORD)'U'         | ( (DWORD)'s' << 8 ) | \
        ( (DWORD)'e' << 16 ) | ( (DWORD)'r' << 24 ) )

/***********************************************************************
 *           NdrUserMarshalMarshall [RPCRT4.@]
 */
unsigned char * WINAPI NdrUserMarshalMarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                              unsigned char *pMemory,
                                              PFORMAT_STRING pFormat)
{
  unsigned flags = pFormat[1];
  unsigned index = *(const WORD*)&pFormat[2];
  unsigned char *saved_buffer = NULL;
  USER_MARSHAL_CB umcb;

  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
  TRACE("index=%d\n", index);

  UserMarshalCB(pStubMsg, USER_MARSHAL_CB_MARSHALL, pFormat, &umcb);

  if (flags & USER_MARSHAL_POINTER)
  {
    ALIGN_POINTER_CLEAR(pStubMsg->Buffer, 4);
    NDR_LOCAL_UINT32_WRITE(pStubMsg->Buffer, USER_MARSHAL_PTR_PREFIX);
    pStubMsg->Buffer += 4;
    if (pStubMsg->PointerBufferMark)
    {
      saved_buffer = pStubMsg->Buffer;
      pStubMsg->Buffer = pStubMsg->PointerBufferMark;
      pStubMsg->PointerBufferMark = NULL;
    }
    ALIGN_POINTER_CLEAR(pStubMsg->Buffer, 8);
  }
  else
    ALIGN_POINTER_CLEAR(pStubMsg->Buffer, (flags & 0xf) + 1);

  pStubMsg->Buffer =
    pStubMsg->StubDesc->aUserMarshalQuadruple[index].pfnMarshall(
      &umcb.Flags, pStubMsg->Buffer, pMemory);

  if (saved_buffer)
  {
    STD_OVERFLOW_CHECK(pStubMsg);
    pStubMsg->PointerBufferMark = pStubMsg->Buffer;
    pStubMsg->Buffer = saved_buffer;
  }

  STD_OVERFLOW_CHECK(pStubMsg);

  return NULL;
}

/***********************************************************************
 *           NdrUserMarshalUnmarshall [RPCRT4.@]
 */
unsigned char * WINAPI NdrUserMarshalUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                                 unsigned char **ppMemory,
                                                 PFORMAT_STRING pFormat,
                                                 unsigned char fMustAlloc)
{
  unsigned flags = pFormat[1];
  unsigned index = *(const WORD*)&pFormat[2];
  DWORD memsize = *(const WORD*)&pFormat[4];
  unsigned char *saved_buffer = NULL;
  USER_MARSHAL_CB umcb;

  TRACE("(%p,%p,%p,%d)\n", pStubMsg, ppMemory, pFormat, fMustAlloc);
  TRACE("index=%d\n", index);

  UserMarshalCB(pStubMsg, USER_MARSHAL_CB_UNMARSHALL, pFormat, &umcb);

  if (flags & USER_MARSHAL_POINTER)
  {
    ALIGN_POINTER(pStubMsg->Buffer, 4);
    /* skip pointer prefix */
    pStubMsg->Buffer += 4;
    if (pStubMsg->PointerBufferMark)
    {
      saved_buffer = pStubMsg->Buffer;
      pStubMsg->Buffer = pStubMsg->PointerBufferMark;
      pStubMsg->PointerBufferMark = NULL;
    }
    ALIGN_POINTER(pStubMsg->Buffer, 8);
  }
  else
    ALIGN_POINTER(pStubMsg->Buffer, (flags & 0xf) + 1);

  if (fMustAlloc || !*ppMemory)
    *ppMemory = NdrAllocate(pStubMsg, memsize);

  pStubMsg->Buffer =
    pStubMsg->StubDesc->aUserMarshalQuadruple[index].pfnUnmarshall(
      &umcb.Flags, pStubMsg->Buffer, *ppMemory);

  if (saved_buffer)
  {
    STD_OVERFLOW_CHECK(pStubMsg);
    pStubMsg->PointerBufferMark = pStubMsg->Buffer;
    pStubMsg->Buffer = saved_buffer;
  }

  return NULL;
}

/***********************************************************************
 *           NdrUserMarshalBufferSize [RPCRT4.@]
 */
void WINAPI NdrUserMarshalBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
                                      unsigned char *pMemory,
                                      PFORMAT_STRING pFormat)
{
  unsigned flags = pFormat[1];
  unsigned index = *(const WORD*)&pFormat[2];
  DWORD bufsize = *(const WORD*)&pFormat[6];
  USER_MARSHAL_CB umcb;
  unsigned long saved_buffer_length = 0;

  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
  TRACE("index=%d\n", index);

  UserMarshalCB(pStubMsg, USER_MARSHAL_CB_BUFFER_SIZE, pFormat, &umcb);

  if (flags & USER_MARSHAL_POINTER)
  {
    ALIGN_LENGTH(pStubMsg->BufferLength, 4);
    /* skip pointer prefix */
    safe_buffer_length_increment(pStubMsg, 4);
    if (pStubMsg->IgnoreEmbeddedPointers)
      return;
    if (pStubMsg->PointerLength)
    {
      saved_buffer_length = pStubMsg->BufferLength;
      pStubMsg->BufferLength = pStubMsg->PointerLength;
      pStubMsg->PointerLength = 0;
    }
    ALIGN_LENGTH(pStubMsg->BufferLength, 8);
  }
  else
    ALIGN_LENGTH(pStubMsg->BufferLength, (flags & 0xf) + 1);

  if (bufsize) {
    TRACE("size=%d\n", bufsize);
    safe_buffer_length_increment(pStubMsg, bufsize);
  }
  else
    pStubMsg->BufferLength =
        pStubMsg->StubDesc->aUserMarshalQuadruple[index].pfnBufferSize(
                             &umcb.Flags, pStubMsg->BufferLength, pMemory);

  if (saved_buffer_length)
  {
    pStubMsg->PointerLength = pStubMsg->BufferLength;
    pStubMsg->BufferLength = saved_buffer_length;
  }

}

/***********************************************************************
 *           NdrUserMarshalMemorySize [RPCRT4.@]
 */
ULONG WINAPI NdrUserMarshalMemorySize(PMIDL_STUB_MESSAGE pStubMsg,
                                      PFORMAT_STRING pFormat)
{
  unsigned flags = pFormat[1];
  unsigned index = *(const WORD*)&pFormat[2];
  DWORD memsize = *(const WORD*)&pFormat[4];
  DWORD bufsize = *(const WORD*)&pFormat[6];

  TRACE("(%p,%p)\n", pStubMsg, pFormat);
  TRACE("index=%d\n", index);

  pStubMsg->MemorySize += memsize;

  if (flags & USER_MARSHAL_POINTER)
  {
    ALIGN_POINTER(pStubMsg->Buffer, 4);
    /* skip pointer prefix */
    pStubMsg->Buffer += 4;
    if (pStubMsg->IgnoreEmbeddedPointers)
      return pStubMsg->MemorySize;
    ALIGN_POINTER(pStubMsg->Buffer, 8);
  }
  else
    ALIGN_POINTER(pStubMsg->Buffer, (flags & 0xf) + 1);

  if (!bufsize)
    FIXME("not implemented for varying buffer size\n");

  pStubMsg->Buffer += bufsize;

  return pStubMsg->MemorySize;
}

/***********************************************************************
 *           NdrUserMarshalFree [RPCRT4.@]
 */
void WINAPI NdrUserMarshalFree(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
/*  unsigned flags = pFormat[1]; */
  unsigned index = *(const WORD*)&pFormat[2];
  USER_MARSHAL_CB umcb;

  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
  TRACE("index=%d\n", index);

  UserMarshalCB(pStubMsg, USER_MARSHAL_CB_FREE, pFormat, &umcb);

  pStubMsg->StubDesc->aUserMarshalQuadruple[index].pfnFree(
    &umcb.Flags, pMemory);
}

/***********************************************************************
 *           NdrClearOutParameters [RPCRT4.@]
 */
void WINAPI NdrClearOutParameters(PMIDL_STUB_MESSAGE pStubMsg,
                                  PFORMAT_STRING pFormat,
                                  void *ArgAddr)
{
  FIXME("(%p,%p,%p): stub\n", pStubMsg, pFormat, ArgAddr);
}

/***********************************************************************
 *           NdrConvert [RPCRT4.@]
 */
void WINAPI NdrConvert( PMIDL_STUB_MESSAGE pStubMsg, PFORMAT_STRING pFormat )
{
  FIXME("(pStubMsg == ^%p, pFormat == ^%p): stub.\n", pStubMsg, pFormat);
  /* FIXME: since this stub doesn't do any converting, the proper behavior
     is to raise an exception */
}

/***********************************************************************
 *           NdrConvert2 [RPCRT4.@]
 */
void WINAPI NdrConvert2( PMIDL_STUB_MESSAGE pStubMsg, PFORMAT_STRING pFormat, LONG NumberParams )
{
  FIXME("(pStubMsg == ^%p, pFormat == ^%p, NumberParams == %d): stub.\n",
    pStubMsg, pFormat, NumberParams);
  /* FIXME: since this stub doesn't do any converting, the proper behavior
     is to raise an exception */
}

#include "pshpack1.h"
typedef struct _NDR_CSTRUCT_FORMAT
{
    unsigned char type;
    unsigned char alignment;
    unsigned short memory_size;
    short offset_to_array_description;
} NDR_CSTRUCT_FORMAT, NDR_CVSTRUCT_FORMAT;
#include "poppack.h"

/***********************************************************************
 *           NdrConformantStructMarshall [RPCRT4.@]
 */
unsigned char *  WINAPI NdrConformantStructMarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
    const NDR_CSTRUCT_FORMAT *pCStructFormat = (const NDR_CSTRUCT_FORMAT *)pFormat;
    PFORMAT_STRING pCArrayFormat;
    ULONG esize, bufsize;

    TRACE("(%p, %p, %p)\n", pStubMsg, pMemory, pFormat);

    pFormat += sizeof(NDR_CSTRUCT_FORMAT);
    if ((pCStructFormat->type != RPC_FC_CPSTRUCT) && (pCStructFormat->type != RPC_FC_CSTRUCT))
    {
        ERR("invalid format type %x\n", pCStructFormat->type);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return NULL;
    }

    pCArrayFormat = (const unsigned char *)&pCStructFormat->offset_to_array_description +
        pCStructFormat->offset_to_array_description;
    if (*pCArrayFormat != RPC_FC_CARRAY)
    {
        ERR("invalid array format type %x\n", pCStructFormat->type);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return NULL;
    }
    esize = *(const WORD*)(pCArrayFormat+2);

    ComputeConformance(pStubMsg, pMemory + pCStructFormat->memory_size,
                       pCArrayFormat + 4, 0);

    WriteConformance(pStubMsg);

    ALIGN_POINTER_CLEAR(pStubMsg->Buffer, pCStructFormat->alignment + 1);

    TRACE("memory_size = %d\n", pCStructFormat->memory_size);

    bufsize = safe_multiply(esize, pStubMsg->MaxCount);
    if (pCStructFormat->memory_size + bufsize < pCStructFormat->memory_size) /* integer overflow */
    {
        ERR("integer overflow of memory_size %u with bufsize %u\n",
            pCStructFormat->memory_size, bufsize);
        RpcRaiseException(RPC_X_BAD_STUB_DATA);
    }
    /* copy constant sized part of struct */
    pStubMsg->BufferMark = pStubMsg->Buffer;
    safe_copy_to_buffer(pStubMsg, pMemory, pCStructFormat->memory_size + bufsize);

    if (pCStructFormat->type == RPC_FC_CPSTRUCT)
        EmbeddedPointerMarshall(pStubMsg, pMemory, pFormat);

    return NULL;
}

/***********************************************************************
 *           NdrConformantStructUnmarshall [RPCRT4.@]
 */
unsigned char *  WINAPI NdrConformantStructUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char **ppMemory,
                                PFORMAT_STRING pFormat,
                                unsigned char fMustAlloc)
{
    const NDR_CSTRUCT_FORMAT *pCStructFormat = (const NDR_CSTRUCT_FORMAT *)pFormat;
    PFORMAT_STRING pCArrayFormat;
    ULONG esize, bufsize;
    unsigned char *saved_buffer;

    TRACE("(%p, %p, %p, %d)\n", pStubMsg, ppMemory, pFormat, fMustAlloc);

    pFormat += sizeof(NDR_CSTRUCT_FORMAT);
    if ((pCStructFormat->type != RPC_FC_CPSTRUCT) && (pCStructFormat->type != RPC_FC_CSTRUCT))
    {
        ERR("invalid format type %x\n", pCStructFormat->type);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return NULL;
    }
    pCArrayFormat = (const unsigned char *)&pCStructFormat->offset_to_array_description +
        pCStructFormat->offset_to_array_description;
    if (*pCArrayFormat != RPC_FC_CARRAY)
    {
        ERR("invalid array format type %x\n", pCStructFormat->type);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return NULL;
    }
    esize = *(const WORD*)(pCArrayFormat+2);

    pCArrayFormat = ReadConformance(pStubMsg, pCArrayFormat + 4);

    ALIGN_POINTER(pStubMsg->Buffer, pCStructFormat->alignment + 1);

    TRACE("memory_size = %d\n", pCStructFormat->memory_size);

    bufsize = safe_multiply(esize, pStubMsg->MaxCount);
    if (pCStructFormat->memory_size + bufsize < pCStructFormat->memory_size) /* integer overflow */
    {
        ERR("integer overflow of memory_size %u with bufsize %u\n",
            pCStructFormat->memory_size, bufsize);
        RpcRaiseException(RPC_X_BAD_STUB_DATA);
    }

    if (fMustAlloc)
    {
        SIZE_T size = pCStructFormat->memory_size + bufsize;
        *ppMemory = NdrAllocate(pStubMsg, size);
    }
    else
    {
        if (!pStubMsg->IsClient && !*ppMemory)
            /* for servers, we just point straight into the RPC buffer */
            *ppMemory = pStubMsg->Buffer;
    }

    saved_buffer = pStubMsg->BufferMark = pStubMsg->Buffer;
    safe_buffer_increment(pStubMsg, pCStructFormat->memory_size + bufsize);
    if (pCStructFormat->type == RPC_FC_CPSTRUCT)
        EmbeddedPointerUnmarshall(pStubMsg, saved_buffer, *ppMemory, pFormat, fMustAlloc);

    TRACE("copying %p to %p\n", saved_buffer, *ppMemory);
    if (*ppMemory != saved_buffer)
        memcpy(*ppMemory, saved_buffer, pCStructFormat->memory_size + bufsize);

    return NULL;
}

/***********************************************************************
 *           NdrConformantStructBufferSize [RPCRT4.@]
 */
void WINAPI NdrConformantStructBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
    const NDR_CSTRUCT_FORMAT * pCStructFormat = (const NDR_CSTRUCT_FORMAT *)pFormat;
    PFORMAT_STRING pCArrayFormat;
    ULONG esize;

    TRACE("(%p, %p, %p)\n", pStubMsg, pMemory, pFormat);

    pFormat += sizeof(NDR_CSTRUCT_FORMAT);
    if ((pCStructFormat->type != RPC_FC_CPSTRUCT) && (pCStructFormat->type != RPC_FC_CSTRUCT))
    {
        ERR("invalid format type %x\n", pCStructFormat->type);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return;
    }
    pCArrayFormat = (const unsigned char *)&pCStructFormat->offset_to_array_description +
        pCStructFormat->offset_to_array_description;
    if (*pCArrayFormat != RPC_FC_CARRAY)
    {
        ERR("invalid array format type %x\n", pCStructFormat->type);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return;
    }
    esize = *(const WORD*)(pCArrayFormat+2);

    pCArrayFormat = ComputeConformance(pStubMsg, pMemory + pCStructFormat->memory_size, pCArrayFormat+4, 0);
    SizeConformance(pStubMsg);

    ALIGN_LENGTH(pStubMsg->BufferLength, pCStructFormat->alignment + 1);

    TRACE("memory_size = %d\n", pCStructFormat->memory_size);

    safe_buffer_length_increment(pStubMsg, pCStructFormat->memory_size);
    safe_buffer_length_increment(pStubMsg, safe_multiply(pStubMsg->MaxCount, esize));

    if (pCStructFormat->type == RPC_FC_CPSTRUCT)
        EmbeddedPointerBufferSize(pStubMsg, pMemory, pFormat);
}

/***********************************************************************
 *           NdrConformantStructMemorySize [RPCRT4.@]
 */
ULONG WINAPI NdrConformantStructMemorySize(PMIDL_STUB_MESSAGE pStubMsg,
                                PFORMAT_STRING pFormat)
{
    FIXME("stub\n");
    return 0;
}

/***********************************************************************
 *           NdrConformantStructFree [RPCRT4.@]
 */
void WINAPI NdrConformantStructFree(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
    const NDR_CSTRUCT_FORMAT *pCStructFormat = (const NDR_CSTRUCT_FORMAT *)pFormat;
    PFORMAT_STRING pCArrayFormat;

    TRACE("(%p, %p, %p)\n", pStubMsg, pMemory, pFormat);

    pFormat += sizeof(NDR_CSTRUCT_FORMAT);
    if ((pCStructFormat->type != RPC_FC_CPSTRUCT) && (pCStructFormat->type != RPC_FC_CSTRUCT))
    {
        ERR("invalid format type %x\n", pCStructFormat->type);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return;
    }

    pCArrayFormat = (const unsigned char *)&pCStructFormat->offset_to_array_description +
        pCStructFormat->offset_to_array_description;
    if (*pCArrayFormat != RPC_FC_CARRAY)
    {
        ERR("invalid array format type %x\n", pCStructFormat->type);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return;
    }

    ComputeConformance(pStubMsg, pMemory + pCStructFormat->memory_size,
                       pCArrayFormat + 4, 0);

    TRACE("memory_size = %d\n", pCStructFormat->memory_size);

    /* copy constant sized part of struct */
    pStubMsg->BufferMark = pStubMsg->Buffer;

    if (pCStructFormat->type == RPC_FC_CPSTRUCT)
        EmbeddedPointerFree(pStubMsg, pMemory, pFormat);
}

/***********************************************************************
 *           NdrConformantVaryingStructMarshall [RPCRT4.@]
 */
unsigned char *  WINAPI NdrConformantVaryingStructMarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
    const NDR_CVSTRUCT_FORMAT *pCVStructFormat = (const NDR_CVSTRUCT_FORMAT *)pFormat;
    PFORMAT_STRING pCVArrayFormat;
    ULONG esize, bufsize;

    TRACE("(%p, %p, %p)\n", pStubMsg, pMemory, pFormat);

    pFormat += sizeof(NDR_CVSTRUCT_FORMAT);
    if (pCVStructFormat->type != RPC_FC_CVSTRUCT)
    {
        ERR("invalid format type %x\n", pCVStructFormat->type);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return NULL;
    }

    pCVArrayFormat = (const unsigned char *)&pCVStructFormat->offset_to_array_description +
        pCVStructFormat->offset_to_array_description;
    switch (*pCVArrayFormat)
    {
    case RPC_FC_CVARRAY:
        esize = *(const WORD*)(pCVArrayFormat+2);

        pCVArrayFormat = ComputeConformance(pStubMsg, pMemory + pCVStructFormat->memory_size,
                                            pCVArrayFormat + 4, 0);
        pCVArrayFormat = ComputeVariance(pStubMsg, pMemory + pCVStructFormat->memory_size,
                                         pCVArrayFormat, 0);
        break;
    case RPC_FC_C_CSTRING:
        TRACE("string=%s\n", debugstr_a((char*)pMemory + pCVStructFormat->memory_size));
        pStubMsg->ActualCount = strlen((char*)pMemory + pCVStructFormat->memory_size)+1;
        esize = sizeof(char);
        if (pCVArrayFormat[1] == RPC_FC_STRING_SIZED)
            pCVArrayFormat = ComputeConformance(pStubMsg, pMemory + pCVStructFormat->memory_size,
                                                pCVArrayFormat + 2, 0);
        else
            pStubMsg->MaxCount = pStubMsg->ActualCount;
        break;
    case RPC_FC_C_WSTRING:
        TRACE("string=%s\n", debugstr_w((LPWSTR)pMemory + pCVStructFormat->memory_size));
        pStubMsg->ActualCount = strlenW((LPWSTR)pMemory + pCVStructFormat->memory_size)+1;
        esize = sizeof(WCHAR);
        if (pCVArrayFormat[1] == RPC_FC_STRING_SIZED)
            pCVArrayFormat = ComputeConformance(pStubMsg, pMemory + pCVStructFormat->memory_size,
                                                pCVArrayFormat + 2, 0);
        else
            pStubMsg->MaxCount = pStubMsg->ActualCount;
        break;
    default:
        ERR("invalid array format type %x\n", *pCVArrayFormat);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return NULL;
    }

    WriteConformance(pStubMsg);

    ALIGN_POINTER_CLEAR(pStubMsg->Buffer, pCVStructFormat->alignment + 1);

    TRACE("memory_size = %d\n", pCVStructFormat->memory_size);

    /* write constant sized part */
    pStubMsg->BufferMark = pStubMsg->Buffer;
    safe_copy_to_buffer(pStubMsg, pMemory, pCVStructFormat->memory_size);

    WriteVariance(pStubMsg);

    bufsize = safe_multiply(esize, pStubMsg->ActualCount);

    /* write array part */
    safe_copy_to_buffer(pStubMsg, pMemory + pCVStructFormat->memory_size, bufsize);

    EmbeddedPointerMarshall(pStubMsg, pMemory, pFormat);

    return NULL;
}

/***********************************************************************
 *           NdrConformantVaryingStructUnmarshall [RPCRT4.@]
 */
unsigned char *  WINAPI NdrConformantVaryingStructUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char **ppMemory,
                                PFORMAT_STRING pFormat,
                                unsigned char fMustAlloc)
{
    const NDR_CVSTRUCT_FORMAT *pCVStructFormat = (const NDR_CVSTRUCT_FORMAT *)pFormat;
    PFORMAT_STRING pCVArrayFormat;
    ULONG esize, bufsize;
    unsigned char cvarray_type;
    unsigned char *saved_buffer, *saved_array_buffer;

    TRACE("(%p, %p, %p, %d)\n", pStubMsg, ppMemory, pFormat, fMustAlloc);

    pFormat += sizeof(NDR_CVSTRUCT_FORMAT);
    if (pCVStructFormat->type != RPC_FC_CVSTRUCT)
    {
        ERR("invalid format type %x\n", pCVStructFormat->type);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return NULL;
    }

    pCVArrayFormat = (const unsigned char *)&pCVStructFormat->offset_to_array_description +
        pCVStructFormat->offset_to_array_description;
    cvarray_type = *pCVArrayFormat;
    switch (cvarray_type)
    {
    case RPC_FC_CVARRAY:
        esize = *(const WORD*)(pCVArrayFormat+2);
        pCVArrayFormat = ReadConformance(pStubMsg, pCVArrayFormat + 4);
        break;
    case RPC_FC_C_CSTRING:
        esize = sizeof(char);
        if (pCVArrayFormat[1] == RPC_FC_STRING_SIZED)
            pCVArrayFormat = ReadConformance(pStubMsg, pCVArrayFormat + 2);
        else
            pCVArrayFormat = ReadConformance(pStubMsg, NULL);
        break;
    case RPC_FC_C_WSTRING:
        esize = sizeof(WCHAR);
        if (pCVArrayFormat[1] == RPC_FC_STRING_SIZED)
            pCVArrayFormat = ReadConformance(pStubMsg, pCVArrayFormat + 2);
        else
            pCVArrayFormat = ReadConformance(pStubMsg, NULL);
        break;
    default:
        ERR("invalid array format type %x\n", *pCVArrayFormat);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return NULL;
    }

    ALIGN_POINTER(pStubMsg->Buffer, pCVStructFormat->alignment + 1);

    TRACE("memory_size = %d\n", pCVStructFormat->memory_size);

    /* work out how much memory to allocate if we need to do so */
    if (!*ppMemory || fMustAlloc)
    {
        SIZE_T size = pCVStructFormat->memory_size + safe_multiply(esize, pStubMsg->MaxCount);
        *ppMemory = NdrAllocate(pStubMsg, size);
    }

    /* mark the start of the constant data */
    saved_buffer = pStubMsg->BufferMark = pStubMsg->Buffer;
    safe_buffer_increment(pStubMsg, pCVStructFormat->memory_size);

    pCVArrayFormat = ReadVariance(pStubMsg, pCVArrayFormat, pStubMsg->MaxCount);

    bufsize = safe_multiply(esize, pStubMsg->ActualCount);

    if ((cvarray_type == RPC_FC_C_CSTRING) ||
        (cvarray_type == RPC_FC_C_WSTRING))
        validate_string_data(pStubMsg, bufsize, esize);

    /* mark the start of the array data */
    saved_array_buffer = pStubMsg->Buffer;
    safe_buffer_increment(pStubMsg, bufsize);

    EmbeddedPointerUnmarshall(pStubMsg, saved_buffer, *ppMemory, pFormat, fMustAlloc);

    /* copy the constant data */
    memcpy(*ppMemory, saved_buffer, pCVStructFormat->memory_size);
    /* copy the array data */
    TRACE("copying %p to %p\n", saved_array_buffer, *ppMemory + pCVStructFormat->memory_size);
    memcpy(*ppMemory + pCVStructFormat->memory_size, saved_array_buffer, bufsize);

    if (cvarray_type == RPC_FC_C_CSTRING)
        TRACE("string=%s\n", debugstr_a((char *)(*ppMemory + pCVStructFormat->memory_size)));
    else if (cvarray_type == RPC_FC_C_WSTRING)
        TRACE("string=%s\n", debugstr_w((WCHAR *)(*ppMemory + pCVStructFormat->memory_size)));

    return NULL;
}

/***********************************************************************
 *           NdrConformantVaryingStructBufferSize [RPCRT4.@]
 */
void WINAPI NdrConformantVaryingStructBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
    const NDR_CVSTRUCT_FORMAT *pCVStructFormat = (const NDR_CVSTRUCT_FORMAT *)pFormat;
    PFORMAT_STRING pCVArrayFormat;
    ULONG esize;

    TRACE("(%p, %p, %p)\n", pStubMsg, pMemory, pFormat);

    pFormat += sizeof(NDR_CVSTRUCT_FORMAT);
    if (pCVStructFormat->type != RPC_FC_CVSTRUCT)
    {
        ERR("invalid format type %x\n", pCVStructFormat->type);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return;
    }

    pCVArrayFormat = (const unsigned char *)&pCVStructFormat->offset_to_array_description +
        pCVStructFormat->offset_to_array_description;
    switch (*pCVArrayFormat)
    {
    case RPC_FC_CVARRAY:
        esize = *(const WORD*)(pCVArrayFormat+2);

        pCVArrayFormat = ComputeConformance(pStubMsg, pMemory + pCVStructFormat->memory_size,
                                            pCVArrayFormat + 4, 0);
        pCVArrayFormat = ComputeVariance(pStubMsg, pMemory + pCVStructFormat->memory_size,
                                         pCVArrayFormat, 0);
        break;
    case RPC_FC_C_CSTRING:
        TRACE("string=%s\n", debugstr_a((char*)pMemory + pCVStructFormat->memory_size));
        pStubMsg->ActualCount = strlen((char*)pMemory + pCVStructFormat->memory_size)+1;
        esize = sizeof(char);
        if (pCVArrayFormat[1] == RPC_FC_STRING_SIZED)
            pCVArrayFormat = ComputeConformance(pStubMsg, pMemory + pCVStructFormat->memory_size,
                                                pCVArrayFormat + 2, 0);
        else
            pStubMsg->MaxCount = pStubMsg->ActualCount;
        break;
    case RPC_FC_C_WSTRING:
        TRACE("string=%s\n", debugstr_w((LPWSTR)pMemory + pCVStructFormat->memory_size));
        pStubMsg->ActualCount = strlenW((LPWSTR)pMemory + pCVStructFormat->memory_size)+1;
        esize = sizeof(WCHAR);
        if (pCVArrayFormat[1] == RPC_FC_STRING_SIZED)
            pCVArrayFormat = ComputeConformance(pStubMsg, pMemory + pCVStructFormat->memory_size,
                                                pCVArrayFormat + 2, 0);
        else
            pStubMsg->MaxCount = pStubMsg->ActualCount;
        break;
    default:
        ERR("invalid array format type %x\n", *pCVArrayFormat);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return;
    }

    SizeConformance(pStubMsg);

    ALIGN_LENGTH(pStubMsg->BufferLength, pCVStructFormat->alignment + 1);

    TRACE("memory_size = %d\n", pCVStructFormat->memory_size);

    safe_buffer_length_increment(pStubMsg, pCVStructFormat->memory_size);
    SizeVariance(pStubMsg);
    safe_buffer_length_increment(pStubMsg, safe_multiply(pStubMsg->MaxCount, esize));

    EmbeddedPointerBufferSize(pStubMsg, pMemory, pFormat);
}

/***********************************************************************
 *           NdrConformantVaryingStructMemorySize [RPCRT4.@]
 */
ULONG WINAPI NdrConformantVaryingStructMemorySize(PMIDL_STUB_MESSAGE pStubMsg,
                                PFORMAT_STRING pFormat)
{
    const NDR_CVSTRUCT_FORMAT *pCVStructFormat = (const NDR_CVSTRUCT_FORMAT *)pFormat;
    PFORMAT_STRING pCVArrayFormat;
    ULONG esize;
    unsigned char cvarray_type;

    TRACE("(%p, %p)\n", pStubMsg, pFormat);

    pFormat += sizeof(NDR_CVSTRUCT_FORMAT);
    if (pCVStructFormat->type != RPC_FC_CVSTRUCT)
    {
        ERR("invalid format type %x\n", pCVStructFormat->type);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return 0;
    }

    pCVArrayFormat = (const unsigned char *)&pCVStructFormat->offset_to_array_description +
        pCVStructFormat->offset_to_array_description;
    cvarray_type = *pCVArrayFormat;
    switch (cvarray_type)
    {
    case RPC_FC_CVARRAY:
        esize = *(const WORD*)(pCVArrayFormat+2);
        pCVArrayFormat = ReadConformance(pStubMsg, pCVArrayFormat + 4);
        break;
    case RPC_FC_C_CSTRING:
        esize = sizeof(char);
        if (pCVArrayFormat[1] == RPC_FC_STRING_SIZED)
            pCVArrayFormat = ReadConformance(pStubMsg, pCVArrayFormat + 2);
        else
            pCVArrayFormat = ReadConformance(pStubMsg, NULL);
        break;
    case RPC_FC_C_WSTRING:
        esize = sizeof(WCHAR);
        if (pCVArrayFormat[1] == RPC_FC_STRING_SIZED)
            pCVArrayFormat = ReadConformance(pStubMsg, pCVArrayFormat + 2);
        else
            pCVArrayFormat = ReadConformance(pStubMsg, NULL);
        break;
    default:
        ERR("invalid array format type %x\n", *pCVArrayFormat);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return 0;
    }

    ALIGN_POINTER(pStubMsg->Buffer, pCVStructFormat->alignment + 1);

    TRACE("memory_size = %d\n", pCVStructFormat->memory_size);

    safe_buffer_increment(pStubMsg, pCVStructFormat->memory_size);
    pCVArrayFormat = ReadVariance(pStubMsg, pCVArrayFormat, pStubMsg->MaxCount);
    safe_buffer_increment(pStubMsg, safe_multiply(esize, pStubMsg->ActualCount));

    pStubMsg->MemorySize += pCVStructFormat->memory_size + safe_multiply(esize, pStubMsg->MaxCount);

    EmbeddedPointerMemorySize(pStubMsg, pFormat);

    return pCVStructFormat->memory_size + pStubMsg->MaxCount * esize;
}

/***********************************************************************
 *           NdrConformantVaryingStructFree [RPCRT4.@]
 */
void WINAPI NdrConformantVaryingStructFree(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
    const NDR_CVSTRUCT_FORMAT *pCVStructFormat = (const NDR_CVSTRUCT_FORMAT *)pFormat;
    PFORMAT_STRING pCVArrayFormat;

    TRACE("(%p, %p, %p)\n", pStubMsg, pMemory, pFormat);

    pFormat += sizeof(NDR_CVSTRUCT_FORMAT);
    if (pCVStructFormat->type != RPC_FC_CVSTRUCT)
    {
        ERR("invalid format type %x\n", pCVStructFormat->type);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return;
    }

    pCVArrayFormat = (const unsigned char *)&pCVStructFormat->offset_to_array_description +
        pCVStructFormat->offset_to_array_description;
    switch (*pCVArrayFormat)
    {
    case RPC_FC_CVARRAY:
        pCVArrayFormat = ComputeConformance(pStubMsg, pMemory + pCVStructFormat->memory_size,
                                            pCVArrayFormat + 4, 0);
        pCVArrayFormat = ComputeVariance(pStubMsg, pMemory + pCVStructFormat->memory_size,
                                         pCVArrayFormat, 0);
        break;
    case RPC_FC_C_CSTRING:
        TRACE("string=%s\n", debugstr_a((char*)pMemory + pCVStructFormat->memory_size));
        pStubMsg->ActualCount = strlen((char*)pMemory + pCVStructFormat->memory_size)+1;
        if (pCVArrayFormat[1] == RPC_FC_STRING_SIZED)
            pCVArrayFormat = ComputeConformance(pStubMsg, pMemory + pCVStructFormat->memory_size,
                                                pCVArrayFormat + 2, 0);
        else
            pStubMsg->MaxCount = pStubMsg->ActualCount;
        break;
    case RPC_FC_C_WSTRING:
        TRACE("string=%s\n", debugstr_w((LPWSTR)pMemory + pCVStructFormat->memory_size));
        pStubMsg->ActualCount = strlenW((LPWSTR)pMemory + pCVStructFormat->memory_size)+1;
        if (pCVArrayFormat[1] == RPC_FC_STRING_SIZED)
            pCVArrayFormat = ComputeConformance(pStubMsg, pMemory + pCVStructFormat->memory_size,
                                                pCVArrayFormat + 2, 0);
        else
            pStubMsg->MaxCount = pStubMsg->ActualCount;
        break;
    default:
        ERR("invalid array format type %x\n", *pCVArrayFormat);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return;
    }

    TRACE("memory_size = %d\n", pCVStructFormat->memory_size);

    EmbeddedPointerFree(pStubMsg, pMemory, pFormat);
}

#include "pshpack1.h"
typedef struct
{
    unsigned char type;
    unsigned char alignment;
    unsigned short total_size;
} NDR_SMFARRAY_FORMAT;

typedef struct
{
    unsigned char type;
    unsigned char alignment;
    unsigned long total_size;
} NDR_LGFARRAY_FORMAT;
#include "poppack.h"

/***********************************************************************
 *           NdrFixedArrayMarshall [RPCRT4.@]
 */
unsigned char *  WINAPI NdrFixedArrayMarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
    const NDR_SMFARRAY_FORMAT *pSmFArrayFormat = (const NDR_SMFARRAY_FORMAT *)pFormat;
    unsigned long total_size;

    TRACE("(%p, %p, %p)\n", pStubMsg, pMemory, pFormat);

    if ((pSmFArrayFormat->type != RPC_FC_SMFARRAY) &&
        (pSmFArrayFormat->type != RPC_FC_LGFARRAY))
    {
        ERR("invalid format type %x\n", pSmFArrayFormat->type);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return NULL;
    }

    ALIGN_POINTER_CLEAR(pStubMsg->Buffer, pSmFArrayFormat->alignment + 1);

    if (pSmFArrayFormat->type == RPC_FC_SMFARRAY)
    {
        total_size = pSmFArrayFormat->total_size;
        pFormat = (const unsigned char *)(pSmFArrayFormat + 1);
    }
    else
    {
        const NDR_LGFARRAY_FORMAT *pLgFArrayFormat = (const NDR_LGFARRAY_FORMAT *)pFormat;
        total_size = pLgFArrayFormat->total_size;
        pFormat = (const unsigned char *)(pLgFArrayFormat + 1);
    }

    pStubMsg->BufferMark = pStubMsg->Buffer;
    safe_copy_to_buffer(pStubMsg, pMemory, total_size);

    pFormat = EmbeddedPointerMarshall(pStubMsg, pMemory, pFormat);

    return NULL;
}

/***********************************************************************
 *           NdrFixedArrayUnmarshall [RPCRT4.@]
 */
unsigned char *  WINAPI NdrFixedArrayUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char **ppMemory,
                                PFORMAT_STRING pFormat,
                                unsigned char fMustAlloc)
{
    const NDR_SMFARRAY_FORMAT *pSmFArrayFormat = (const NDR_SMFARRAY_FORMAT *)pFormat;
    unsigned long total_size;
    unsigned char *saved_buffer;

    TRACE("(%p, %p, %p, %d)\n", pStubMsg, ppMemory, pFormat, fMustAlloc);

    if ((pSmFArrayFormat->type != RPC_FC_SMFARRAY) &&
        (pSmFArrayFormat->type != RPC_FC_LGFARRAY))
    {
        ERR("invalid format type %x\n", pSmFArrayFormat->type);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return NULL;
    }

    ALIGN_POINTER(pStubMsg->Buffer, pSmFArrayFormat->alignment + 1);

    if (pSmFArrayFormat->type == RPC_FC_SMFARRAY)
    {
        total_size = pSmFArrayFormat->total_size;
        pFormat = (const unsigned char *)(pSmFArrayFormat + 1);
    }
    else
    {
        const NDR_LGFARRAY_FORMAT *pLgFArrayFormat = (const NDR_LGFARRAY_FORMAT *)pFormat;
        total_size = pLgFArrayFormat->total_size;
        pFormat = (const unsigned char *)(pLgFArrayFormat + 1);
    }

    if (fMustAlloc)
        *ppMemory = NdrAllocate(pStubMsg, total_size);
    else
    {
        if (!pStubMsg->IsClient && !*ppMemory)
            /* for servers, we just point straight into the RPC buffer */
            *ppMemory = pStubMsg->Buffer;
    }

    saved_buffer = pStubMsg->BufferMark = pStubMsg->Buffer;
    safe_buffer_increment(pStubMsg, total_size);
    pFormat = EmbeddedPointerUnmarshall(pStubMsg, saved_buffer, *ppMemory, pFormat, fMustAlloc);

    TRACE("copying %p to %p\n", saved_buffer, *ppMemory);
    if (*ppMemory != saved_buffer)
        memcpy(*ppMemory, saved_buffer, total_size);

    return NULL;
}

/***********************************************************************
 *           NdrFixedArrayBufferSize [RPCRT4.@]
 */
void WINAPI NdrFixedArrayBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
    const NDR_SMFARRAY_FORMAT *pSmFArrayFormat = (const NDR_SMFARRAY_FORMAT *)pFormat;
    unsigned long total_size;

    TRACE("(%p, %p, %p)\n", pStubMsg, pMemory, pFormat);

    if ((pSmFArrayFormat->type != RPC_FC_SMFARRAY) &&
        (pSmFArrayFormat->type != RPC_FC_LGFARRAY))
    {
        ERR("invalid format type %x\n", pSmFArrayFormat->type);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return;
    }

    ALIGN_LENGTH(pStubMsg->BufferLength, pSmFArrayFormat->alignment + 1);

    if (pSmFArrayFormat->type == RPC_FC_SMFARRAY)
    {
        total_size = pSmFArrayFormat->total_size;
        pFormat = (const unsigned char *)(pSmFArrayFormat + 1);
    }
    else
    {
        const NDR_LGFARRAY_FORMAT *pLgFArrayFormat = (const NDR_LGFARRAY_FORMAT *)pFormat;
        total_size = pLgFArrayFormat->total_size;
        pFormat = (const unsigned char *)(pLgFArrayFormat + 1);
    }
    safe_buffer_length_increment(pStubMsg, total_size);

    EmbeddedPointerBufferSize(pStubMsg, pMemory, pFormat);
}

/***********************************************************************
 *           NdrFixedArrayMemorySize [RPCRT4.@]
 */
ULONG WINAPI NdrFixedArrayMemorySize(PMIDL_STUB_MESSAGE pStubMsg,
                                PFORMAT_STRING pFormat)
{
    const NDR_SMFARRAY_FORMAT *pSmFArrayFormat = (const NDR_SMFARRAY_FORMAT *)pFormat;
    ULONG total_size;

    TRACE("(%p, %p)\n", pStubMsg, pFormat);

    if ((pSmFArrayFormat->type != RPC_FC_SMFARRAY) &&
        (pSmFArrayFormat->type != RPC_FC_LGFARRAY))
    {
        ERR("invalid format type %x\n", pSmFArrayFormat->type);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return 0;
    }

    ALIGN_POINTER(pStubMsg->Buffer, pSmFArrayFormat->alignment + 1);

    if (pSmFArrayFormat->type == RPC_FC_SMFARRAY)
    {
        total_size = pSmFArrayFormat->total_size;
        pFormat = (const unsigned char *)(pSmFArrayFormat + 1);
    }
    else
    {
        const NDR_LGFARRAY_FORMAT *pLgFArrayFormat = (const NDR_LGFARRAY_FORMAT *)pFormat;
        total_size = pLgFArrayFormat->total_size;
        pFormat = (const unsigned char *)(pLgFArrayFormat + 1);
    }
    pStubMsg->BufferMark = pStubMsg->Buffer;
    safe_buffer_increment(pStubMsg, total_size);
    pStubMsg->MemorySize += total_size;

    EmbeddedPointerMemorySize(pStubMsg, pFormat);

    return total_size;
}

/***********************************************************************
 *           NdrFixedArrayFree [RPCRT4.@]
 */
void WINAPI NdrFixedArrayFree(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
    const NDR_SMFARRAY_FORMAT *pSmFArrayFormat = (const NDR_SMFARRAY_FORMAT *)pFormat;

    TRACE("(%p, %p, %p)\n", pStubMsg, pMemory, pFormat);

    if ((pSmFArrayFormat->type != RPC_FC_SMFARRAY) &&
        (pSmFArrayFormat->type != RPC_FC_LGFARRAY))
    {
        ERR("invalid format type %x\n", pSmFArrayFormat->type);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return;
    }

    if (pSmFArrayFormat->type == RPC_FC_SMFARRAY)
        pFormat = (const unsigned char *)(pSmFArrayFormat + 1);
    else
    {
        const NDR_LGFARRAY_FORMAT *pLgFArrayFormat = (const NDR_LGFARRAY_FORMAT *)pFormat;
        pFormat = (const unsigned char *)(pLgFArrayFormat + 1);
    }

    EmbeddedPointerFree(pStubMsg, pMemory, pFormat);
}

/***********************************************************************
 *           NdrVaryingArrayMarshall [RPCRT4.@]
 */
unsigned char *  WINAPI NdrVaryingArrayMarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
    unsigned char alignment;
    DWORD elements, esize;
    ULONG bufsize;

    TRACE("(%p, %p, %p)\n", pStubMsg, pMemory, pFormat);

    if ((pFormat[0] != RPC_FC_SMVARRAY) &&
        (pFormat[0] != RPC_FC_LGVARRAY))
    {
        ERR("invalid format type %x\n", pFormat[0]);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return NULL;
    }

    alignment = pFormat[1] + 1;

    if (pFormat[0] == RPC_FC_SMVARRAY)
    {
        pFormat += 2;
        pFormat += sizeof(WORD);
        elements = *(const WORD*)pFormat;
        pFormat += sizeof(WORD);
    }
    else
    {
        pFormat += 2;
        pFormat += sizeof(DWORD);
        elements = *(const DWORD*)pFormat;
        pFormat += sizeof(DWORD);
    }

    esize = *(const WORD*)pFormat;
    pFormat += sizeof(WORD);

    pFormat = ComputeVariance(pStubMsg, pMemory, pFormat, 0);
    if ((pStubMsg->ActualCount > elements) ||
        (pStubMsg->ActualCount + pStubMsg->Offset > elements))
    {
        RpcRaiseException(RPC_S_INVALID_BOUND);
        return NULL;
    }

    WriteVariance(pStubMsg);

    ALIGN_POINTER_CLEAR(pStubMsg->Buffer, alignment);

    bufsize = safe_multiply(esize, pStubMsg->ActualCount);
    pStubMsg->BufferMark = pStubMsg->Buffer;
    safe_copy_to_buffer(pStubMsg, pMemory + pStubMsg->Offset, bufsize);

    EmbeddedPointerMarshall(pStubMsg, pMemory, pFormat);

    return NULL;
}

/***********************************************************************
 *           NdrVaryingArrayUnmarshall [RPCRT4.@]
 */
unsigned char *  WINAPI NdrVaryingArrayUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char **ppMemory,
                                PFORMAT_STRING pFormat,
                                unsigned char fMustAlloc)
{
    unsigned char alignment;
    DWORD size, elements, esize;
    ULONG bufsize;
    unsigned char *saved_buffer;
    ULONG offset;

    TRACE("(%p, %p, %p, %d)\n", pStubMsg, ppMemory, pFormat, fMustAlloc);

    if ((pFormat[0] != RPC_FC_SMVARRAY) &&
        (pFormat[0] != RPC_FC_LGVARRAY))
    {
        ERR("invalid format type %x\n", pFormat[0]);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return NULL;
    }

    alignment = pFormat[1] + 1;

    if (pFormat[0] == RPC_FC_SMVARRAY)
    {
        pFormat += 2;
        size = *(const WORD*)pFormat;
        pFormat += sizeof(WORD);
        elements = *(const WORD*)pFormat;
        pFormat += sizeof(WORD);
    }
    else
    {
        pFormat += 2;
        size = *(const DWORD*)pFormat;
        pFormat += sizeof(DWORD);
        elements = *(const DWORD*)pFormat;
        pFormat += sizeof(DWORD);
    }

    esize = *(const WORD*)pFormat;
    pFormat += sizeof(WORD);

    pFormat = ReadVariance(pStubMsg, pFormat, elements);

    ALIGN_POINTER(pStubMsg->Buffer, alignment);

    bufsize = safe_multiply(esize, pStubMsg->ActualCount);
    offset = pStubMsg->Offset;

    if (!*ppMemory || fMustAlloc)
        *ppMemory = NdrAllocate(pStubMsg, size);
    saved_buffer = pStubMsg->BufferMark = pStubMsg->Buffer;
    safe_buffer_increment(pStubMsg, bufsize);

    EmbeddedPointerUnmarshall(pStubMsg, saved_buffer, *ppMemory, pFormat, fMustAlloc);

    memcpy(*ppMemory + offset, saved_buffer, bufsize);

    return NULL;
}

/***********************************************************************
 *           NdrVaryingArrayBufferSize [RPCRT4.@]
 */
void WINAPI NdrVaryingArrayBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
    unsigned char alignment;
    DWORD elements, esize;

    TRACE("(%p, %p, %p)\n", pStubMsg, pMemory, pFormat);

    if ((pFormat[0] != RPC_FC_SMVARRAY) &&
        (pFormat[0] != RPC_FC_LGVARRAY))
    {
        ERR("invalid format type %x\n", pFormat[0]);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return;
    }

    alignment = pFormat[1] + 1;

    if (pFormat[0] == RPC_FC_SMVARRAY)
    {
        pFormat += 2;
        pFormat += sizeof(WORD);
        elements = *(const WORD*)pFormat;
        pFormat += sizeof(WORD);
    }
    else
    {
        pFormat += 2;
        pFormat += sizeof(DWORD);
        elements = *(const DWORD*)pFormat;
        pFormat += sizeof(DWORD);
    }

    esize = *(const WORD*)pFormat;
    pFormat += sizeof(WORD);

    pFormat = ComputeVariance(pStubMsg, pMemory, pFormat, 0);
    if ((pStubMsg->ActualCount > elements) ||
        (pStubMsg->ActualCount + pStubMsg->Offset > elements))
    {
        RpcRaiseException(RPC_S_INVALID_BOUND);
        return;
    }

    SizeVariance(pStubMsg);

    ALIGN_LENGTH(pStubMsg->BufferLength, alignment);

    safe_buffer_length_increment(pStubMsg, safe_multiply(esize, pStubMsg->ActualCount));

    EmbeddedPointerBufferSize(pStubMsg, pMemory, pFormat);
}

/***********************************************************************
 *           NdrVaryingArrayMemorySize [RPCRT4.@]
 */
ULONG WINAPI NdrVaryingArrayMemorySize(PMIDL_STUB_MESSAGE pStubMsg,
                                PFORMAT_STRING pFormat)
{
    unsigned char alignment;
    DWORD size, elements, esize;

    TRACE("(%p, %p)\n", pStubMsg, pFormat);

    if ((pFormat[0] != RPC_FC_SMVARRAY) &&
        (pFormat[0] != RPC_FC_LGVARRAY))
    {
        ERR("invalid format type %x\n", pFormat[0]);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return 0;
    }

    alignment = pFormat[1] + 1;

    if (pFormat[0] == RPC_FC_SMVARRAY)
    {
        pFormat += 2;
        size = *(const WORD*)pFormat;
        pFormat += sizeof(WORD);
        elements = *(const WORD*)pFormat;
        pFormat += sizeof(WORD);
    }
    else
    {
        pFormat += 2;
        size = *(const DWORD*)pFormat;
        pFormat += sizeof(DWORD);
        elements = *(const DWORD*)pFormat;
        pFormat += sizeof(DWORD);
    }

    esize = *(const WORD*)pFormat;
    pFormat += sizeof(WORD);

    pFormat = ReadVariance(pStubMsg, pFormat, elements);

    ALIGN_POINTER(pStubMsg->Buffer, alignment);

    safe_buffer_increment(pStubMsg, safe_multiply(esize, pStubMsg->ActualCount));
    pStubMsg->MemorySize += size;

    EmbeddedPointerMemorySize(pStubMsg, pFormat);

    return pStubMsg->MemorySize;
}

/***********************************************************************
 *           NdrVaryingArrayFree [RPCRT4.@]
 */
void WINAPI NdrVaryingArrayFree(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
    DWORD elements;

    TRACE("(%p, %p, %p)\n", pStubMsg, pMemory, pFormat);

    if ((pFormat[0] != RPC_FC_SMVARRAY) &&
        (pFormat[0] != RPC_FC_LGVARRAY))
    {
        ERR("invalid format type %x\n", pFormat[0]);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return;
    }

    if (pFormat[0] == RPC_FC_SMVARRAY)
    {
        pFormat += 2;
        pFormat += sizeof(WORD);
        elements = *(const WORD*)pFormat;
        pFormat += sizeof(WORD);
    }
    else
    {
        pFormat += 2;
        pFormat += sizeof(DWORD);
        elements = *(const DWORD*)pFormat;
        pFormat += sizeof(DWORD);
    }

    pFormat += sizeof(WORD);

    pFormat = ComputeVariance(pStubMsg, pMemory, pFormat, 0);
    if ((pStubMsg->ActualCount > elements) ||
        (pStubMsg->ActualCount + pStubMsg->Offset > elements))
    {
        RpcRaiseException(RPC_S_INVALID_BOUND);
        return;
    }

    EmbeddedPointerFree(pStubMsg, pMemory, pFormat);
}

static ULONG get_discriminant(unsigned char fc, const unsigned char *pMemory)
{
    switch (fc)
    {
    case RPC_FC_BYTE:
    case RPC_FC_CHAR:
    case RPC_FC_SMALL:
    case RPC_FC_USMALL:
        return *pMemory;
    case RPC_FC_WCHAR:
    case RPC_FC_SHORT:
    case RPC_FC_USHORT:
    case RPC_FC_ENUM16:
        return *(const USHORT *)pMemory;
    case RPC_FC_LONG:
    case RPC_FC_ULONG:
    case RPC_FC_ENUM32:
        return *(const ULONG *)pMemory;
    default:
        FIXME("Unhandled base type: 0x%02x\n", fc);
        return 0;
    }
}

static PFORMAT_STRING get_arm_offset_from_union_arm_selector(PMIDL_STUB_MESSAGE pStubMsg,
                                                             unsigned long discriminant,
                                                             PFORMAT_STRING pFormat)
{
    unsigned short num_arms, arm, type;

    num_arms = *(const SHORT*)pFormat & 0x0fff;
    pFormat += 2;
    for(arm = 0; arm < num_arms; arm++)
    {
        if(discriminant == *(const ULONG*)pFormat)
        {
            pFormat += 4;
            break;
        }
        pFormat += 6;
    }

    type = *(const unsigned short*)pFormat;
    TRACE("type %04x\n", type);
    if(arm == num_arms) /* default arm extras */
    {
        if(type == 0xffff)
        {
            ERR("no arm for 0x%lx and no default case\n", discriminant);
            RpcRaiseException(RPC_S_INVALID_TAG);
            return NULL;
        }
        if(type == 0)
        {
            TRACE("falling back to empty default case for 0x%lx\n", discriminant);
            return NULL;
        }
    }
    return pFormat;
}

static unsigned char *union_arm_marshall(PMIDL_STUB_MESSAGE pStubMsg, unsigned char *pMemory, ULONG discriminant, PFORMAT_STRING pFormat)
{
    unsigned short type;

    pFormat += 2;

    pFormat = get_arm_offset_from_union_arm_selector(pStubMsg, discriminant, pFormat);
    if(!pFormat)
        return NULL;

    type = *(const unsigned short*)pFormat;
    if((type & 0xff00) == 0x8000)
    {
        unsigned char basetype = LOBYTE(type);
        return NdrBaseTypeMarshall(pStubMsg, pMemory, &basetype);
    }
    else
    {
        PFORMAT_STRING desc = pFormat + *(const SHORT*)pFormat;
        NDR_MARSHALL m = NdrMarshaller[*desc & NDR_TABLE_MASK];
        if (m)
        {
            unsigned char *saved_buffer = NULL;
            int pointer_buffer_mark_set = 0;
            switch(*desc)
            {
            case RPC_FC_RP:
            case RPC_FC_UP:
            case RPC_FC_OP:
            case RPC_FC_FP:
                ALIGN_POINTER_CLEAR(pStubMsg->Buffer, 4);
                saved_buffer = pStubMsg->Buffer;
                if (pStubMsg->PointerBufferMark)
                {
                  pStubMsg->Buffer = pStubMsg->PointerBufferMark;
                  pStubMsg->PointerBufferMark = NULL;
                  pointer_buffer_mark_set = 1;
                }
                else
                  safe_buffer_increment(pStubMsg, 4); /* for pointer ID */

                PointerMarshall(pStubMsg, saved_buffer, *(unsigned char **)pMemory, desc);
                if (pointer_buffer_mark_set)
                {
                  STD_OVERFLOW_CHECK(pStubMsg);
                  pStubMsg->PointerBufferMark = pStubMsg->Buffer;
                  if (saved_buffer + 4 > (unsigned char *)pStubMsg->RpcMsg->Buffer + pStubMsg->BufferLength)
                  {
                      ERR("buffer overflow - saved_buffer = %p, BufferEnd = %p\n",
                          saved_buffer, (unsigned char *)pStubMsg->RpcMsg->Buffer + pStubMsg->BufferLength);
                      RpcRaiseException(RPC_X_BAD_STUB_DATA);
                  }
                  pStubMsg->Buffer = saved_buffer + 4;
                }
                break;
            default:
                m(pStubMsg, pMemory, desc);
            }
        }
        else FIXME("no marshaller for embedded type %02x\n", *desc);
    }
    return NULL;
}

static unsigned char *union_arm_unmarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char **ppMemory,
                                ULONG discriminant,
                                PFORMAT_STRING pFormat,
                                unsigned char fMustAlloc)
{
    unsigned short type;

    pFormat += 2;

    pFormat = get_arm_offset_from_union_arm_selector(pStubMsg, discriminant, pFormat);
    if(!pFormat)
        return NULL;

    type = *(const unsigned short*)pFormat;
    if((type & 0xff00) == 0x8000)
    {
        unsigned char basetype = LOBYTE(type);
        return NdrBaseTypeUnmarshall(pStubMsg, ppMemory, &basetype, FALSE);
    }
    else
    {
        PFORMAT_STRING desc = pFormat + *(const SHORT*)pFormat;
        NDR_UNMARSHALL m = NdrUnmarshaller[*desc & NDR_TABLE_MASK];
        if (m)
        {
            unsigned char *saved_buffer = NULL;
            int pointer_buffer_mark_set = 0;
            switch(*desc)
            {
            case RPC_FC_RP:
            case RPC_FC_UP:
            case RPC_FC_OP:
            case RPC_FC_FP:
                **(void***)ppMemory = NULL;
                ALIGN_POINTER(pStubMsg->Buffer, 4);
                saved_buffer = pStubMsg->Buffer;
                if (pStubMsg->PointerBufferMark)
                {
                  pStubMsg->Buffer = pStubMsg->PointerBufferMark;
                  pStubMsg->PointerBufferMark = NULL;
                  pointer_buffer_mark_set = 1;
                }
                else
                  pStubMsg->Buffer += 4; /* for pointer ID */

                if (saved_buffer + 4 > pStubMsg->BufferEnd)
                {
                    ERR("buffer overflow - saved_buffer = %p, BufferEnd = %p\n",
                        saved_buffer, pStubMsg->BufferEnd);
                    RpcRaiseException(RPC_X_BAD_STUB_DATA);
                }

                PointerUnmarshall(pStubMsg, saved_buffer, *(unsigned char ***)ppMemory, **(unsigned char ***)ppMemory, desc, fMustAlloc);
                if (pointer_buffer_mark_set)
                {
                  STD_OVERFLOW_CHECK(pStubMsg);
                  pStubMsg->PointerBufferMark = pStubMsg->Buffer;
                  pStubMsg->Buffer = saved_buffer + 4;
                }
                break;
            default:
                m(pStubMsg, ppMemory, desc, fMustAlloc);
            }
        }
        else FIXME("no marshaller for embedded type %02x\n", *desc);
    }
    return NULL;
}

static void union_arm_buffer_size(PMIDL_STUB_MESSAGE pStubMsg,
                                  unsigned char *pMemory,
                                  ULONG discriminant,
                                  PFORMAT_STRING pFormat)
{
    unsigned short type;

    pFormat += 2;

    pFormat = get_arm_offset_from_union_arm_selector(pStubMsg, discriminant, pFormat);
    if(!pFormat)
        return;

    type = *(const unsigned short*)pFormat;
    if((type & 0xff00) == 0x8000)
    {
        unsigned char basetype = LOBYTE(type);
        NdrBaseTypeBufferSize(pStubMsg, pMemory, &basetype);
    }
    else
    {
        PFORMAT_STRING desc = pFormat + *(const SHORT*)pFormat;
        NDR_BUFFERSIZE m = NdrBufferSizer[*desc & NDR_TABLE_MASK];
        if (m)
        {
            switch(*desc)
            {
            case RPC_FC_RP:
            case RPC_FC_UP:
            case RPC_FC_OP:
            case RPC_FC_FP:
                ALIGN_LENGTH(pStubMsg->BufferLength, 4);
                safe_buffer_length_increment(pStubMsg, 4); /* for pointer ID */
                if (!pStubMsg->IgnoreEmbeddedPointers)
                {
                    int saved_buffer_length = pStubMsg->BufferLength;
                    pStubMsg->BufferLength = pStubMsg->PointerLength;
                    pStubMsg->PointerLength = 0;
                    if(!pStubMsg->BufferLength)
                        ERR("BufferLength == 0??\n");
                    PointerBufferSize(pStubMsg, *(unsigned char **)pMemory, desc);
                    pStubMsg->PointerLength = pStubMsg->BufferLength;
                    pStubMsg->BufferLength = saved_buffer_length;
                }
                break;
            default:
                m(pStubMsg, pMemory, desc);
            }
        }
        else FIXME("no buffersizer for embedded type %02x\n", *desc);
    }
}

static ULONG union_arm_memory_size(PMIDL_STUB_MESSAGE pStubMsg,
                                   ULONG discriminant,
                                   PFORMAT_STRING pFormat)
{
    unsigned short type, size;

    size = *(const unsigned short*)pFormat;
    pStubMsg->Memory += size;
    pFormat += 2;

    pFormat = get_arm_offset_from_union_arm_selector(pStubMsg, discriminant, pFormat);
    if(!pFormat)
        return 0;

    type = *(const unsigned short*)pFormat;
    if((type & 0xff00) == 0x8000)
    {
        return NdrBaseTypeMemorySize(pStubMsg, pFormat);
    }
    else
    {
        PFORMAT_STRING desc = pFormat + *(const SHORT*)pFormat;
        NDR_MEMORYSIZE m = NdrMemorySizer[*desc & NDR_TABLE_MASK];
        unsigned char *saved_buffer;
        if (m)
        {
            switch(*desc)
            {
            case RPC_FC_RP:
            case RPC_FC_UP:
            case RPC_FC_OP:
            case RPC_FC_FP:
                ALIGN_POINTER(pStubMsg->Buffer, 4);
                saved_buffer = pStubMsg->Buffer;
                safe_buffer_increment(pStubMsg, 4);
                ALIGN_LENGTH(pStubMsg->MemorySize, 4);
                pStubMsg->MemorySize += 4;
                if (!pStubMsg->IgnoreEmbeddedPointers)
                    PointerMemorySize(pStubMsg, saved_buffer, pFormat);
                break;
            default:
                return m(pStubMsg, desc);
            }
        }
        else FIXME("no marshaller for embedded type %02x\n", *desc);
    }

    TRACE("size %d\n", size);
    return size;
}

static void union_arm_free(PMIDL_STUB_MESSAGE pStubMsg,
                           unsigned char *pMemory,
                           ULONG discriminant,
                           PFORMAT_STRING pFormat)
{
    unsigned short type;

    pFormat += 2;

    pFormat = get_arm_offset_from_union_arm_selector(pStubMsg, discriminant, pFormat);
    if(!pFormat)
        return;

    type = *(const unsigned short*)pFormat;
    if((type & 0xff00) != 0x8000)
    {
        PFORMAT_STRING desc = pFormat + *(const SHORT*)pFormat;
        NDR_FREE m = NdrFreer[*desc & NDR_TABLE_MASK];
        if (m)
        {
            switch(*desc)
            {
            case RPC_FC_RP:
            case RPC_FC_UP:
            case RPC_FC_OP:
            case RPC_FC_FP:
                PointerFree(pStubMsg, *(unsigned char **)pMemory, desc);
                break;
            default:
                m(pStubMsg, pMemory, desc);
            }
        }
    }
}

/***********************************************************************
 *           NdrEncapsulatedUnionMarshall [RPCRT4.@]
 */
unsigned char *  WINAPI NdrEncapsulatedUnionMarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
    unsigned char switch_type;
    unsigned char increment;
    ULONG switch_value;

    TRACE("(%p, %p, %p)\n", pStubMsg, pMemory, pFormat);
    pFormat++;

    switch_type = *pFormat & 0xf;
    increment = (*pFormat & 0xf0) >> 4;
    pFormat++;

    ALIGN_POINTER_CLEAR(pStubMsg->Buffer, increment);

    switch_value = get_discriminant(switch_type, pMemory);
    TRACE("got switch value 0x%x\n", switch_value);

    NdrBaseTypeMarshall(pStubMsg, pMemory, &switch_type);
    pMemory += increment;

    return union_arm_marshall(pStubMsg, pMemory, switch_value, pFormat);
}

/***********************************************************************
 *           NdrEncapsulatedUnionUnmarshall [RPCRT4.@]
 */
unsigned char *  WINAPI NdrEncapsulatedUnionUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char **ppMemory,
                                PFORMAT_STRING pFormat,
                                unsigned char fMustAlloc)
{
    unsigned char switch_type;
    unsigned char increment;
    ULONG switch_value;
    unsigned short size;
    unsigned char *pMemoryArm;

    TRACE("(%p, %p, %p, %d)\n", pStubMsg, ppMemory, pFormat, fMustAlloc);
    pFormat++;

    switch_type = *pFormat & 0xf;
    increment = (*pFormat & 0xf0) >> 4;
    pFormat++;

    ALIGN_POINTER(pStubMsg->Buffer, increment);
    switch_value = get_discriminant(switch_type, pStubMsg->Buffer);
    TRACE("got switch value 0x%x\n", switch_value);

    size = *(const unsigned short*)pFormat + increment;
    if(!*ppMemory || fMustAlloc)
        *ppMemory = NdrAllocate(pStubMsg, size);

    NdrBaseTypeUnmarshall(pStubMsg, ppMemory, &switch_type, FALSE);
    pMemoryArm = *ppMemory + increment;

    return union_arm_unmarshall(pStubMsg, &pMemoryArm, switch_value, pFormat, fMustAlloc);
}

/***********************************************************************
 *           NdrEncapsulatedUnionBufferSize [RPCRT4.@]
 */
void WINAPI NdrEncapsulatedUnionBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
    unsigned char switch_type;
    unsigned char increment;
    ULONG switch_value;

    TRACE("(%p, %p, %p)\n", pStubMsg, pMemory, pFormat);
    pFormat++;

    switch_type = *pFormat & 0xf;
    increment = (*pFormat & 0xf0) >> 4;
    pFormat++;

    ALIGN_LENGTH(pStubMsg->BufferLength, increment);
    switch_value = get_discriminant(switch_type, pMemory);
    TRACE("got switch value 0x%x\n", switch_value);

    /* Add discriminant size */
    NdrBaseTypeBufferSize(pStubMsg, (unsigned char *)&switch_value, &switch_type);
    pMemory += increment;

    union_arm_buffer_size(pStubMsg, pMemory, switch_value, pFormat);
}

/***********************************************************************
 *           NdrEncapsulatedUnionMemorySize [RPCRT4.@]
 */
ULONG WINAPI NdrEncapsulatedUnionMemorySize(PMIDL_STUB_MESSAGE pStubMsg,
                                PFORMAT_STRING pFormat)
{
    unsigned char switch_type;
    unsigned char increment;
    ULONG switch_value;

    switch_type = *pFormat & 0xf;
    increment = (*pFormat & 0xf0) >> 4;
    pFormat++;

    ALIGN_POINTER(pStubMsg->Buffer, increment);
    switch_value = get_discriminant(switch_type, pStubMsg->Buffer);
    TRACE("got switch value 0x%x\n", switch_value);

    pStubMsg->Memory += increment;

    return increment + union_arm_memory_size(pStubMsg, switch_value, pFormat + *(const SHORT*)pFormat);
}

/***********************************************************************
 *           NdrEncapsulatedUnionFree [RPCRT4.@]
 */
void WINAPI NdrEncapsulatedUnionFree(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
    unsigned char switch_type;
    unsigned char increment;
    ULONG switch_value;

    TRACE("(%p, %p, %p)\n", pStubMsg, pMemory, pFormat);
    pFormat++;

    switch_type = *pFormat & 0xf;
    increment = (*pFormat & 0xf0) >> 4;
    pFormat++;

    switch_value = get_discriminant(switch_type, pMemory);
    TRACE("got switch value 0x%x\n", switch_value);

    pMemory += increment;

    union_arm_free(pStubMsg, pMemory, switch_value, pFormat);
}

/***********************************************************************
 *           NdrNonEncapsulatedUnionMarshall [RPCRT4.@]
 */
unsigned char *  WINAPI NdrNonEncapsulatedUnionMarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
    unsigned char switch_type;

    TRACE("(%p, %p, %p)\n", pStubMsg, pMemory, pFormat);
    pFormat++;

    switch_type = *pFormat;
    pFormat++;

    pFormat = ComputeConformance(pStubMsg, pMemory, pFormat, 0);
    TRACE("got switch value 0x%lx\n", pStubMsg->MaxCount);
    /* Marshall discriminant */
    NdrBaseTypeMarshall(pStubMsg, (unsigned char *)&pStubMsg->MaxCount, &switch_type);

    return union_arm_marshall(pStubMsg, pMemory, pStubMsg->MaxCount, pFormat + *(const SHORT*)pFormat);
}

static long unmarshall_discriminant(PMIDL_STUB_MESSAGE pStubMsg,
                                    PFORMAT_STRING *ppFormat)
{
    long discriminant = 0;

    switch(**ppFormat)
    {
    case RPC_FC_BYTE:
    case RPC_FC_CHAR:
    case RPC_FC_SMALL:
    case RPC_FC_USMALL:
    {
        UCHAR d;
        safe_copy_from_buffer(pStubMsg, &d, sizeof(d));
        discriminant = d;
        break;
    }
    case RPC_FC_WCHAR:
    case RPC_FC_SHORT:
    case RPC_FC_USHORT:
    {
        USHORT d;
        ALIGN_POINTER(pStubMsg->Buffer, sizeof(USHORT));
        safe_copy_from_buffer(pStubMsg, &d, sizeof(d));
        discriminant = d;
        break;
    }
    case RPC_FC_LONG:
    case RPC_FC_ULONG:
    {
        ULONG d;
        ALIGN_POINTER(pStubMsg->Buffer, sizeof(ULONG));
        safe_copy_from_buffer(pStubMsg, &d, sizeof(d));
        discriminant = d;
        break;
    }
    default:
        FIXME("Unhandled base type: 0x%02x\n", **ppFormat);
    }
    (*ppFormat)++;

    if (pStubMsg->fHasNewCorrDesc)
        *ppFormat += 6;
    else
        *ppFormat += 4;
    return discriminant;
}

/**********************************************************************
 *           NdrNonEncapsulatedUnionUnmarshall [RPCRT4.@]
 */
unsigned char *  WINAPI NdrNonEncapsulatedUnionUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char **ppMemory,
                                PFORMAT_STRING pFormat,
                                unsigned char fMustAlloc)
{
    long discriminant;
    unsigned short size;

    TRACE("(%p, %p, %p, %d)\n", pStubMsg, ppMemory, pFormat, fMustAlloc);
    pFormat++;

    /* Unmarshall discriminant */
    discriminant = unmarshall_discriminant(pStubMsg, &pFormat);
    TRACE("unmarshalled discriminant %lx\n", discriminant);

    pFormat += *(const SHORT*)pFormat;

    size = *(const unsigned short*)pFormat;

    if(!*ppMemory || fMustAlloc)
        *ppMemory = NdrAllocate(pStubMsg, size);

    return union_arm_unmarshall(pStubMsg, ppMemory, discriminant, pFormat, fMustAlloc);
}

/***********************************************************************
 *           NdrNonEncapsulatedUnionBufferSize [RPCRT4.@]
 */
void WINAPI NdrNonEncapsulatedUnionBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
    unsigned char switch_type;

    TRACE("(%p, %p, %p)\n", pStubMsg, pMemory, pFormat);
    pFormat++;

    switch_type = *pFormat;
    pFormat++;

    pFormat = ComputeConformance(pStubMsg, pMemory, pFormat, 0);
    TRACE("got switch value 0x%lx\n", pStubMsg->MaxCount);
    /* Add discriminant size */
    NdrBaseTypeBufferSize(pStubMsg, (unsigned char *)&pStubMsg->MaxCount, &switch_type);

    union_arm_buffer_size(pStubMsg, pMemory, pStubMsg->MaxCount, pFormat + *(const SHORT*)pFormat);
}

/***********************************************************************
 *           NdrNonEncapsulatedUnionMemorySize [RPCRT4.@]
 */
ULONG WINAPI NdrNonEncapsulatedUnionMemorySize(PMIDL_STUB_MESSAGE pStubMsg,
                                PFORMAT_STRING pFormat)
{
    ULONG discriminant;

    pFormat++;
    /* Unmarshall discriminant */
    discriminant = unmarshall_discriminant(pStubMsg, &pFormat);
    TRACE("unmarshalled discriminant 0x%x\n", discriminant);

    return union_arm_memory_size(pStubMsg, discriminant, pFormat + *(const SHORT*)pFormat);
}

/***********************************************************************
 *           NdrNonEncapsulatedUnionFree [RPCRT4.@]
 */
void WINAPI NdrNonEncapsulatedUnionFree(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
    TRACE("(%p, %p, %p)\n", pStubMsg, pMemory, pFormat);
    pFormat++;
    pFormat++;

    pFormat = ComputeConformance(pStubMsg, pMemory, pFormat, 0);
    TRACE("got switch value 0x%lx\n", pStubMsg->MaxCount);

    union_arm_free(pStubMsg, pMemory, pStubMsg->MaxCount, pFormat + *(const SHORT*)pFormat);
}

/***********************************************************************
 *           NdrByteCountPointerMarshall [RPCRT4.@]
 */
unsigned char *  WINAPI NdrByteCountPointerMarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
    FIXME("stub\n");
    return NULL;
}

/***********************************************************************
 *           NdrByteCountPointerUnmarshall [RPCRT4.@]
 */
unsigned char *  WINAPI NdrByteCountPointerUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char **ppMemory,
                                PFORMAT_STRING pFormat,
                                unsigned char fMustAlloc)
{
    FIXME("stub\n");
    return NULL;
}

/***********************************************************************
 *           NdrByteCountPointerBufferSize [RPCRT4.@]
 */
void WINAPI NdrByteCountPointerBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
    FIXME("stub\n");
}

/***********************************************************************
 *           NdrByteCountPointerMemorySize [RPCRT4.@]
 */
ULONG WINAPI NdrByteCountPointerMemorySize(PMIDL_STUB_MESSAGE pStubMsg,
                                PFORMAT_STRING pFormat)
{
    FIXME("stub\n");
    return 0;
}

/***********************************************************************
 *           NdrByteCountPointerFree [RPCRT4.@]
 */
void WINAPI NdrByteCountPointerFree(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
    FIXME("stub\n");
}

/***********************************************************************
 *           NdrXmitOrRepAsMarshall [RPCRT4.@]
 */
unsigned char *  WINAPI NdrXmitOrRepAsMarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
    FIXME("stub\n");
    return NULL;
}

/***********************************************************************
 *           NdrXmitOrRepAsUnmarshall [RPCRT4.@]
 */
unsigned char *  WINAPI NdrXmitOrRepAsUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char **ppMemory,
                                PFORMAT_STRING pFormat,
                                unsigned char fMustAlloc)
{
    FIXME("stub\n");
    return NULL;
}

/***********************************************************************
 *           NdrXmitOrRepAsBufferSize [RPCRT4.@]
 */
void WINAPI NdrXmitOrRepAsBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
    FIXME("stub\n");
}

/***********************************************************************
 *           NdrXmitOrRepAsMemorySize [RPCRT4.@]
 */
ULONG WINAPI NdrXmitOrRepAsMemorySize(PMIDL_STUB_MESSAGE pStubMsg,
                                PFORMAT_STRING pFormat)
{
    FIXME("stub\n");
    return 0;
}

/***********************************************************************
 *           NdrXmitOrRepAsFree [RPCRT4.@]
 */
void WINAPI NdrXmitOrRepAsFree(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
    FIXME("stub\n");
}

/***********************************************************************
 *           NdrRangeMarshall [internal]
 */
unsigned char *WINAPI NdrRangeMarshall(
    PMIDL_STUB_MESSAGE pStubMsg,
    unsigned char *pMemory,
    PFORMAT_STRING pFormat)
{
    NDR_RANGE *pRange = (NDR_RANGE *)pFormat;
    unsigned char base_type;

    TRACE("pStubMsg %p, pMemory %p, type 0x%02x\n", pStubMsg, pMemory, *pFormat);

    if (pRange->type != RPC_FC_RANGE)
    {
        ERR("invalid format type %x\n", pRange->type);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return NULL;
    }

    base_type = pRange->flags_type & 0xf;

    return NdrBaseTypeMarshall(pStubMsg, pMemory, &base_type);
}

/***********************************************************************
 *           NdrRangeUnmarshall
 */
unsigned char *WINAPI NdrRangeUnmarshall(
    PMIDL_STUB_MESSAGE pStubMsg,
    unsigned char **ppMemory,
    PFORMAT_STRING pFormat,
    unsigned char fMustAlloc)
{
    NDR_RANGE *pRange = (NDR_RANGE *)pFormat;
    unsigned char base_type;

    TRACE("pStubMsg: %p, ppMemory: %p, type: 0x%02x, fMustAlloc: %s\n", pStubMsg, ppMemory, *pFormat, fMustAlloc ? "true" : "false");

    if (pRange->type != RPC_FC_RANGE)
    {
        ERR("invalid format type %x\n", pRange->type);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return NULL;
    }
    base_type = pRange->flags_type & 0xf;

    TRACE("base_type = 0x%02x, low_value = %d, high_value = %d\n",
        base_type, pRange->low_value, pRange->high_value);

#define RANGE_UNMARSHALL(type, format_spec) \
    do \
    { \
        ALIGN_POINTER(pStubMsg->Buffer, sizeof(type)); \
        if (fMustAlloc || !*ppMemory) \
            *ppMemory = NdrAllocate(pStubMsg, sizeof(type)); \
        if (pStubMsg->Buffer + sizeof(type) > pStubMsg->BufferEnd) \
        { \
            ERR("buffer overflow - Buffer = %p, BufferEnd = %p\n", \
                pStubMsg->Buffer, (unsigned char *)pStubMsg->RpcMsg->Buffer + pStubMsg->BufferLength); \
            RpcRaiseException(RPC_X_BAD_STUB_DATA); \
        } \
        if ((*(type *)pStubMsg->Buffer < (type)pRange->low_value) || \
            (*(type *)pStubMsg->Buffer > (type)pRange->high_value)) \
        { \
            ERR("value exceeded bounds: " format_spec ", low: " format_spec ", high: " format_spec "\n", \
                *(type *)pStubMsg->Buffer, (type)pRange->low_value, \
                (type)pRange->high_value); \
            RpcRaiseException(RPC_S_INVALID_BOUND); \
            return NULL; \
        } \
        TRACE("*ppMemory: %p\n", *ppMemory); \
        **(type **)ppMemory = *(type *)pStubMsg->Buffer; \
        pStubMsg->Buffer += sizeof(type); \
    } while (0)

    switch(base_type)
    {
    case RPC_FC_CHAR:
    case RPC_FC_SMALL:
        RANGE_UNMARSHALL(UCHAR, "%d");
        TRACE("value: 0x%02x\n", **ppMemory);
        break;
    case RPC_FC_BYTE:
    case RPC_FC_USMALL:
        RANGE_UNMARSHALL(CHAR, "%u");
        TRACE("value: 0x%02x\n", **ppMemory);
        break;
    case RPC_FC_WCHAR: /* FIXME: valid? */
    case RPC_FC_USHORT:
        RANGE_UNMARSHALL(USHORT, "%u");
        TRACE("value: 0x%04x\n", **(USHORT **)ppMemory);
        break;
    case RPC_FC_SHORT:
        RANGE_UNMARSHALL(SHORT, "%d");
        TRACE("value: 0x%04x\n", **(USHORT **)ppMemory);
        break;
    case RPC_FC_LONG:
        RANGE_UNMARSHALL(LONG, "%d");
        TRACE("value: 0x%08x\n", **(ULONG **)ppMemory);
        break;
    case RPC_FC_ULONG:
        RANGE_UNMARSHALL(ULONG, "%u");
        TRACE("value: 0x%08x\n", **(ULONG **)ppMemory);
        break;
    case RPC_FC_ENUM16:
    case RPC_FC_ENUM32:
        FIXME("Unhandled enum type\n");
        break;
    case RPC_FC_ERROR_STATUS_T: /* FIXME: valid? */
    case RPC_FC_FLOAT:
    case RPC_FC_DOUBLE:
    case RPC_FC_HYPER:
    default:
        ERR("invalid range base type: 0x%02x\n", base_type);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
    }

    return NULL;
}

/***********************************************************************
 *           NdrRangeBufferSize [internal]
 */
void WINAPI NdrRangeBufferSize(
    PMIDL_STUB_MESSAGE pStubMsg,
    unsigned char *pMemory,
    PFORMAT_STRING pFormat)
{
    NDR_RANGE *pRange = (NDR_RANGE *)pFormat;
    unsigned char base_type;

    TRACE("pStubMsg %p, pMemory %p, type 0x%02x\n", pStubMsg, pMemory, *pFormat);

    if (pRange->type != RPC_FC_RANGE)
    {
        ERR("invalid format type %x\n", pRange->type);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
    }
    base_type = pRange->flags_type & 0xf;

    NdrBaseTypeBufferSize(pStubMsg, pMemory, &base_type);
}

/***********************************************************************
 *           NdrRangeMemorySize [internal]
 */
ULONG WINAPI NdrRangeMemorySize(
    PMIDL_STUB_MESSAGE pStubMsg,
    PFORMAT_STRING pFormat)
{
    NDR_RANGE *pRange = (NDR_RANGE *)pFormat;
    unsigned char base_type;

    if (pRange->type != RPC_FC_RANGE)
    {
        ERR("invalid format type %x\n", pRange->type);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
        return 0;
    }
    base_type = pRange->flags_type & 0xf;

    return NdrBaseTypeMemorySize(pStubMsg, &base_type);
}

/***********************************************************************
 *           NdrRangeFree [internal]
 */
void WINAPI NdrRangeFree(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
   TRACE("pStubMsg %p pMemory %p type 0x%02x\n", pStubMsg, pMemory, *pFormat);

   /* nothing to do */
}

/***********************************************************************
 *           NdrBaseTypeMarshall [internal]
 */
static unsigned char *WINAPI NdrBaseTypeMarshall(
    PMIDL_STUB_MESSAGE pStubMsg,
    unsigned char *pMemory,
    PFORMAT_STRING pFormat)
{
    TRACE("pStubMsg %p, pMemory %p, type 0x%02x\n", pStubMsg, pMemory, *pFormat);

    switch(*pFormat)
    {
    case RPC_FC_BYTE:
    case RPC_FC_CHAR:
    case RPC_FC_SMALL:
    case RPC_FC_USMALL:
        safe_copy_to_buffer(pStubMsg, pMemory, sizeof(UCHAR));
        TRACE("value: 0x%02x\n", *pMemory);
        break;
    case RPC_FC_WCHAR:
    case RPC_FC_SHORT:
    case RPC_FC_USHORT:
        ALIGN_POINTER_CLEAR(pStubMsg->Buffer, sizeof(USHORT));
        safe_copy_to_buffer(pStubMsg, pMemory, sizeof(USHORT));
        TRACE("value: 0x%04x\n", *(USHORT *)pMemory);
        break;
    case RPC_FC_LONG:
    case RPC_FC_ULONG:
    case RPC_FC_ERROR_STATUS_T:
    case RPC_FC_ENUM32:
        ALIGN_POINTER_CLEAR(pStubMsg->Buffer, sizeof(ULONG));
        safe_copy_to_buffer(pStubMsg, pMemory, sizeof(ULONG));
        TRACE("value: 0x%08x\n", *(ULONG *)pMemory);
        break;
    case RPC_FC_FLOAT:
        ALIGN_POINTER_CLEAR(pStubMsg->Buffer, sizeof(float));
        safe_copy_to_buffer(pStubMsg, pMemory, sizeof(float));
        break;
    case RPC_FC_DOUBLE:
        ALIGN_POINTER_CLEAR(pStubMsg->Buffer, sizeof(double));
        safe_copy_to_buffer(pStubMsg, pMemory, sizeof(double));
        break;
    case RPC_FC_HYPER:
        ALIGN_POINTER_CLEAR(pStubMsg->Buffer, sizeof(ULONGLONG));
        safe_copy_to_buffer(pStubMsg, pMemory, sizeof(ULONGLONG));
        TRACE("value: %s\n", wine_dbgstr_longlong(*(ULONGLONG*)pMemory));
        break;
    case RPC_FC_ENUM16:
        /* only 16-bits on the wire, so do a sanity check */
        if (*(UINT *)pMemory > SHRT_MAX)
            RpcRaiseException(RPC_X_ENUM_VALUE_OUT_OF_RANGE);
        ALIGN_POINTER_CLEAR(pStubMsg->Buffer, sizeof(USHORT));
        if (pStubMsg->Buffer + sizeof(USHORT) > (unsigned char *)pStubMsg->RpcMsg->Buffer + pStubMsg->BufferLength)
            RpcRaiseException(RPC_X_BAD_STUB_DATA);
        *(USHORT *)pStubMsg->Buffer = *(UINT *)pMemory;
        pStubMsg->Buffer += sizeof(USHORT);
        TRACE("value: 0x%04x\n", *(UINT *)pMemory);
        break;
    case RPC_FC_IGNORE:
        break;
    default:
        FIXME("Unhandled base type: 0x%02x\n", *pFormat);
    }

    /* FIXME: what is the correct return value? */
    return NULL;
}

/***********************************************************************
 *           NdrBaseTypeUnmarshall [internal]
 */
static unsigned char *WINAPI NdrBaseTypeUnmarshall(
    PMIDL_STUB_MESSAGE pStubMsg,
    unsigned char **ppMemory,
    PFORMAT_STRING pFormat,
    unsigned char fMustAlloc)
{
    TRACE("pStubMsg: %p, ppMemory: %p, type: 0x%02x, fMustAlloc: %s\n", pStubMsg, ppMemory, *pFormat, fMustAlloc ? "true" : "false");

#define BASE_TYPE_UNMARSHALL(type) \
        ALIGN_POINTER(pStubMsg->Buffer, sizeof(type)); \
        if (!fMustAlloc && !pStubMsg->IsClient && !*ppMemory) \
        { \
            *ppMemory = pStubMsg->Buffer; \
            TRACE("*ppMemory: %p\n", *ppMemory); \
            safe_buffer_increment(pStubMsg, sizeof(type)); \
        } \
        else \
        {  \
            if (fMustAlloc) \
                *ppMemory = NdrAllocate(pStubMsg, sizeof(type)); \
            TRACE("*ppMemory: %p\n", *ppMemory); \
            safe_copy_from_buffer(pStubMsg, *ppMemory, sizeof(type)); \
        }

    switch(*pFormat)
    {
    case RPC_FC_BYTE:
    case RPC_FC_CHAR:
    case RPC_FC_SMALL:
    case RPC_FC_USMALL:
        BASE_TYPE_UNMARSHALL(UCHAR);
        TRACE("value: 0x%02x\n", **ppMemory);
        break;
    case RPC_FC_WCHAR:
    case RPC_FC_SHORT:
    case RPC_FC_USHORT:
        BASE_TYPE_UNMARSHALL(USHORT);
        TRACE("value: 0x%04x\n", **(USHORT **)ppMemory);
        break;
    case RPC_FC_LONG:
    case RPC_FC_ULONG:
    case RPC_FC_ERROR_STATUS_T:
    case RPC_FC_ENUM32:
        BASE_TYPE_UNMARSHALL(ULONG);
        TRACE("value: 0x%08x\n", **(ULONG **)ppMemory);
        break;
   case RPC_FC_FLOAT:
        BASE_TYPE_UNMARSHALL(float);
        TRACE("value: %f\n", **(float **)ppMemory);
        break;
    case RPC_FC_DOUBLE:
        BASE_TYPE_UNMARSHALL(double);
        TRACE("value: %f\n", **(double **)ppMemory);
        break;
    case RPC_FC_HYPER:
        BASE_TYPE_UNMARSHALL(ULONGLONG);
        TRACE("value: %s\n", wine_dbgstr_longlong(**(ULONGLONG **)ppMemory));
        break;
    case RPC_FC_ENUM16:
        ALIGN_POINTER(pStubMsg->Buffer, sizeof(USHORT));
        if (fMustAlloc || !*ppMemory)
            *ppMemory = NdrAllocate(pStubMsg, sizeof(UINT));
        if (pStubMsg->Buffer + sizeof(USHORT) > pStubMsg->BufferEnd)
            RpcRaiseException(RPC_X_BAD_STUB_DATA);
        TRACE("*ppMemory: %p\n", *ppMemory);
        /* 16-bits on the wire, but int in memory */
        **(UINT **)ppMemory = *(USHORT *)pStubMsg->Buffer;
        pStubMsg->Buffer += sizeof(USHORT);
        TRACE("value: 0x%08x\n", **(UINT **)ppMemory);
        break;
    case RPC_FC_IGNORE:
        break;
    default:
        FIXME("Unhandled base type: 0x%02x\n", *pFormat);
    }
#undef BASE_TYPE_UNMARSHALL

    /* FIXME: what is the correct return value? */

    return NULL;
}

/***********************************************************************
 *           NdrBaseTypeBufferSize [internal]
 */
static void WINAPI NdrBaseTypeBufferSize(
    PMIDL_STUB_MESSAGE pStubMsg,
    unsigned char *pMemory,
    PFORMAT_STRING pFormat)
{
    TRACE("pStubMsg %p, pMemory %p, type 0x%02x\n", pStubMsg, pMemory, *pFormat);

    switch(*pFormat)
    {
    case RPC_FC_BYTE:
    case RPC_FC_CHAR:
    case RPC_FC_SMALL:
    case RPC_FC_USMALL:
        safe_buffer_length_increment(pStubMsg, sizeof(UCHAR));
        break;
    case RPC_FC_WCHAR:
    case RPC_FC_SHORT:
    case RPC_FC_USHORT:
    case RPC_FC_ENUM16:
        ALIGN_LENGTH(pStubMsg->BufferLength, sizeof(USHORT));
        safe_buffer_length_increment(pStubMsg, sizeof(USHORT));
        break;
    case RPC_FC_LONG:
    case RPC_FC_ULONG:
    case RPC_FC_ENUM32:
        ALIGN_LENGTH(pStubMsg->BufferLength, sizeof(ULONG));
        safe_buffer_length_increment(pStubMsg, sizeof(ULONG));
        break;
    case RPC_FC_FLOAT:
        ALIGN_LENGTH(pStubMsg->BufferLength, sizeof(float));
        safe_buffer_length_increment(pStubMsg, sizeof(float));
        break;
    case RPC_FC_DOUBLE:
        ALIGN_LENGTH(pStubMsg->BufferLength, sizeof(double));
        safe_buffer_length_increment(pStubMsg, sizeof(double));
        break;
    case RPC_FC_HYPER:
        ALIGN_LENGTH(pStubMsg->BufferLength, sizeof(ULONGLONG));
        safe_buffer_length_increment(pStubMsg, sizeof(ULONGLONG));
        break;
    case RPC_FC_ERROR_STATUS_T:
        ALIGN_LENGTH(pStubMsg->BufferLength, sizeof(error_status_t));
        safe_buffer_length_increment(pStubMsg, sizeof(error_status_t));
        break;
    case RPC_FC_IGNORE:
        break;
    default:
        FIXME("Unhandled base type: 0x%02x\n", *pFormat);
    }
}

/***********************************************************************
 *           NdrBaseTypeMemorySize [internal]
 */
static ULONG WINAPI NdrBaseTypeMemorySize(
    PMIDL_STUB_MESSAGE pStubMsg,
    PFORMAT_STRING pFormat)
{
    TRACE("pStubMsg %p, type 0x%02x\n", pStubMsg, *pFormat);

    switch(*pFormat)
    {
    case RPC_FC_BYTE:
    case RPC_FC_CHAR:
    case RPC_FC_SMALL:
    case RPC_FC_USMALL:
        safe_buffer_increment(pStubMsg, sizeof(UCHAR));
        pStubMsg->MemorySize += sizeof(UCHAR);
        return sizeof(UCHAR);
    case RPC_FC_WCHAR:
    case RPC_FC_SHORT:
    case RPC_FC_USHORT:
        safe_buffer_increment(pStubMsg, sizeof(USHORT));
        pStubMsg->MemorySize += sizeof(USHORT);
        return sizeof(USHORT);
    case RPC_FC_LONG:
    case RPC_FC_ULONG:
    case RPC_FC_ENUM32:
        safe_buffer_increment(pStubMsg, sizeof(ULONG));
        pStubMsg->MemorySize += sizeof(ULONG);
        return sizeof(ULONG);
    case RPC_FC_FLOAT:
        safe_buffer_increment(pStubMsg, sizeof(float));
        pStubMsg->MemorySize += sizeof(float);
        return sizeof(float);
    case RPC_FC_DOUBLE:
        safe_buffer_increment(pStubMsg, sizeof(double));
        pStubMsg->MemorySize += sizeof(double);
        return sizeof(double);
    case RPC_FC_HYPER:
        safe_buffer_increment(pStubMsg, sizeof(ULONGLONG));
        pStubMsg->MemorySize += sizeof(ULONGLONG);
        return sizeof(ULONGLONG);
    case RPC_FC_ERROR_STATUS_T:
        safe_buffer_increment(pStubMsg, sizeof(error_status_t));
        pStubMsg->MemorySize += sizeof(error_status_t);
        return sizeof(error_status_t);
    case RPC_FC_ENUM16:
        safe_buffer_increment(pStubMsg, sizeof(USHORT));
        pStubMsg->MemorySize += sizeof(UINT);
        return sizeof(UINT);
    case RPC_FC_IGNORE:
        pStubMsg->MemorySize += sizeof(void *);
        return sizeof(void *);
    default:
        FIXME("Unhandled base type: 0x%02x\n", *pFormat);
       return 0;
    }
}

/***********************************************************************
 *           NdrBaseTypeFree [internal]
 */
static void WINAPI NdrBaseTypeFree(PMIDL_STUB_MESSAGE pStubMsg,
                                unsigned char *pMemory,
                                PFORMAT_STRING pFormat)
{
   TRACE("pStubMsg %p pMemory %p type 0x%02x\n", pStubMsg, pMemory, *pFormat);

   /* nothing to do */
}

/***********************************************************************
 *           NdrContextHandleBufferSize [internal]
 */
static void WINAPI NdrContextHandleBufferSize(
    PMIDL_STUB_MESSAGE pStubMsg,
    unsigned char *pMemory,
    PFORMAT_STRING pFormat)
{
    TRACE("pStubMsg %p, pMemory %p, type 0x%02x\n", pStubMsg, pMemory, *pFormat);

    if (*pFormat != RPC_FC_BIND_CONTEXT)
    {
        ERR("invalid format type %x\n", *pFormat);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
    }
    ALIGN_LENGTH(pStubMsg->BufferLength, 4);
    safe_buffer_length_increment(pStubMsg, cbNDRContext);
}

/***********************************************************************
 *           NdrContextHandleMarshall [internal]
 */
static unsigned char *WINAPI NdrContextHandleMarshall(
    PMIDL_STUB_MESSAGE pStubMsg,
    unsigned char *pMemory,
    PFORMAT_STRING pFormat)
{
    TRACE("pStubMsg %p, pMemory %p, type 0x%02x\n", pStubMsg, pMemory, *pFormat);

    if (*pFormat != RPC_FC_BIND_CONTEXT)
    {
        ERR("invalid format type %x\n", *pFormat);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
    }
    TRACE("flags: 0x%02x\n", pFormat[1]);

    if (pFormat[1] & 0x80)
        NdrClientContextMarshall(pStubMsg, *(NDR_CCONTEXT **)pMemory, FALSE);
    else
        NdrClientContextMarshall(pStubMsg, (NDR_CCONTEXT *)pMemory, FALSE);

    return NULL;
}

/***********************************************************************
 *           NdrContextHandleUnmarshall [internal]
 */
static unsigned char *WINAPI NdrContextHandleUnmarshall(
    PMIDL_STUB_MESSAGE pStubMsg,
    unsigned char **ppMemory,
    PFORMAT_STRING pFormat,
    unsigned char fMustAlloc)
{
    TRACE("pStubMsg %p, ppMemory %p, pFormat %p, fMustAlloc %s\n", pStubMsg,
        ppMemory, pFormat, fMustAlloc ? "TRUE": "FALSE");

    if (*pFormat != RPC_FC_BIND_CONTEXT)
    {
        ERR("invalid format type %x\n", *pFormat);
        RpcRaiseException(RPC_S_INTERNAL_ERROR);
    }
    TRACE("flags: 0x%02x\n", pFormat[1]);

    /* [out]-only or [ret] param */
    if ((pFormat[1] & 0x60) == 0x20)
        **(NDR_CCONTEXT **)ppMemory = NULL;
    NdrClientContextUnmarshall(pStubMsg, *(NDR_CCONTEXT **)ppMemory, pStubMsg->RpcMsg->Handle);

    return NULL;
}

/***********************************************************************
 *           NdrClientContextMarshall [RPCRT4.@]
 */
void WINAPI NdrClientContextMarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                     NDR_CCONTEXT ContextHandle,
                                     int fCheck)
{
    TRACE("(%p, %p, %d)\n", pStubMsg, ContextHandle, fCheck);

    ALIGN_POINTER_CLEAR(pStubMsg->Buffer, 4);

    if (pStubMsg->Buffer + cbNDRContext > (unsigned char *)pStubMsg->RpcMsg->Buffer + pStubMsg->BufferLength)
    {
        ERR("buffer overflow - Buffer = %p, BufferEnd = %p\n",
            pStubMsg->Buffer, (unsigned char *)pStubMsg->RpcMsg->Buffer + pStubMsg->BufferLength);
        RpcRaiseException(RPC_X_BAD_STUB_DATA);
    }

    /* FIXME: what does fCheck do? */
    NDRCContextMarshall(ContextHandle,
                        pStubMsg->Buffer);

    pStubMsg->Buffer += cbNDRContext;
}

/***********************************************************************
 *           NdrClientContextUnmarshall [RPCRT4.@]
 */
void WINAPI NdrClientContextUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                       NDR_CCONTEXT * pContextHandle,
                                       RPC_BINDING_HANDLE BindHandle)
{
    TRACE("(%p, %p, %p)\n", pStubMsg, pContextHandle, BindHandle);

    ALIGN_POINTER(pStubMsg->Buffer, 4);

    if (pStubMsg->Buffer + cbNDRContext > pStubMsg->BufferEnd)
        RpcRaiseException(RPC_X_BAD_STUB_DATA);

    NDRCContextUnmarshall(pContextHandle,
                          BindHandle,
                          pStubMsg->Buffer,
                          pStubMsg->RpcMsg->DataRepresentation);

    pStubMsg->Buffer += cbNDRContext;
}

void WINAPI NdrServerContextMarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                     NDR_SCONTEXT ContextHandle,
                                     NDR_RUNDOWN RundownRoutine )
{
    TRACE("(%p, %p, %p)\n", pStubMsg, ContextHandle, RundownRoutine);

    ALIGN_POINTER(pStubMsg->Buffer, 4);

    if (pStubMsg->Buffer + cbNDRContext > (unsigned char *)pStubMsg->RpcMsg->Buffer + pStubMsg->BufferLength)
    {
        ERR("buffer overflow - Buffer = %p, BufferEnd = %p\n",
            pStubMsg->Buffer, (unsigned char *)pStubMsg->RpcMsg->Buffer + pStubMsg->BufferLength);
        RpcRaiseException(RPC_X_BAD_STUB_DATA);
    }

    NDRSContextMarshall2(pStubMsg->RpcMsg->Handle, ContextHandle,
                         pStubMsg->Buffer, RundownRoutine, NULL,
                         RPC_CONTEXT_HANDLE_DEFAULT_FLAGS);
    pStubMsg->Buffer += cbNDRContext;
}

NDR_SCONTEXT WINAPI NdrServerContextUnmarshall(PMIDL_STUB_MESSAGE pStubMsg)
{
    NDR_SCONTEXT ContextHandle;

    TRACE("(%p)\n", pStubMsg);

    ALIGN_POINTER(pStubMsg->Buffer, 4);

    if (pStubMsg->Buffer + cbNDRContext > (unsigned char *)pStubMsg->RpcMsg->Buffer + pStubMsg->BufferLength)
    {
        ERR("buffer overflow - Buffer = %p, BufferEnd = %p\n",
            pStubMsg->Buffer, (unsigned char *)pStubMsg->RpcMsg->Buffer + pStubMsg->BufferLength);
        RpcRaiseException(RPC_X_BAD_STUB_DATA);
    }

    ContextHandle = NDRSContextUnmarshall2(pStubMsg->RpcMsg->Handle,
                                           pStubMsg->Buffer,
                                           pStubMsg->RpcMsg->DataRepresentation,
                                           NULL, RPC_CONTEXT_HANDLE_DEFAULT_FLAGS);
    pStubMsg->Buffer += cbNDRContext;

    return ContextHandle;
}

void WINAPI NdrContextHandleSize(PMIDL_STUB_MESSAGE pStubMsg,
                                 unsigned char* pMemory,
                                 PFORMAT_STRING pFormat)
{
    FIXME("(%p, %p, %p): stub\n", pStubMsg, pMemory, pFormat);
}

NDR_SCONTEXT WINAPI NdrContextHandleInitialize(PMIDL_STUB_MESSAGE pStubMsg,
                                               PFORMAT_STRING pFormat)
{
    RPC_SYNTAX_IDENTIFIER *if_id = NULL;
    ULONG flags = RPC_CONTEXT_HANDLE_DEFAULT_FLAGS;

    TRACE("(%p, %p)\n", pStubMsg, pFormat);

    if (pFormat[1] & NDR_CONTEXT_HANDLE_SERIALIZE)
        flags |= RPC_CONTEXT_HANDLE_SERIALIZE;
    if (pFormat[1] & NDR_CONTEXT_HANDLE_NO_SERIALIZE)
        flags |= RPC_CONTEXT_HANDLE_DONT_SERIALIZE;
    if (pFormat[1] & NDR_STRICT_CONTEXT_HANDLE)
    {
        RPC_SERVER_INTERFACE *sif = pStubMsg->StubDesc->RpcInterfaceInformation;
        if_id = &sif->InterfaceId;
    }

    return NDRSContextUnmarshall2(pStubMsg->RpcMsg->Handle, NULL,
                                  pStubMsg->RpcMsg->DataRepresentation, if_id,
                                  flags);
}

void WINAPI NdrServerContextNewMarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                        NDR_SCONTEXT ContextHandle,
                                        NDR_RUNDOWN RundownRoutine,
                                        PFORMAT_STRING pFormat)
{
    RPC_SYNTAX_IDENTIFIER *if_id = NULL;
    ULONG flags = RPC_CONTEXT_HANDLE_DEFAULT_FLAGS;

    TRACE("(%p, %p, %p, %p)\n", pStubMsg, ContextHandle, RundownRoutine, pFormat);

    ALIGN_POINTER(pStubMsg->Buffer, 4);

    if (pStubMsg->Buffer + cbNDRContext > (unsigned char *)pStubMsg->RpcMsg->Buffer + pStubMsg->BufferLength)
    {
        ERR("buffer overflow - Buffer = %p, BufferEnd = %p\n",
            pStubMsg->Buffer, (unsigned char *)pStubMsg->RpcMsg->Buffer + pStubMsg->BufferLength);
        RpcRaiseException(RPC_X_BAD_STUB_DATA);
    }

    if (pFormat[1] & NDR_CONTEXT_HANDLE_SERIALIZE)
        flags |= RPC_CONTEXT_HANDLE_SERIALIZE;
    if (pFormat[1] & NDR_CONTEXT_HANDLE_NO_SERIALIZE)
        flags |= RPC_CONTEXT_HANDLE_DONT_SERIALIZE;
    if (pFormat[1] & NDR_STRICT_CONTEXT_HANDLE)
    {
        RPC_SERVER_INTERFACE *sif = pStubMsg->StubDesc->RpcInterfaceInformation;
        if_id = &sif->InterfaceId;
    }

    NDRSContextMarshall2(pStubMsg->RpcMsg->Handle, ContextHandle,
                          pStubMsg->Buffer, RundownRoutine, if_id, flags);
    pStubMsg->Buffer += cbNDRContext;
}

NDR_SCONTEXT WINAPI NdrServerContextNewUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
                                                  PFORMAT_STRING pFormat)
{
    NDR_SCONTEXT ContextHandle;
    RPC_SYNTAX_IDENTIFIER *if_id = NULL;
    ULONG flags = RPC_CONTEXT_HANDLE_DEFAULT_FLAGS;

    TRACE("(%p, %p)\n", pStubMsg, pFormat);

    ALIGN_POINTER(pStubMsg->Buffer, 4);

    if (pStubMsg->Buffer + cbNDRContext > (unsigned char *)pStubMsg->RpcMsg->Buffer + pStubMsg->BufferLength)
    {
        ERR("buffer overflow - Buffer = %p, BufferEnd = %p\n",
            pStubMsg->Buffer, (unsigned char *)pStubMsg->RpcMsg->Buffer + pStubMsg->BufferLength);
        RpcRaiseException(RPC_X_BAD_STUB_DATA);
    }

    if (pFormat[1] & NDR_CONTEXT_HANDLE_SERIALIZE)
        flags |= RPC_CONTEXT_HANDLE_SERIALIZE;
    if (pFormat[1] & NDR_CONTEXT_HANDLE_NO_SERIALIZE)
        flags |= RPC_CONTEXT_HANDLE_DONT_SERIALIZE;
    if (pFormat[1] & NDR_STRICT_CONTEXT_HANDLE)
    {
        RPC_SERVER_INTERFACE *sif = pStubMsg->StubDesc->RpcInterfaceInformation;
        if_id = &sif->InterfaceId;
    }

    ContextHandle = NDRSContextUnmarshall2(pStubMsg->RpcMsg->Handle,
                                           pStubMsg->Buffer,
                                           pStubMsg->RpcMsg->DataRepresentation,
                                           if_id, flags);
    pStubMsg->Buffer += cbNDRContext;

    return ContextHandle;
}

/***********************************************************************
 *           NdrCorrelationInitialize [RPCRT4.@]
 *
 * Initializes correlation validity checking.
 *
 * PARAMS
 *  pStubMsg    [I] MIDL_STUB_MESSAGE used during unmarshalling.
 *  pMemory     [I] Pointer to memory to use as a cache.
 *  CacheSize   [I] Size of the memory pointed to by pMemory.
 *  Flags       [I] Reserved. Set to zero.
 *
 * RETURNS
 *  Nothing.
 */
void WINAPI NdrCorrelationInitialize(PMIDL_STUB_MESSAGE pStubMsg, void *pMemory, ULONG CacheSize, ULONG Flags)
{
    FIXME("(%p, %p, %d, 0x%x): stub\n", pStubMsg, pMemory, CacheSize, Flags);
    pStubMsg->fHasNewCorrDesc = TRUE;
}

/***********************************************************************
 *           NdrCorrelationPass [RPCRT4.@]
 *
 * Performs correlation validity checking.
 *
 * PARAMS
 *  pStubMsg    [I] MIDL_STUB_MESSAGE used during unmarshalling.
 *
 * RETURNS
 *  Nothing.
 */
void WINAPI NdrCorrelationPass(PMIDL_STUB_MESSAGE pStubMsg)
{
    FIXME("(%p): stub\n", pStubMsg);
}

/***********************************************************************
 *           NdrCorrelationFree [RPCRT4.@]
 *
 * Frees any resources used while unmarshalling parameters that need
 * correlation validity checking.
 *
 * PARAMS
 *  pStubMsg    [I] MIDL_STUB_MESSAGE used during unmarshalling.
 *
 * RETURNS
 *  Nothing.
 */
void WINAPI NdrCorrelationFree(PMIDL_STUB_MESSAGE pStubMsg)
{
    FIXME("(%p): stub\n", pStubMsg);
}
