// Copyright (c) 1998 Microsoft Corporation
// Validate.h
//
// Pointer validation macros
//
// Summary:
//
// V_INAME(interfacename)                - Set the interface name for error display
// V_STRUCTPTR_READ(ptr,type)            - A dwSize struct which we will read
// V_STRUCTPTR_WRITE(ptr,type)           - A dwSize struct which we will read/write
// V_PTR_READ(ptr,type)                  - A typed ptr w/o a dwSize which we will read
// V_PTR_WRITE(ptr,type)                 - A typed ptr w/o a dwSize which we will read/write
// V_PTR_WRITE_OPT(ptr,type)             - An optional typed ptr w/o a dwSize which we will read/write
// V_BUFPTR_READ(ptr,size)               - A variable-size buffer that we will read
// V_BUFPTR_READ_OPT(ptr,size)			 - An optional variable-size buffer that we will read
// V_BUFPTR_WRITE(ptr,size)              - A variable-size buffer that we will read/write
// V_BUFPTR_WRITE_OPT(ptr,size)          - An optional variable-size buffer that we will read/write
// V_PTRPTR_WRITE(ptrptr)                - A pointer to a pointer to write to
// V_PTRPTR_WRITE_OPT(ptrptr)            - A pointer to a pointer to write to that is optional
// V_PUNKOUTER(punk)                     - A pointer to a controlling unknown, aggregation supported
// V_PUNKOUTER_NOADD(punk)               - A pointer to a controlling unknown, aggregation not supported
// V_INTERFACE(ptr)                      - A pointer to a COM interface
// V_INTERFACE_OPT(ptr)                  - An optional pointer to a COM interface
// V_REFGUID(ref)                        - A reference to a GUID (type REFGUID)
// V_HWND(hwnd)							 - A window handle
// V_HWNDOPT(hwnd)						 - An optional window handle
//
// 'optional' means the pointer is allowed to be NULL by the interface specification.
//
// Sample usage:
//
// int IDirectMusic::SetFooBarInterface(
//     LPDMUS_REQUESTED_CAPS    pCaps,          // Caps w/ dwSize (read-only)
//     LPVOID                   pBuffer,        // Buffer we will fill in
//     DWORD                    cbSize,         // Size of the buffer
//     PDIRECTMUSICBAR          pBar)           // Callback interface for bar on this buffer
// {
//     V_INTERFACE(IDirectMusic::SetFooBarInterface);
//     V_STRUCTPTR_READ(pCaps, DMUS_REQUESTED_CAPS);
//     V_BUFPTR_WRITE(pBuffer, cbSize);
//     V_INTERFACE(pBar);
//
//     ...
// }
//
#ifndef _VALIDATE_H_
#define _VALIDATE_H_


#ifdef DBG
#include <stddef.h>

#include "debug.h"

// To turn on DebugBreak on parameter error, use the following or -DRIP_BREAK in the build:
//
//#define RIP_BREAK 1

#ifdef RIP_BREAK
#define _RIP_BREAK DebugBreak();
#else
#define _RIP_BREAK 
#endif

#define V_INAME(x) \
    static const char __szValidateInterfaceName[] = #x;                       

#define RIP_E_POINTER(ptr) \
{   Trace(-1, "%s: Invalid pointer " #ptr "\n", __szValidateInterfaceName); \
    _RIP_BREAK \
    return E_POINTER; }

#define RIP_E_INVALIDARG(ptr) \
{   Trace(-1, "%s: Invalid argument " #ptr "\n", __szValidateInterfaceName); \
    _RIP_BREAK \
    return E_INVALIDARG; }

#define RIP_E_HANDLE(h) \
{	Trace(-1, "%s: Invalid handle " #h "\n", __szValidateInterfaceName); \
	return E_HANDLE; }

// NOTE: The DebugBreak() not in #ifdef is intentional - this is something that
// must be fixed in our code, not an app-generated error.
//
#define V_ASSERT(exp) \
{   if (!(exp)) { \
        Trace(-1, "%s@%s: %s\n", __FILE__, __LINE__, #exp); \
        DebugBreak(); }}

#else

#define V_INAME(x)
#define RIP_E_POINTER(ptr)    { return E_POINTER; }
#define RIP_E_INVALIDARG(ptr) { return E_INVALIDARG; }
#define RIP_E_HANDLE(h)	      { return E_HANDLE; }
#define V_ASSERT(exp)

#endif          // DBG

// A passed struct we will only read from or may write to. Must be a struct
// with a dwSize.
//
// int foo(CFoo *pFoo)
// ...
// V_STRUCTPTR_READ(pFoo, CFoo);
// V_STRUCTPTR_WRITE(pFoo, CFoo);
//
// Use _PTR_ variants for structs w/o a dwSize
//
#define V_STRUCTPTR_READ(ptr,type) \
{   V_ASSERT(offsetof(type, dwSize) == 0); \
    if (IsBadReadPtr(ptr, sizeof(DWORD)))               RIP_E_POINTER(ptr); \
	if (ptr->dwSize < sizeof(type))						RIP_E_INVALIDARG(ptr); \
    if (IsBadReadPtr(ptr, (ptr)->dwSize))               RIP_E_POINTER(ptr); }

#define V_STRUCTPTR_WRITE(ptr,type) \
{   V_ASSERT(offsetof(type, dwSize) == 0); \
    if (IsBadReadPtr(ptr, sizeof(DWORD)))               RIP_E_POINTER(ptr); \
	if (ptr->dwSize < sizeof(type))						RIP_E_INVALIDARG(ptr); \
    if (IsBadWritePtr(ptr, (ptr)->dwSize))              RIP_E_POINTER(ptr); }

#define V_PTR_READ(ptr,type) \
{ if (IsBadReadPtr(ptr, sizeof(type)))                  RIP_E_POINTER(ptr); }

#define V_PTR_WRITE(ptr,type) \
{ if (IsBadWritePtr(ptr, sizeof(type)))                 RIP_E_POINTER(ptr); }

#define V_PTR_WRITE_OPT(ptr,type) \
{ if (ptr) if (IsBadWritePtr(ptr, sizeof(type)))        RIP_E_POINTER(ptr); }

// A buffer pointer with separate length (not defined by the pointer type) we will only
// read from or may write to.
//
// int foo(LPVOID *pBuffer, DWORD cbBuffer)
// ...
// V_BUFPTR_READ(pBuffer, cbBuffer);
// V_BUFPTR_WRITE(pBuffer, cbBuffer);
//
#define V_BUFPTR_READ(ptr,len) \
{   if (IsBadReadPtr(ptr, len))                         RIP_E_POINTER(ptr); }

#define V_BUFPTR_READ_OPT(ptr,len) \
{	if (ptr) V_BUFPTR_READ(ptr,len); }

#define V_BUFPTR_WRITE(ptr,len) \
{   if (IsBadWritePtr(ptr, len))                        RIP_E_POINTER(ptr); }

#define V_BUFPTR_WRITE_OPT(ptr,len) \
{	if (ptr) V_BUFPTR_WRITE(ptr,len); }

// A pointer to a pointer (such as a pointer to an interface pointer) to return
//
// int foo(IReturnMe **ppRet)
// ...
// V_PTRPTR_WRITE(ppRet);
// V_PTRPTR_WRITE_OPT(ppRet);
//
#define V_PTRPTR_WRITE(ptr) \
{   if (IsBadWritePtr(ptr, sizeof(void*)))              RIP_E_POINTER(ptr); }

#define V_PTRPTR_WRITE_OPT(ptr) \
{   if (ptr) if (IsBadWritePtr(ptr, sizeof(void*)))     RIP_E_POINTER(ptr); }

// A pointer to a controlling unknown
//
#define V_PUNKOUTER(punk) \
{   if (punk && IsBadCodePtr(punk))                     RIP_E_POINTER(ptr); }

// A pointer to a controlling unknown for which we don't support aggregation
//
#define V_PUNKOUTER_NOAGG(punk) \
{   if (punk && IsBadReadPtr(punk, sizeof(IUnknown)))   RIP_E_POINTER(ptr); \
    if (punk) return CLASS_E_NOAGGREGATION; }

// Validate an incoming interface pointer. 
//
struct _V_GENERIC_INTERFACE
{
    FARPROC *(__vptr[1]);
};

#define V_INTERFACE(ptr) \
{   if (IsBadReadPtr(ptr, sizeof(_V_GENERIC_INTERFACE)))                              RIP_E_POINTER(ptr); \
    if (IsBadReadPtr(*reinterpret_cast<_V_GENERIC_INTERFACE*>(ptr)->__vptr, sizeof(FARPROC))) \
                                                                                      RIP_E_POINTER(ptr); \
    if (IsBadCodePtr(*(reinterpret_cast<_V_GENERIC_INTERFACE*>(ptr)->__vptr)[0]))     RIP_E_POINTER(ptr); }

#define V_INTERFACE_OPT(ptr) \
{   if (ptr) V_INTERFACE(ptr); }

// Validation for a reference to a GUID, which we only ever read. 
//
#define V_REFGUID(ref) \
{   if (IsBadReadPtr((void*)&ref, sizeof(GUID)))        RIP_E_POINTER((void*)&ref); }

// Validation for a window handle
//
#define V_HWND(h) \
{	if (!IsWindow(h))									RIP_E_HANDLE(h); }	

#define V_HWND_OPT(h) \
{	if (h) if (!IsWindow(h))							RIP_E_HANDLE(h); }	

#endif          // _VALIDATE_H_
