//  Win32Perl.h
//  -----------
//  This header is used exclusively to provide seemless support for Perl extensions
//  under the Win32 platform.  It could easily be adapted to other platforms as well.
//   
//  The bulk of this header is to provide extension development without necesitating 
//  the need to adopt XS or other meta language formats.
//  We prefer to use our own C/C++ coding styles than that which XS dictates.
//  
//  This file adapts to the version of Perl being used: 5.003, 5.004, 5.005, 5.006 with 
//  or without support for PERL_OBJECT.
//  
//  1999.11.14 roth   
//
//  (c) 1998-1999 Dave Roth
//  Courtesy of Roth Consulting
//  http://www.roth.net/

#ifndef _Win32Perl_h_

#include <tchar.h>
#include "patchlevel.h"

//
//  Various macro definitions for various Perl builds:
//  --------------------------------------------------
//      v5.006 Core (default from ActiveState.com)
//          WIN32,_WINDOWS,EMBED,MSWIN32,HAVE_DES_FCRYPT,MULTIPLICITY,PERL_IMPLICIT_CONTEXT,PERL_IMPLICIT_SYS,PERL_MSVCRT_READFIX,PERL_NO_GET_CONTEXT,PERL_POLLUTE,USE_ITHREADS
//          
//      v5.005 ActiveState
//          EMBED,MSWIN32,PERL_OBJECT
//
//      v5.005 Core
//          EMBED,MSWIN32
//
//      v5.004 ActiveState
//          EMBED,MSWIN32,PERL_OBJECT

#   define _Win32Perl_h_
#   define _Win32Perl_h_VERSION     20000608
#   ifdef PERLVER
//#       undef PERLVER
#   endif // PERLVER


// Update REVISION and VERSION values. Some 
#ifndef PERL_VERSION
//  Now test for version 5.005 of perl...
#	if PATCHLEVEL == 5
#       define  PERL_REVISION           5
#       define  PERL_VERSION            5
#   else
#       ifdef PERL_OBJECT
#           define PERL_REVISION        5
#           define PERL_VERSION         4   
#       else
#           define PERL_REVISION        5
#           define PERL_VERSION         3
#       endif // PERL_OBJECT
#   endif // PATCHLEVEL == 5    
#endif // ! PERL_VERSION


#define PERL_OBJECT_INSTANCE_DELIMITER      ,

#if PERL_VERSION == 6
#   define  PERL_VER_STRING                 "5.6"
#   define	FILE_EXTENSION	                "DLL"
//#   ifdef PERL_OBJECT
//  Starting with 5.6 this is really screwy:
//      The pTHX macro is the FULL prototype such as: register PerlInterpreter *my_perl
//      and aTHX macro is the argument such as: my_perl
//  Then under certain screwed up conditions pTHX becomes void. Yes void. Sigh. So
//  this, of course, breaks everything since void itself is not a valid arguement. Idiots.
//  Luckily if this occurs USE_THREADS is defined so we can use this to test...
#   ifndef USE_THREADS
#       define  PERL_OBJECT_INSTANCE        my_perl
#       define  PERL_OBJECT_CLASS           PerlInterpreter*
#       define  PERL_OBJECT_CLASS_STRING    "PerlInterpreter"
#	else
#       define  PERL_OBJECT_CLASS           
#		define  PERL_OBJECT_CLASS_STRING   "pTHX"
#       define  PERL_OBJECT_PROTO           pTHXo_
#       define  PERL_OBJECT_PROTO1          perl_thread *thr
#		define  PERL_OBJECT_ARGS            aTHXo_
#       define  PERL_OBJECT_ARG             aTHXo
#       define  PERL_OBJECT_INSTANCE        aTHX
#   endif
#   pragma message( "...using the " PERL_OBJECT_CLASS_STRING )
#endif

#if PERL_VERSION == 5
#   define  PERL_VER_STRING                 "5.005"
#   define	FILE_EXTENSION	                "DLL"
#   ifdef PERL_OBJECT
#       define  PERL_OBJECT_INSTANCE        pPerl
#       define  PERL_OBJECT_CLASS           CPerlObj*
#       define  PERL_OBJECT_CLASS_STRING    "CPerlObj"
#   else
//      NOTE: Do not define the PERL_CLASS_OBJECT since it is used
//            to determine what class is used for the Perl interpreter.
//            Here we don't use one.
#       define  PERL_OBJECT_PROTO
#       define  PERL_OBJECT_PROTO1
#		define  PERL_OBJECT_ARGS
#       define  PERL_OBJECT_ARG
#   endif // PERL_OBJECT
#endif

#if PERL_VERSION == 4
#   define  PERL_VER_STRING                 "5.004"
#   define  FILE_EXTENSION                  "DLL"
    //  Define our own macros here for v5.004.  This way
    //  we won't get the comma char messed with anything since these macros
    //  must not resolve to anything
#   define  PERL_BRAND                      "Core Distribution"
//      NOTE: Do not define the PERL_CLASS_OBJECT since it is used
//            to determine what class is used for the Perl interpreter.
//            Here we don't use one.
#       define  PERL_OBJECT_PROTO
#       define  PERL_OBJECT_PROTO1
#		define  PERL_OBJECT_ARGS
#       define  PERL_OBJECT_ARG
#endif

#if PERL_VERSION == 3
#   define  PERL_VER_STRING                 "5.003"
#   ifdef PERL_OBJECT
#       define  FILE_EXTENSION              "PLL"
#       define  PERL_OBJECT_INSTANCE        pPerl
#       define  PERL_OBJECT_CLASS           CPerl*
#       define  PERL_OBJECT_CLASS_STRING    "CPerl"
#   else // ! PERL_OBJECT
#       define  FILE_EXTENSION              "DLL"
//      NOTE: Do not define the PERL_CLASS_OBJECT since it is used
//            to determine what class is used for the Perl interpreter.
//            Here we don't use one.
#       define  PERL_OBJECT_PROTO
#       define  PERL_OBJECT_PROTO1
#		define  PERL_OBJECT_ARGS
#       define  PERL_OBJECT_ARG
#   endif // PERL_OBJECT
#endif // SUB_VERSION


//  Now we test for PERL_OBJECT again to create the object macros...

#ifdef PERL_OBJECT
#   define  PERL_BRAND          "ActiveState"
#else // PERL_OBJECT
#   define  PERL_BRAND          "Core Distribution"
#endif // PERL_OBJECT

#ifndef PERL_OBJECT_PROTO
#   define  PERL_OBJECT_PROTO   PERL_OBJECT_CLASS PERL_OBJECT_INSTANCE PERL_OBJECT_INSTANCE_DELIMITER
#   define  PERL_OBJECT_PROTO1  PERL_OBJECT_CLASS PERL_OBJECT_INSTANCE
#   define  PERL_OBJECT_ARGS    PERL_OBJECT_INSTANCE PERL_OBJECT_INSTANCE_DELIMITER
#   define  PERL_OBJECT_ARG     PERL_OBJECT_INSTANCE
#endif

#define PERLVER  "v" PERL_VER_STRING " (" PERL_BRAND ") Win32 Perl"

#ifdef H_PERL    //  Do this only if Perl.h was called
#   pragma message ( "  * Using " PERLVER )
#   ifdef PERL_OBJECT_CLASS
#       pragma message( "    - Perl Class: " PERL_OBJECT_CLASS_STRING )
#   endif   //  PERL_OBJECT_CLASS
#   pragma message( "\n  ================================================================================\n\n" )
#endif // H_PERL

//  "na" is a constant used with SvPV()
//  This macro has been removed in v5.6
#ifndef na
#   define na			PL_na
#endif // 


    ///////////////////////////////////////////////////////////////////////////////
    //  Declare our standard extension macros for easy Perl extension coding... 
    //
    //  To use these macros it is best to declare the EXTENSION_VARS macro
    //  somewhere in the beginning of the Perl function.
    //  For example:
    //  XS( XS_DecodeBuffer )
    //  {
	//      dXSARGS;            //  Standard Perl extension delcaration
    //      EXTENSION_VARS;     //  Win32Perl.h declaration
	//      
	//      ...process code where you may be pushing values onto the retyurn stack...
    //      PUSH_IV( 32 );
    //      PUSH_PV( "Hello" );
    //         
    //      EXTENSION_RETURN;   //  Win32Perl.h return declaration
    //  }
	  
	// Number of elements we can push onto the return stack
	// before having to extend the stack.
    #define DEFAULT_PERL_STACK_SIZE		5		

    //  Set up the default extension variables what we need...
    #define	EXTENSION_VARS		int    iNumOfReturnStackElements = 0;			\
							    int    iStackCount = DEFAULT_PERL_STACK_SIZE;	

	//	Routine to use every time we push a value onto the return stack.  This will monitor
	//	the stack's size and extend it every time it needs to be extended.
    #define CHECK_PERL_STACK_SIZE	if( 0 <= iStackCount )					    \
								    {											\
									    iStackCount = DEFAULT_PERL_STACK_SIZE;	\
									    EXTEND( sp, iStackCount );				\
								    }

    /////////////////////////////////////////////////////////////
    //  Return Stack Macros
    //
    //  Pop an SV off of the stack and update the return stack. This is called when we have
    //  accidently pushed a value onto the return stack...
    //  
    //  The following macros are defined to push elements onto the return stack:
    //      POP_SV............Pop the top SV off the stack. The SV is lost (not returned)
    //
    //  These macros all push their respective values onto the stack. Each macro will
    //  create a new SV and tag it as mortal before pushing onto the stack.
    //  
    //      PUSH_IV(x)........Push a 32 bit value onto the return stack.
    //      PUSH_NV(x)........Push a floating point value (a double) onto the stack
    //      PUSH_PV(x)........Push a nul terminated string onto the stack.
    //      PUSH_PNV(x,y).....Push a binary object onto the return stack. X=LPBYTE; Y=Length in bytes.
    //      PUSH_NOREF(x).....Push the specified SV* onto the stack as is.
    //      PUSH_REF(x).......Create a reference to (x), tag it as mortal then push it onto the return stack.
    //      PUSH_AV(x)........Push an array onto the return stack.
    //      PUSH_HV(x)........Push a hash onto the return stack.


    #define POP_SV              POPs;                                           \
                                iNumOfReturnStackElements--
                                
    //  Push an IV value onto the return stack...
    #define PUSH_IV(x)          CHECK_PERL_STACK_SIZE;                                                      \
                                ST( iNumOfReturnStackElements ) = sv_2mortal( newSViv( (IV) (x) ) );        \
                                iNumOfReturnStackElements++

    //  Push an NV (double) value onto the return stack...
    #define PUSH_NV(x)          CHECK_PERL_STACK_SIZE;                                                      \
                                ST( iNumOfReturnStackElements ) = sv_2mortal( newSVnv( (double) (x) ) );         \
                                iNumOfReturnStackElements++
    
    //  Push a string value onto the stack...
    #define PUSH_PV(x)          if( NULL != (x) )                                                               \
                                {                                                                               \
                                    CHECK_PERL_STACK_SIZE;                                                      \
                                    ST( iNumOfReturnStackElements ) = sv_2mortal( newSVpv( (LPTSTR)(x), 0 ) );   \
                                    iNumOfReturnStackElements++;                                                \
                                }

    //  Push a string value onto the stack...
    #define PUSH_PNV(x,y)       if( NULL != (x) )                                                                       \
                                {                                                                                       \
                                    CHECK_PERL_STACK_SIZE;                                                              \
                                    ST( iNumOfReturnStackElements ) = sv_2mortal( newSVpv( (LPTSTR)(x), (int) (y) ) );   \
                                    iNumOfReturnStackElements++;                                                        \
                                }


    //  Push an SV value onto the stack...
    #define PUSH_NOREF(x)       if( NULL != (x) )                                                           \
                                {                                                                           \
                                    CHECK_PERL_STACK_SIZE;                                                  \
                                    ST( iNumOfReturnStackElements ) = (SV*)(x);                             \
                                    iNumOfReturnStackElements++;                                            \
                                }

    //  All PUSH_SV() macros need to reference PUSH_REF() or PUSH_NOREF() instead.
    //  #define PUSH_SV(x)          PUSH_REF(x)

    //  Create and push a new reference onto the stack...
    #define PUSH_REF(x)         if( NULL != (x) )                                                           \
                                {                                                                           \
                                    CHECK_PERL_STACK_SIZE;                                                  \
                                    ST( iNumOfReturnStackElements ) = sv_2mortal( newRV( (SV*)(x) ) );      \
                                    iNumOfReturnStackElements++;                                            \
                                }
    
    //  Push an array onto the stack...
    #define PUSH_AV(x)          PUSH_REF(x)
    
    //  Push a hash onto the stack...
    #define PUSH_HV(x)          PUSH_REF(x)


    // Return a boolean yes or no...
    #define XSRETURN_BOOL(x)    ST( 0 ) = sv_2mortal( newSViv( (FALSE != (x))? 1 : 0 ) );       \
                                XSRETURN( 1 )            
    
    #define EXTENSION_RETURN_BOOL(x)    XSRETURN_BOOL( (x) )

    // Return with the return stack...
    #define EXTENSION_RETURN    XSRETURN( iNumOfReturnStackElements )

    /////////////////////////////////////////////////////////////
    //  HASH Macros

    //  The hash retrieval macros. These all have a prototype of: HASH_GET_xx( pHash, szKeyName )
    #define HASH_GET_SV(x,y)    HashGetSV( PERL_OBJECT_ARGS (x), (y) )
    #define HASH_GET_PV(x,y)    HashGetPV( PERL_OBJECT_ARGS (x), (y) )
    #define HASH_GET_IV(x,y)    HashGetIV( PERL_OBJECT_ARGS (x), (y) )
    #define HASH_GET_NV(x,y)    HashGetNV( PERL_OBJECT_ARGS (x), (y) )
    #define HASH_GET_AV(x,y)    EXTRACT_AV( HashGetSV( PERL_OBJECT_ARGS (x), (y) ) )
    #define HASH_GET_HV(x,y)    EXTRACT_HV( HashGetSV( PERL_OBJECT_ARGS (x), (y) ) )
	
    //	Extract a hash reference from an SV: SV *pSv = ST( 0 );
    //                                       HV *pHv = EXTRACT_HV( pSv );
    #define EXTRACT_HV(x)       _EXTRACT_HV( (SV*) (x) )

    inline HV *_EXTRACT_HV( SV *pSv )
    {
        HV *pHv = NULL;
        if( NULL == pSv )
        {
            return( NULL );
        }

        if( SvROK( pSv ) )
	    {
            pSv = SvRV( pSv );
        }
	    if( SVt_PVHV == SvTYPE( pSv ) )
	    {
	        pHv = (HV*) pSv;
	    }
        return( pHv );
    }

    //  Delete a key from a hash: HASH_DELETE( pHash, szKeyName )
    #define HASH_DELETE(x,y)            if( hv_exists( (HV*) (x), (LPTSTR)(y), _tcslen( (LPTSTR)(y) ) ) )                \
                                        {                                                                               \
		                                    hv_delete( (HV*) (x), (LPTSTR)(y), _tcslen( (LPTSTR)(y) ), G_DISCARD );      \
	                                    }

    // Store a hash (HV*) or array (AV*) into a hash. This will create a reference then store that
    // into the hash. 
    // --------------
    // The storing of an AV* or HV* is special. We need to create a reference WITHOUT increasing the
    // references reference count. Silly but this is what happens:
    //      a) Reference is made creating a ref count of 1 and increasing the AV or HV's ref count
    //      b) Reference is added to a hash increasing it's ref count to 2
    //  When the array is undefed the reference ref count is decremented, of course.
    //  so it now becomes 1.  Since it is not zero it is not purged however nothing points to it so
    //  it has become an orphan hence a memory leak.
    #define HASH_STORE_AV(x,y,z)        HASH_STORE_HV(x,y,z)
    #define HASH_STORE_HV(x,y,z)        if( ( NULL != (x) ) && ( NULL != (y) ) && ( NULL != (z) ) )                         \
                                        {                                                                                   \
                                            SV* P_SV_TEMP = newRV_noinc( (SV*) (z) );                                       \
                                            if( NULL != P_SV_TEMP )                                                         \
                                            {                                                                               \
                                                hv_store( (HV*) (x), (LPTSTR) (y), _tcslen( (LPTSTR) (y) ), P_SV_TEMP, 0 ); \
                                            }                                                                               \
                                        }


    //  Store an SV into a hash: HASH_STORE_SV( pHash, szKeyName, pSv );
    //  This will auto create a reference to the SV and store that reference in the hash.
    #define HASH_STORE_SV(x,y,z)        if( ( NULL != (x) ) && ( NULL != (y) ) && ( NULL != (z) ) )                         \
                                        {                                                                                   \
                                            SV* P_SV_TEMP = newRV( (SV*) (z) );                                             \
                                            if( NULL != P_SV_TEMP )                                                         \
                                            {                                                                               \
                                                hv_store( (HV*) (x), (LPTSTR) (y), _tcslen( (LPTSTR) (y) ), P_SV_TEMP, 0 );    \
                                            }                                                                               \
                                        }

    //  Store an SV into a hash without any references: HASH_STORE_SVNOREF( pHash, szKeyName, pSv );
    //  Typically you don't do this unless you already have a reference you need to store in the hash.
    //  You would normally call HASH_STORE_SV() which auto creates the reference for you.
    #define HASH_STORE_SVNOREF(x,y,z)   if( ( NULL != (x) ) && ( NULL != (y) ) && ( NULL != (z) ) )                         \
                                        {                                                                                   \
                                            hv_store( (HV*) (x), (LPTSTR) (y), _tcslen( (LPTSTR) (y) ), (SV*)(z), 0 );      \
                                        }

    //  Store a data array into a hash (storing a string but specify the number of elements hence it can
    //  contain nul chars: HASH_STORE_PNV( pHv, szKeyName, pData, dwDataBufferSize )
    #define HASH_STORE_PNV(x,y,z,size)  if( ( NULL != (x) ) && ( NULL != (y) ) && ( NULL != (z) ) )                         \
                                        {                                                                                   \
                                            SV* P_SV_TEMP = newSVpv( (LPTSTR)(z), (int)(size) );                             \
                                            if( NULL != P_SV_TEMP )                                                         \
                                            {                                                                               \
                                                hv_store( (HV*) (x), (LPTSTR) (y), _tcslen( (LPTSTR) (y) ), P_SV_TEMP, 0 );    \
                                            }                                                                               \
                                        } 
    //  Store a C string into a hash: HASH_STORE_PV( pHash, szKeyName, szString )
    #define HASH_STORE_PV(x,y,z)        HASH_STORE_PNV(x,y,z, _tcslen( (LPTSTR)(z) ) )

    //  Store a floating point number into a hash: HASH_STORE_NV( pHash, szKeyName, dFloatingPoint )
    #define HASH_STORE_NV(x,y)          if( ( NULL != (x) ) && ( NULL != (y) ) )                                            \
                                        {                                                                                   \
                                            SV* P_SV_TEMP = newSVnv( (NV)(z) );                                             \
                                            if( NULL != P_SV_TEMP )                                                         \
                                            {                                                                               \
                                                hv_store( (HV*) (x), (LPTSTR) (y), _tcslen( (LPTSTR) (y) ), P_SV_TEMP, 0 );    \
                                            }                                                                               \
                                        } 

    //  Store a 32 bit integer into a hash: HASH_STORE_IV( pHash, szKeyName, dwNumber )
    #define HASH_STORE_IV(x,y,z)        if( ( NULL != (x) ) && ( NULL != (y) ) )                                            \
                                        {                                                                                   \
                                            SV* P_SV_TEMP = newSViv( (IV)(z) );                                             \
                                            if( NULL != P_SV_TEMP )                                                         \
                                            {                                                                               \
                                                hv_store( (HV*) (x), (LPTSTR) (y), _tcslen( (LPTSTR) (y) ), P_SV_TEMP, 0 );    \
                                            }                                                                               \
                                        } 
    
    // Check that a hash key exists: HASH_KEY_EXISTS( pHash, szKeyName )
    #define HASH_KEY_EXISTS(x,y)        ( 0 != hv_exists( (HV*)(x), (LPTSTR)(y), _tcslen( (LPTSTR)(y) ) ) )

    //  Define the inline hash extraction prototypes...
    char *HashGetPV( PERL_OBJECT_PROTO HV *pHv, LPTSTR pszKeyName );
    IV HashGetIV( PERL_OBJECT_PROTO HV *pHv, LPTSTR pszKeyName );
    double HashGetNV( PERL_OBJECT_PROTO HV *pHv, LPTSTR pszKeyName );
    SV *HashGetSV( PERL_OBJECT_PROTO HV *pHv, LPTSTR pszKeyName );

    //  Now define the inline functions used by the hash macros
    inline LPTSTR HashGetPV( PERL_OBJECT_PROTO HV *pHv, LPTSTR pszKeyName )
    {
        SV *pSv = HashGetSV( PERL_OBJECT_ARGS pHv, pszKeyName );
        if( NULL != pSv )
        {
            return( SvPV( pSv, na ) );
        }
        else
        {
            return( "" );
        }
    }

    inline IV HashGetIV( PERL_OBJECT_PROTO HV *pHv, LPTSTR pszKeyName )
    {
        SV *pSv = HashGetSV( PERL_OBJECT_ARGS pHv, pszKeyName );
        if( NULL != pSv )
        {
            return( SvIV( pSv) );
        }
        else
        {
            return( 0 );
        }
    }

    inline double HashGetNV( PERL_OBJECT_PROTO HV *pHv, LPTSTR pszKeyName )
    {
        SV *pSv = HashGetSV( PERL_OBJECT_ARGS pHv, pszKeyName );
        if( NULL != pSv )
        {
            return( SvNV( pSv) );
        }
        else
        {
            return( 0.0 );
        }
    }

    inline SV * HashGetSV( PERL_OBJECT_PROTO HV *pHv, LPTSTR pszKeyName )
    {
        SV *pSv = NULL;
        if( ( NULL == pszKeyName ) || ( NULL == pHv ) )
            return( NULL );

        if( hv_exists( pHv, pszKeyName, _tcslen( pszKeyName ) ) )
        {
            pSv = (SV*) hv_fetch( pHv, pszKeyName, _tcslen( pszKeyName ), 0 );
            if( NULL != pSv )
            {
                pSv = *(SV**) pSv;
            }
        }
        return( pSv );
    }
   

    /////////////////////////////////////////////////////////////
    //  ARRAY Macros
    //
    //  Push a given type into an array. The all follow the format:
    //      ARRAY_PUSH_xx( pAv, value )
    //  eg: ARRAY_PUSH_NV( pAv, 3.14 );

    #define ARRAY_PUSH_PV(x,y)          av_push( (AV*) (x), newSVpv( (LPTSTR) (y), 0 ) )
    #define ARRAY_PUSH_PNV(x,y,z)       av_push( (AV*) (x), newSVpv( (LPTSTR) (y), (int) (z) ) )
    #define ARRAY_PUSH_IV(x,y)          av_push( (AV*) (x), newSViv( (IV) (y) ) )
    #define ARRAY_PUSH_NV(x,y)          av_push( (AV*) (x), newSVnv( (NV) (y) ) )
    #define ARRAY_PUSH_SV(x,y)          av_push( (AV*) (x), newSVsv( (SV*)(y) ) )
    #define ARRAY_PUSH_RV(x,y)          av_push( (AV*) (x), newRV( (SV*)(y) ) )
    #define ARRAY_PUSH(x,y)             av_push( (AV*) (x), (SV*) (y) )
    // The pushing of AV* and HV* is special. We need to create a reference WITHOUT increasing the
    // references reference count. Silly but this is what happens:
    //      a) Reference is made creating a ref count of 1 and increasing the AV or HV's ref count
    //      b) Reference is added to an array increasing it's ref count to 2
    //  When the array is undefed the reference ref count is decremented, of course.
    //  so it now becomes 1.  Since it is not zero it is not purged however nothing points to it so
    //  it has become an orphan hence a memory leak.
    #define ARRAY_PUSH_AV(x,y)          av_push( (AV*) (x), newRV_noinc( (SV*)(y) ) )
    #define ARRAY_PUSH_HV(x,y)          ARRAY_PUSH_AV(x,y)


    //  Get a particular value from a specified index in an array.  Format is:
    //      ARRAY_GET_xx( pAv, dwIndex )
    //  eg: (char*) pszString = ARRAY_GET_PV( pAv, 18 );
    //  One exception is the ARRAY_GET_PVN( pAv, dwIndex, dwLength )
    //  This will return a string of dwLength bytes long ignoring any embedded nul chars.
    #define ARRAY_GET(x,y)          (SV*) _ARRAY_FETCH( PERL_OBJECT_ARGS (AV*)(x), (I32)(y) )
    #define ARRAY_GET_SV(x,y)       (SV*) _ARRAY_FETCH( PERL_OBJECT_ARGS (AV*)(x), (I32)(y) )
    #define ARRAY_GET_PV(x,y)       SvPV( _ARRAY_FETCH( PERL_OBJECT_ARGS (AV*)(x), (I32)(y) ), na )
    #define ARRAY_GET_PVN(x,y,z)    SvPV( _ARRAY_FETCH( PERL_OBJECT_ARGS (AV*)(x), (I32)(y) ), (I32)(z) )
    #define ARRAY_GET_IV(x,y)       SvIV( _ARRAY_FETCH( PERL_OBJECT_ARGS (AV*)(x), (I32)(y) ) )
    #define ARRAY_GET_NV(x,y)       SvNV( _ARRAY_FETCH( PERL_OBJECT_ARGS (AV*)(x), (I32)(y) ) )
    #define ARRAY_GET_AV(x,y)       EXTRACT_AV( ARRAY_GET_SV( (x), (y) ) )
    #define ARRAY_GET_HV(x,y)       EXTRACT_HV( ARRAY_GET_SV( (x), (y) ) )

    ////////////////////////////////////////////////////////////////////////////
    //  Extract AV from an SV:  SV *pSv = ST( 0 );
    //                          AV *pAv = EXTRACT_AV( pSv );
    #define EXTRACT_AV(x)           _EXTRACT_AV( (SV*) (x) )    
    inline AV *_EXTRACT_AV( SV *pSv  )
    {
        AV *pAv = NULL;
        if( NULL == pSv )
        {
            return( NULL );
        }

        if( SvROK( pSv ) )
	    {
            pSv = SvRV( pSv );
        }
	    if( SVt_PVAV == SvTYPE( pSv ) )
	    {
	        pAv = (AV*) pSv;
	    }
        return( pAv );
    }

    ////////////////////////////////////////////////////////////////////////////
    // Extract an SV* from an array 
    //
    inline SV* _ARRAY_FETCH( PERL_OBJECT_PROTO AV *pAv, I32 Index )
    {
        SV *pSv = NULL;
        if( NULL != pAv )
        {
            SV **ppSvTemp = av_fetch( pAv, Index, 0 );
            if( NULL != ppSvTemp )
            {
                pSv = ppSvTemp[ 0 ];
            }
        }
        return( pSv );
    }

#endif  //  _Win32Perl_h_



