/******************************************************************************
 *
 * Project:  Virtual GDAL Datasets
 * Purpose:  Implementation of a sourced raster band that derives its raster
 *           by applying an algorithm (GDALDerivedPixelFunc) to the sources.
 * Author:   Pete Nagy
 *
 ******************************************************************************
 * Copyright (c) 2005 Vexcel Corp.
 * Copyright (c) 2008-2011, Even Rouault <even dot rouault at mines-paris dot org>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *****************************************************************************/

#include "cpl_minixml.h"
#include "cpl_string.h"
#include "vrtdataset.h"
#include "cpl_multiproc.h"
#include "cpl_spawn.h"

#if !defined(WIN32)
  #include <sys/stat.h>
  #include <sys/types.h>
  #ifdef HAVE_UNISTD_H
    #include <unistd.h>
  #endif
#endif

#include <algorithm>
#include <map>
#include <vector>
#include <utility>

/*! @cond Doxygen_Suppress */

CPL_CVSID("$Id: vrtderivedrasterband.cpp 38039 2017-04-16 13:08:39Z rouault $");

// #define GDAL_VRT_DISABLE_PYTHON
// #define PYTHONSO_DEFAULT "libpython2.7.so"

#ifndef GDAL_VRT_ENABLE_PYTHON_DEFAULT
// Can be YES, NO or TRUSTED_MODULES
#define GDAL_VRT_ENABLE_PYTHON_DEFAULT "TRUSTED_MODULES"
#endif

static std::map<CPLString, GDALDerivedPixelFunc> osMapPixelFunction;
static bool gbHasInitializedPython = false;
static int gnPythonInstanceCounter = 0;
static CPLMutex* ghMutex = NULL;

// Subset of Python API defined as function of pointers
typedef struct PyObject_t PyObject;
#define Py_file_input 257
static void (*Py_SetProgramName)(const char*) = NULL;
static PyObject* (*PyBuffer_FromReadWriteMemory)(void*, size_t) = NULL;
static PyObject* (*PyTuple_New)(size_t) = NULL;
static PyObject* (*PyInt_FromLong)(long) = NULL;
static PyObject* (*PyFloat_FromDouble)(double) = NULL;
static PyObject* (*PyObject_Call)(PyObject*, PyObject*, PyObject*) = NULL;
static void (*Py_IncRef)(PyObject*) = NULL;
static void (*Py_DecRef)(PyObject*) = NULL;
static PyObject* (*PyErr_Occurred)(void) = NULL;
static void (*PyErr_Print)(void) = NULL;
static int (*Py_IsInitialized)(void) = NULL;
static void (*Py_InitializeEx)(int) = NULL;
static void (*PyEval_InitThreads)(void) = NULL;
typedef struct PyThreadState_t PyThreadState;
static PyThreadState* (*PyEval_SaveThread)(void) = NULL;
static void (*PyEval_RestoreThread)(PyThreadState*) = NULL;
static void (*Py_Finalize)(void) = NULL;
static PyObject* (*Py_CompileString)(const char*, const char*, int) = NULL;
static PyObject* (*PyImport_ExecCodeModule)(const char*, PyObject*) = NULL;
static PyObject* (*PyObject_GetAttrString)(PyObject*, const char*) = NULL;
static int (*PyTuple_SetItem)(PyObject *, size_t, PyObject *) = NULL;
static void (*PyObject_Print)(PyObject*,FILE*,int) = NULL;
static PyObject* (*PyString_FromStringAndSize)(const void*, size_t) = NULL;
static PyObject* (*PyImport_ImportModule)(const char*) = NULL;
static int (*PyCallable_Check)(PyObject*) = NULL;
static PyObject* (*PyDict_New)(void) = NULL;
static int (*PyDict_SetItemString)(PyObject *p, const char *key,
                                   PyObject *val) = NULL;
static void (*PyErr_Fetch)(PyObject **poPyType, PyObject **poPyValue,
                           PyObject **poPyTraceback) = NULL;
static void (*PyErr_Clear)(void) = NULL;
static const char* (*PyString_AsString)(PyObject*) = NULL;
static const char* (*Py_GetVersion)(void) = NULL;

typedef int PyGILState_STATE;
static PyGILState_STATE (*PyGILState_Ensure)(void) = NULL;
static void (*PyGILState_Release)(PyGILState_STATE) = NULL;

/* Flags for getting buffers */
#define PyBUF_WRITABLE 0x0001
#define PyBUF_FORMAT 0x0004
#define PyBUF_ND 0x0008
#define PyBUF_STRIDES (0x0010 | PyBUF_ND)
#define PyBUF_INDIRECT (0x0100 | PyBUF_STRIDES)
#define PyBUF_FULL (PyBUF_INDIRECT | PyBUF_WRITABLE | PyBUF_FORMAT)

typedef struct
{
    //cppcheck-suppress unusedStructMember
    char big_enough[256];
} Py_buffer;
static int (*PyBuffer_FillInfo)(Py_buffer *view, PyObject *obj, void *buf,
                                size_t len, int readonly, int infoflags) = NULL;
static PyObject* (*PyMemoryView_FromBuffer)(Py_buffer *view) = NULL;

static PyThreadState* gphThreadState = NULL;

/************************************************************************/
/*                        GDALCreateNumpyArray()                        */
/************************************************************************/

static PyObject* GDALCreateNumpyArray(PyObject* pCreateArray,
                                      void* pBuffer,
                                      GDALDataType eType,
                                      int nHeight,
                                      int nWidth )
{
    PyObject* poPyBuffer;
    const size_t nSize = static_cast<size_t>(nHeight) * nWidth *
                                    GDALGetDataTypeSizeBytes(eType);
    if( PyBuffer_FromReadWriteMemory )
    {
        // Python 2
        poPyBuffer = PyBuffer_FromReadWriteMemory(pBuffer, nSize);
    }
    else
    {
        // Python 3
        Py_buffer pybuffer;
        if( PyBuffer_FillInfo(&pybuffer, NULL, (char*)pBuffer,
                              nSize,
                              0, PyBUF_FULL) != 0)
        {
            return NULL;
        }
        poPyBuffer = PyMemoryView_FromBuffer(&pybuffer);
    }
    PyObject* pArgsCreateArray = PyTuple_New(4);
    PyTuple_SetItem(pArgsCreateArray, 0, poPyBuffer);
    const char* pszDataType = NULL;
    switch( eType )
    {
        case GDT_Byte: pszDataType = "uint8"; break;
        case GDT_UInt16: pszDataType = "uint16"; break;
        case GDT_Int16: pszDataType = "int16"; break;
        case GDT_UInt32: pszDataType = "uint32"; break;
        case GDT_Int32: pszDataType = "int32"; break;
        case GDT_Float32: pszDataType = "float32"; break;
        case GDT_Float64: pszDataType = "float64"; break;
        case GDT_CInt16:
        case GDT_CInt32:
            CPLAssert(FALSE);
            break;
        case GDT_CFloat32: pszDataType = "complex64"; break;
        case GDT_CFloat64: pszDataType = "complex128"; break;
        default:
            CPLAssert(FALSE);
            break;
    }
    PyTuple_SetItem(pArgsCreateArray, 1,
                PyString_FromStringAndSize(pszDataType, strlen(pszDataType)));
    PyTuple_SetItem(pArgsCreateArray, 2, PyInt_FromLong(nHeight));
    PyTuple_SetItem(pArgsCreateArray, 3, PyInt_FromLong(nWidth));
    PyObject* poNumpyArray = PyObject_Call(pCreateArray, pArgsCreateArray, NULL);
    Py_DecRef(pArgsCreateArray);
    if (PyErr_Occurred())
        PyErr_Print();
    return poNumpyArray;
}

/* MinGW32 might define HAVE_DLFCN_H, so skip the unix implementation */
#if defined(HAVE_DLFCN_H) && !defined(WIN32)

#include <dlfcn.h>

typedef void* LibraryHandle;

#define LOAD_NOCHECK_WITH_NAME(libHandle, x, name) \
    do { \
            void* ptr = dlsym(libHandle, name); \
            memcpy(&x, &ptr, sizeof(void*)); \
    } while(0)

#elif defined(WIN32)

#include <windows.h>
#include <psapi.h>

typedef HMODULE LibraryHandle;

#define LOAD_NOCHECK_WITH_NAME(libHandle, x, name) \
    do { \
            FARPROC ptr = GetProcAddress(libHandle, name); \
            memcpy(&x, &ptr, sizeof(void*)); \
    } while(0)

#endif

#define STRINGIFY(x) #x

#define LOAD_NOCHECK(libHandle, x) LOAD_NOCHECK_WITH_NAME(libHandle, x, STRINGIFY(x))
#define LOAD_WITH_NAME(libHandle, x, name) \
    do { \
            LOAD_NOCHECK_WITH_NAME(libHandle, x, name); \
            if (!x) \
            { \
                CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s", name); \
                return false; \
            } \
    } while(0)
#define LOAD(libHandle, x) LOAD_WITH_NAME(libHandle, x, STRINGIFY(x))

/************************************************************************/
/*                          LoadPythonAPI()                             */
/************************************************************************/

#if defined(LOAD_NOCHECK_WITH_NAME) && defined(HAVE_DLFCN_H) && !defined(WIN32)
static LibraryHandle libHandleStatic = NULL;
#endif

/** Load the subset of the Python C API that we need */
static bool LoadPythonAPI()
{
    CPLMutexHolder oHolder(&ghMutex);

    static bool bInit = false;
    if( bInit )
        return true;

#ifdef LOAD_NOCHECK_WITH_NAME
    // The static here is just to avoid Coverity warning about resource leak.
    LibraryHandle libHandle = NULL;

    const char* pszPythonSO = CPLGetConfigOption("PYTHONSO", NULL);
#if defined(HAVE_DLFCN_H) && !defined(WIN32)

    // First try in the current process in case the python symbols would
    // be already loaded
    libHandle = dlopen(NULL, RTLD_LAZY);
    libHandleStatic = libHandle;
    if( libHandle != NULL &&
        dlsym(libHandle, "Py_SetProgramName") != NULL )
    {
        CPLDebug("VRT", "Current process has python symbols loaded");
    }
    else
    {
        libHandle = NULL;
    }

    // Then try the user provided shared object name
    if( libHandle == NULL && pszPythonSO != NULL )
    {
        // coverity[tainted_string]
        libHandle = dlopen(pszPythonSO, RTLD_NOW | RTLD_GLOBAL);
        if( libHandle == NULL )
        {
            CPLError(CE_Failure, CPLE_AppDefined,
                     "Cannot load %s",
                     pszPythonSO);
            return false;
        }
        if( dlsym(libHandle, "Py_SetProgramName") == NULL )
        {
            CPLError(CE_Failure, CPLE_AppDefined,
                     "Cannot find Py_SetProgramName symbol in %s",
                     pszPythonSO);
            return false;
        }
    }

    // Then try the PYTHONSO_DEFAULT if defined at compile time
#ifdef PYTHONSO_DEFAULT
    if( libHandle == NULL )
    {
        libHandle = dlopen(PYTHONSO_DEFAULT, RTLD_NOW | RTLD_GLOBAL);
        if( !libHandle )
        {
            CPLDebug("VRT", "%s found", PYTHONSO_DEFAULT);
        }
    }
#endif

    // Then try to find the libpython that corresponds to the python binary
    // in the PATH
    if( libHandle == NULL )
    {
#if defined(__MACH__) && defined(__APPLE__)
#define SO_EXT "dylib"
#else
#define SO_EXT "so"
#endif
        CPLString osVersion;
        char* pszPath = getenv("PATH");
        if( pszPath != NULL
#ifdef DEBUG
           // For testing purposes
           && CPLTestBool( CPLGetConfigOption(
                                    "VRT_ENABLE_PYTHON_PATH", "YES") )
#endif
          )
        {
            char** papszTokens = CSLTokenizeString2(pszPath, ":", 0);
            for( int iTry = 0; iTry < 2; ++iTry )
            {
                for( char** papszIter = papszTokens;
                        papszIter != NULL && *papszIter != NULL;
                        ++papszIter )
                {
                    struct stat sStat;
                    CPLString osPythonBinary(
                        CPLFormFilename(*papszIter, "python", NULL));
                    if( iTry == 1 )
                        osPythonBinary += "3";
                    if( lstat(osPythonBinary, &sStat) != 0 )
                        continue;

                    CPLDebug("VRT", "Found %s", osPythonBinary.c_str());

                    if( S_ISLNK(sStat.st_mode)
#ifdef DEBUG
                        // For testing purposes
                        && CPLTestBool( CPLGetConfigOption(
                                    "VRT_ENABLE_PYTHON_SYMLINK", "YES") )
#endif
                        )
                    {
                        // If this is a symlink, hopefully the resolved
                        // name will be like "python2.7"
                        const int nBufSize = 2048;
                        std::vector<char> oFilename(nBufSize);
                        char *szPointerFilename = &oFilename[0];
                        int nBytes = static_cast<int>(
                            readlink( osPythonBinary, szPointerFilename,
                                      nBufSize ) );
                        if (nBytes != -1)
                        {
                            szPointerFilename[std::min(nBytes,
                                                       nBufSize - 1)] = 0;
                            CPLString osFilename(
                                            CPLGetFilename(szPointerFilename));
                            CPLDebug("VRT", "Which is an alias to: %s",
                                     szPointerFilename);
                            if( STARTS_WITH(osFilename, "python") )
                            {
                                osVersion = osFilename.substr(strlen("python"));
                                CPLDebug("VRT",
                                         "Python version from binary name: %s",
                                         osVersion.c_str());
                            }
                        }
                        else
                        {
                            CPLDebug("VRT", "realink(%s) failed",
                                        osPythonBinary.c_str());
                        }
                    }

                    // Otherwise, expensive way: start the binary and ask
                    // it for its version...
                    if( osVersion.empty() )
                    {
                        const char* pszPrintVersion =
                            "import sys; print(str(sys.version_info[0]) +"
                            "'.' + str(sys.version_info[1]))";
                        const char* const apszArgv[] = {
                                osPythonBinary.c_str(), "-c",
                                pszPrintVersion,
                                NULL };
                        const CPLString osTmpFilename(
                                        "/vsimem/LoadPythonAPI/out.txt");
                        VSILFILE* fout = VSIFOpenL( osTmpFilename, "wb+");
                        if( CPLSpawn( apszArgv, NULL, fout, FALSE ) == 0 )
                        {
                            char* pszStr = reinterpret_cast<char*>(
                                VSIGetMemFileBuffer( osTmpFilename,
                                                        NULL, FALSE ));
                            osVersion = pszStr;
                            if( !osVersion.empty() &&
                                osVersion.back() == '\n' )
                            {
                                osVersion.resize(osVersion.size() - 1);
                            }
                            CPLDebug("VRT", "Python version from binary: %s",
                                        osVersion.c_str());
                        }
                        VSIFCloseL(fout);
                        VSIUnlink(osTmpFilename);
                    }
                    break;
                }
                if( !osVersion.empty() )
                    break;
            }
            CSLDestroy(papszTokens);
        }

        if( !osVersion.empty() )
        {
            CPLString osPythonSO("libpython");
            osPythonSO += osVersion + "." SO_EXT;
            CPLDebug("VRT", "Trying %s", osPythonSO.c_str());
            libHandle = dlopen(osPythonSO, RTLD_NOW | RTLD_GLOBAL);
            if( libHandle != NULL )
            {
                CPLDebug("VRT", "... success");
            }
            else if( osVersion[0] == '3' )
            {
                osPythonSO = "libpython" + osVersion + "m." SO_EXT;
                CPLDebug("VRT", "Trying %s", osPythonSO.c_str());
                libHandle = dlopen(osPythonSO, RTLD_NOW | RTLD_GLOBAL);
                if( libHandle != NULL )
                {
                    CPLDebug("VRT", "... success");
                }
            }
        }
    }

    // Otherwise probe a few known objects.
    // Note: update vrt_tutorial.dox if change
    if( libHandle == NULL )
    {
        const char* const apszPythonSO[] = { "libpython2.7." SO_EXT,
                                                "libpython2.6." SO_EXT,
                                                "libpython3.4m." SO_EXT,
                                                "libpython3.5m." SO_EXT,
                                                "libpython3.6m." SO_EXT,
                                                "libpython3.3." SO_EXT,
                                                "libpython3.2." SO_EXT };
        for( size_t i = 0; libHandle == NULL &&
                            i < CPL_ARRAYSIZE(apszPythonSO); ++i )
        {
            CPLDebug("VRT", "Trying %s", apszPythonSO[i]);
            libHandle = dlopen(apszPythonSO[i], RTLD_NOW | RTLD_GLOBAL);
            if( libHandle != NULL )
                CPLDebug("VRT", "... success");
        }
    }

#elif defined(WIN32)

    // First try in the current process in case the python symbols would
    // be already loaded
    HANDLE hProcess = GetCurrentProcess();
    HMODULE ahModules[100];
    DWORD nSizeNeeded = 0;

    EnumProcessModules(hProcess, ahModules, sizeof(ahModules),
                        &nSizeNeeded);

    const size_t nModules =
        std::min(size_t(100),
                 static_cast<size_t>(nSizeNeeded) / sizeof(HMODULE));
    for( size_t i = 0; i < nModules; i++ )
    {
        if( GetProcAddress(ahModules[i], "Py_SetProgramName") )
        {
            libHandle = ahModules[i];
            CPLDebug("VRT", "Current process has python symbols loaded");
            break;
        }
    }

    // Then try the user provided shared object name
    if( libHandle == NULL && pszPythonSO != NULL )
    {
        UINT        uOldErrorMode;
        /* Avoid error boxes to pop up (#5211, #5525) */
        uOldErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX |
                                     SEM_FAILCRITICALERRORS);

#if (defined(WIN32) && _MSC_VER >= 1310) || __MSVCRT_VERSION__ >= 0x0601
        if( CPLTestBool( CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) )
        {
            wchar_t *pwszFilename =
                CPLRecodeToWChar( pszPythonSO, CPL_ENC_UTF8, CPL_ENC_UCS2 );
            libHandle = LoadLibraryW(pwszFilename);
            CPLFree( pwszFilename );
        }
        else
#endif
        {
            libHandle = LoadLibrary(pszPythonSO);
        }

        SetErrorMode(uOldErrorMode);

        if( libHandle == NULL )
        {
            CPLError(CE_Failure, CPLE_AppDefined,
                     "Cannot load %s",
                     pszPythonSO);
            return false;
        }
        if( GetProcAddress(libHandle, "Py_SetProgramName") == NULL )
        {
            CPLError(CE_Failure, CPLE_AppDefined,
                     "Cannot find Py_SetProgramName symbol in %s",
                     pszPythonSO);
            return false;
        }
    }

    // Then try the PYTHONSO_DEFAULT if defined at compile time
#ifdef PYTHONSO_DEFAULT
    if( libHandle == NULL )
    {
        UINT        uOldErrorMode;
        uOldErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX |
                                        SEM_FAILCRITICALERRORS);

        libHandle = LoadLibrary(PYTHONSO_DEFAULT);
        SetErrorMode(uOldErrorMode);
        if( !libHandle )
        {
            CPLDebug("VRT", "%s found", PYTHONSO_DEFAULT);
        }
    }
#endif

    // Then try to find the pythonXY.dll that corresponds to the python binary
    // in the PATH
    if( libHandle == NULL )
    {
        CPLString osDLLName;
        char* pszPath = getenv("PATH");
        if( pszPath != NULL
#ifdef DEBUG
           // For testing purposes
           && CPLTestBool( CPLGetConfigOption(
                                    "VRT_ENABLE_PYTHON_PATH", "YES") )
#endif
          )
        {
            char** papszTokens = CSLTokenizeString2(pszPath, ";", 0);
            for( int iTry = 0; iTry < 2; ++iTry )
            {
                for( char** papszIter = papszTokens;
                        papszIter != NULL && *papszIter != NULL;
                        ++papszIter )
                {
                    VSIStatBufL sStat;
                    CPLString osPythonBinary(
                            CPLFormFilename(*papszIter, "python.exe", NULL));
                    if( iTry == 1 )
                        osPythonBinary += "3";
                    if( VSIStatL(osPythonBinary, &sStat) != 0 )
                        continue;

                    CPLDebug("VRT", "Found %s", osPythonBinary.c_str());

                    // In python2.7, the dll is in the same directory as the exe
                    char** papszFiles = VSIReadDir(*papszIter);
                    for( char** papszFileIter = papszFiles;
                                papszFileIter != NULL && *papszFileIter != NULL;
                                ++papszFileIter )
                    {
                        if( STARTS_WITH_CI(*papszFileIter, "python") &&
                            EQUAL(CPLGetExtension(*papszFileIter), "dll") )
                        {
                            osDLLName = CPLFormFilename(*papszIter,
                                                        *papszFileIter,
                                                        NULL);
                            break;
                        }
                    }
                    CSLDestroy(papszFiles);

                    // In python3.2, the dll is in the DLLs subdirectory
                    if( osDLLName.empty() )
                    {
                        CPLString osDLLsDir(
                                CPLFormFilename(*papszIter, "DLLs", NULL));
                        papszFiles = VSIReadDir( osDLLsDir );
                        for( char** papszFileIter = papszFiles;
                                    papszFileIter != NULL && *papszFileIter != NULL;
                                    ++papszFileIter )
                        {
                            if( STARTS_WITH_CI(*papszFileIter, "python") &&
                                EQUAL(CPLGetExtension(*papszFileIter), "dll") )
                            {
                                osDLLName = CPLFormFilename(osDLLsDir,
                                                            *papszFileIter,
                                                            NULL);
                                break;
                            }
                        }
                        CSLDestroy(papszFiles);
                    }

                    break;
                }
                if( !osDLLName.empty() )
                    break;
            }
            CSLDestroy(papszTokens);
        }

        if( !osDLLName.empty() )
        {
            //CPLDebug("VRT", "Trying %s", osDLLName.c_str());
            UINT        uOldErrorMode;
            uOldErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX |
                                            SEM_FAILCRITICALERRORS);
            libHandle = LoadLibrary(osDLLName);
            SetErrorMode(uOldErrorMode);
            if( libHandle != NULL )
            {
                CPLDebug("VRT", "%s loaded", osDLLName.c_str());
            }
        }
    }

    // Otherwise probe a few known objects
    // Note: update vrt_tutorial.dox if change
    if( libHandle == NULL )
    {
        const char* const apszPythonSO[] = { "python27.dll",
                                            "python26.dll",
                                            "python34.dll",
                                            "python35.dll",
                                            "python36.dll",
                                            "python33.dll",
                                            "python32.dll" };
        UINT        uOldErrorMode;
        uOldErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX |
                                        SEM_FAILCRITICALERRORS);

        for( size_t i = 0; libHandle == NULL &&
                            i < CPL_ARRAYSIZE(apszPythonSO); ++i )
        {
            CPLDebug("VRT", "Trying %s", apszPythonSO[i]);
            libHandle = LoadLibrary(apszPythonSO[i]);
            if( libHandle != NULL )
                CPLDebug("VRT", "... success");
        }
        SetErrorMode(uOldErrorMode);
    }
#endif
    if( !libHandle )
    {
        CPLError(CE_Failure, CPLE_AppDefined,
                 "Cannot find python/libpython. You can set the PYTHONSO "
                 "configuration option to point to the a python .so/.dll/.dylib");
        return false;
    }

    LOAD(libHandle, Py_SetProgramName);
    LOAD_NOCHECK(libHandle, PyBuffer_FromReadWriteMemory);
    LOAD_NOCHECK(libHandle, PyBuffer_FillInfo);
    LOAD_NOCHECK(libHandle, PyMemoryView_FromBuffer);
    if( PyBuffer_FromReadWriteMemory == NULL &&
        (PyBuffer_FillInfo == NULL || PyMemoryView_FromBuffer == NULL) )
    {
        CPLError(CE_Failure, CPLE_AppDefined,
                 "Cannot find PyBuffer_FillInfo or "
                 "PyBuffer_FillInfo+PyMemoryView_FromBuffer\n");
        return false;
    }
    LOAD(libHandle, PyTuple_New);
    if( PyBuffer_FromReadWriteMemory )
    {
        // Python 2
        LOAD(libHandle, PyInt_FromLong);
        LOAD(libHandle, PyString_FromStringAndSize);
        LOAD(libHandle, PyString_AsString);
    }
    else
    {
        // Python 3
        LOAD_WITH_NAME(libHandle, PyInt_FromLong, "PyLong_FromLong");
        LOAD_WITH_NAME(libHandle, PyString_FromStringAndSize,
                                        "PyBytes_FromStringAndSize");
        LOAD_WITH_NAME(libHandle, PyString_AsString, "PyBytes_AsString");
    }
    LOAD(libHandle, PyFloat_FromDouble);
    LOAD(libHandle, PyObject_Call);
    LOAD(libHandle, Py_IncRef);
    LOAD(libHandle, Py_DecRef);
    LOAD(libHandle, PyErr_Occurred);
    LOAD(libHandle, PyErr_Print);
    LOAD(libHandle, Py_IsInitialized);
    LOAD(libHandle, Py_InitializeEx);
    LOAD(libHandle, PyEval_InitThreads);
    LOAD(libHandle, PyEval_SaveThread);
    LOAD(libHandle, PyEval_RestoreThread);
    LOAD(libHandle, Py_Finalize);
    LOAD(libHandle, Py_CompileString);
    LOAD(libHandle, PyImport_ExecCodeModule);
    LOAD(libHandle, PyObject_GetAttrString);
    LOAD(libHandle, PyTuple_SetItem);
    LOAD(libHandle, PyObject_Print);
    LOAD(libHandle, PyImport_ImportModule);
    LOAD(libHandle, PyCallable_Check);
    LOAD(libHandle, PyDict_New);
    LOAD(libHandle, PyDict_SetItemString);
    LOAD(libHandle, PyGILState_Ensure);
    LOAD(libHandle, PyGILState_Release);
    LOAD(libHandle, PyErr_Fetch);
    LOAD(libHandle, PyErr_Clear);
    LOAD(libHandle, Py_GetVersion);

    CPLString osPythonVersion(Py_GetVersion());
    osPythonVersion.replaceAll("\r\n", ' ');
    osPythonVersion.replaceAll('\n', ' ');
    CPLDebug("VRT", "Python version used: %s", osPythonVersion.c_str());

#else // LOAD_NOCHECK_WITH_NAME
    CPLError(CE_Failure, CPLE_AppDefined,
             "This platform doesn't support dynamic loading of libraries")
    return false;
#endif // LOAD_NOCHECK_WITH_NAME

    bInit = true;
    return bInit;
}

/************************************************************************/
/*                      GetPyExceptionString()                          */
/************************************************************************/

static CPLString GetPyExceptionString()
{
    PyObject *poPyType = NULL;
    PyObject *poPyValue = NULL;
    PyObject *poPyTraceback = NULL;

    PyErr_Fetch(&poPyType, &poPyValue, &poPyTraceback);
    if( poPyType )
        Py_IncRef(poPyType);
    if( poPyValue )
        Py_IncRef(poPyValue);
    if( poPyTraceback )
        Py_IncRef(poPyTraceback);

    // This is a mess. traceback.format_exception/format_exception_only
    // sometimes throw exceptions themselves !
    CPLString osPythonCode(
        "import traceback\n"
        "\n"
        "def GDALFormatException2(etype, value):\n"
        "    try:\n"
        "       return ''.join(traceback.format_exception_only(etype, value)).encode('UTF-8')\n"
        "    except:\n"
        "       return (str(etype) + ', ' + str(value)).encode('UTF-8')\n"
        "\n"
        "def GDALFormatException3(etype, value, tb):\n"
        //"    print(etype, value, tb)\n"
        "    try:\n"
        "       return ''.join(traceback.format_exception(etype, value, tb)).encode('UTF-8')\n"
        "    except:\n"
        "       return (str(etype) + ', ' + str(value)).encode('UTF-8')\n");

    CPLString osRet("An exception occurred in exception formatting code...");

    static int nCounter = 0;
    CPLString osModuleName( CPLSPrintf("gdal_exception_%d", nCounter));
    PyObject* poCompiledString = Py_CompileString(osPythonCode,
                                                  osModuleName, Py_file_input);
    if( poCompiledString == NULL || PyErr_Occurred() )
    {
        PyErr_Print();
    }
    else
    {
        PyObject* poModule =
            PyImport_ExecCodeModule(osModuleName, poCompiledString);
        CPLAssert(poModule);

        Py_DecRef(poCompiledString);

        PyObject* poPyGDALFormatException2 = PyObject_GetAttrString(poModule,
                                                "GDALFormatException2" );
        CPLAssert(poPyGDALFormatException2);

        PyObject* poPyGDALFormatException3 = PyObject_GetAttrString(poModule,
                                                "GDALFormatException3" );
        CPLAssert(poPyGDALFormatException3);

        Py_DecRef(poModule);

        PyObject* pyArgs = PyTuple_New( poPyTraceback ? 3 : 2);
        PyTuple_SetItem(pyArgs, 0, poPyType);
        PyTuple_SetItem(pyArgs, 1, poPyValue);
        if( poPyTraceback )
            PyTuple_SetItem(pyArgs, 2, poPyTraceback );
        PyObject* poPyRet = PyObject_Call(
            poPyTraceback ? poPyGDALFormatException3 : poPyGDALFormatException2,
            pyArgs, NULL );
        Py_DecRef(pyArgs);

        if( PyErr_Occurred() )
        {
            osRet = "An exception occurred in exception formatting code...";
            PyErr_Print();
        }
        else
        {
            osRet = PyString_AsString(poPyRet);
            Py_DecRef(poPyRet);
        }

        Py_DecRef(poPyGDALFormatException2);
        Py_DecRef(poPyGDALFormatException3);
    }

    if( poPyType )
        Py_DecRef(poPyType);
    if( poPyValue )
        Py_DecRef(poPyValue);
    if( poPyTraceback )
        Py_DecRef(poPyTraceback);

    return osRet;
}

/************************************************************************/
/* ==================================================================== */
/*                     VRTDerivedRasterBandPrivateData                  */
/* ==================================================================== */
/************************************************************************/

class VRTDerivedRasterBandPrivateData
{
    public:
        CPLString m_osCode;
        CPLString m_osLanguage;
        int       m_nBufferRadius;
        PyObject* m_poGDALCreateNumpyArray;
        PyObject* m_poUserFunction;
        bool      m_bPythonInitializationDone;
        bool      m_bPythonInitializationSuccess;
        bool      m_bExclusiveLock;
        bool      m_bFirstTime;
        std::vector< std::pair<CPLString,CPLString> > m_oFunctionArgs;

        VRTDerivedRasterBandPrivateData():
            m_osLanguage("C"),
            m_nBufferRadius(0),
            m_poGDALCreateNumpyArray(NULL),
            m_poUserFunction(NULL),
            m_bPythonInitializationDone(false),
            m_bPythonInitializationSuccess(false),
            m_bExclusiveLock(false),
            m_bFirstTime(true)
        {
        }

        virtual ~VRTDerivedRasterBandPrivateData()
        {
            if( m_poGDALCreateNumpyArray )
                Py_DecRef(m_poGDALCreateNumpyArray);
            if( m_poUserFunction )
                Py_DecRef(m_poUserFunction);

            CPLMutexHolder oHolder(&ghMutex);
            gnPythonInstanceCounter --;
        }
};

/************************************************************************/
/* ==================================================================== */
/*                            VRT_GIL_Holder                            */
/* ==================================================================== */
/************************************************************************/

class VRT_GIL_Holder
{
        bool             m_bExclusiveLock;
        PyGILState_STATE m_eState;

    public:

        explicit VRT_GIL_Holder(bool bExclusiveLock);
        virtual ~VRT_GIL_Holder();
};

VRT_GIL_Holder::VRT_GIL_Holder(bool bExclusiveLock):
    m_bExclusiveLock(bExclusiveLock)
{
    if( bExclusiveLock )
    {
        if( ghMutex )
            CPLAcquireMutex( ghMutex, 1000.0 );
    }
    m_eState = PyGILState_Ensure();
}

VRT_GIL_Holder::~VRT_GIL_Holder()
{
    PyGILState_Release(m_eState);
    if( m_bExclusiveLock )
    {
        if( ghMutex )
            CPLReleaseMutex( ghMutex );
    }
    else
    {
    }
}

/************************************************************************/
/* ==================================================================== */
/*                          VRTDerivedRasterBand                        */
/* ==================================================================== */
/************************************************************************/

/************************************************************************/
/*                        VRTDerivedRasterBand()                        */
/************************************************************************/

VRTDerivedRasterBand::VRTDerivedRasterBand( GDALDataset *poDSIn, int nBandIn ) :
    VRTSourcedRasterBand( poDSIn, nBandIn ),
    m_poPrivate(NULL),
    pszFuncName(NULL),
    eSourceTransferType(GDT_Unknown)
{
    m_poPrivate = new VRTDerivedRasterBandPrivateData;
}

/************************************************************************/
/*                        VRTDerivedRasterBand()                        */
/************************************************************************/

VRTDerivedRasterBand::VRTDerivedRasterBand( GDALDataset *poDSIn, int nBandIn,
                                            GDALDataType eType,
                                            int nXSize, int nYSize ) :
    VRTSourcedRasterBand(poDSIn, nBandIn, eType, nXSize, nYSize),
    m_poPrivate(NULL),
    pszFuncName(NULL),
    eSourceTransferType(GDT_Unknown)
{
    m_poPrivate = new VRTDerivedRasterBandPrivateData;
}

/************************************************************************/
/*                       ~VRTDerivedRasterBand()                        */
/************************************************************************/

VRTDerivedRasterBand::~VRTDerivedRasterBand()

{
    CPLFree( pszFuncName );
    delete m_poPrivate;
}

/************************************************************************/
/*                               Cleanup()                              */
/************************************************************************/

void VRTDerivedRasterBand::Cleanup()
{
    if( ghMutex )
        CPLDestroyMutex(ghMutex);
    ghMutex = NULL;

    if( gnPythonInstanceCounter == 0 && gbHasInitializedPython &&
        CPLTestBool(CPLGetConfigOption("GDAL_VRT_ENABLE_PYTHON_FINALIZE",
                                       "YES")) )
    {
        // We call Py_Finalize at driver destruction, rather at dataset
        // destruction, since numpy crashes when it is reloaded after the next
        // Py_Initialize
        CPLDebug("VRT", "Py_Finalize() = %p", Py_Finalize);
        PyEval_RestoreThread(gphThreadState);
        Py_Finalize();
        gbHasInitializedPython = false;
        gphThreadState = NULL;
    }
}

/************************************************************************/
/*                           AddPixelFunction()                         */
/************************************************************************/

/*! @endcond */

/**
 * This adds a pixel function to the global list of available pixel
 * functions for derived bands.  Pixel functions must be registered
 * in this way before a derived band tries to access data.
 *
 * Derived bands are stored with only the name of the pixel function
 * that it will apply, and if a pixel function matching the name is not
 * found the IRasterIO() call will do nothing.
 *
 * @param pszFuncName Name used to access pixel function
 * @param pfnNewFunction Pixel function associated with name.  An
 *  existing pixel function registered with the same name will be
 *  replaced with the new one.
 *
 * @return CE_None, invalid (NULL) parameters are currently ignored.
 */
CPLErr CPL_STDCALL
GDALAddDerivedBandPixelFunc( const char *pszFuncName,
                             GDALDerivedPixelFunc pfnNewFunction )
{
    if( pszFuncName == NULL || pszFuncName[0] == '\0' ||
        pfnNewFunction == NULL )
    {
      return CE_None;
    }

    osMapPixelFunction[pszFuncName] = pfnNewFunction;

    return CE_None;
}

/*! @cond Doxygen_Suppress */

/**
 * This adds a pixel function to the global list of available pixel
 * functions for derived bands.
 *
 * This is the same as the c function GDALAddDerivedBandPixelFunc()
 *
 * @param pszFuncName Name used to access pixel function
 * @param pfnNewFunction Pixel function associated with name.  An
 *  existing pixel function registered with the same name will be
 *  replaced with the new one.
 *
 * @return CE_None, invalid (NULL) parameters are currently ignored.
 */
CPLErr
VRTDerivedRasterBand::AddPixelFunction(
    const char *pszFuncName, GDALDerivedPixelFunc pfnNewFunction )
{
    return GDALAddDerivedBandPixelFunc(pszFuncName, pfnNewFunction);
}

/************************************************************************/
/*                           GetPixelFunction()                         */
/************************************************************************/

/**
 * Get a pixel function previously registered using the global
 * AddPixelFunction.
 *
 * @param pszFuncName The name associated with the pixel function.
 *
 * @return A derived band pixel function, or NULL if none have been
 * registered for pszFuncName.
 */
GDALDerivedPixelFunc
VRTDerivedRasterBand::GetPixelFunction( const char *pszFuncName )
{
    if( pszFuncName == NULL || pszFuncName[0] == '\0' )
    {
        return NULL;
    }

    std::map<CPLString, GDALDerivedPixelFunc>::iterator oIter =
        osMapPixelFunction.find(pszFuncName);

    if( oIter == osMapPixelFunction.end())
        return NULL;

    return oIter->second;
}

/************************************************************************/
/*                         SetPixelFunctionName()                       */
/************************************************************************/

/**
 * Set the pixel function name to be applied to this derived band.  The
 * name should match a pixel function registered using AddPixelFunction.
 *
 * @param pszFuncNameIn Name of pixel function to be applied to this derived
 * band.
 */
void VRTDerivedRasterBand::SetPixelFunctionName( const char *pszFuncNameIn )
{
    pszFuncName = CPLStrdup( pszFuncNameIn );
}

/************************************************************************/
/*                         SetSourceTransferType()                      */
/************************************************************************/

/**
 * Set the transfer type to be used to obtain pixel information from
 * all of the sources.  If unset, the transfer type used will be the
 * same as the derived band data type.  This makes it possible, for
 * example, to pass CFloat32 source pixels to the pixel function, even
 * if the pixel function generates a raster for a derived band that
 * is of type Byte.
 *
 * @param eDataTypeIn Data type to use to obtain pixel information from
 * the sources to be passed to the derived band pixel function.
 */
void VRTDerivedRasterBand::SetSourceTransferType( GDALDataType eDataTypeIn )
{
    eSourceTransferType = eDataTypeIn;
}

/************************************************************************/
/*                           InitializePython()                         */
/************************************************************************/

bool VRTDerivedRasterBand::InitializePython()
{
    if( m_poPrivate->m_bPythonInitializationDone )
        return m_poPrivate->m_bPythonInitializationSuccess;

    m_poPrivate->m_bPythonInitializationDone = true;
    m_poPrivate->m_bPythonInitializationSuccess = false;

    const CPLString osPythonFullname( pszFuncName ? pszFuncName : "" );
    const size_t nIdxDot = osPythonFullname.rfind(".");
    CPLString osPythonModule;
    CPLString osPythonFunction;
    if( nIdxDot != std::string::npos )
    {
        osPythonModule = osPythonFullname.substr(0, nIdxDot);
        osPythonFunction = osPythonFullname.substr(nIdxDot+1);
    }
    else
    {
        osPythonFunction = osPythonFullname;
    }

#ifndef GDAL_VRT_DISABLE_PYTHON
    const char* pszPythonEnabled =
                            CPLGetConfigOption("GDAL_VRT_ENABLE_PYTHON", NULL);
#else
    const char* pszPythonEnabled = "NO";
#endif
    const CPLString osPythonEnabled(pszPythonEnabled ? pszPythonEnabled :
                                            GDAL_VRT_ENABLE_PYTHON_DEFAULT);

    if( EQUAL(osPythonEnabled, "TRUSTED_MODULES") )
    {
        bool bIsTrustedModule = false;
        const CPLString osVRTTrustedModules(
                    CPLGetConfigOption( "GDAL_VRT_PYTHON_TRUSTED_MODULES", "") );
        if( !osPythonModule.empty() )
        {
            char** papszTrustedModules = CSLTokenizeString2(
                                                osVRTTrustedModules, ",", 0 );
            for( char** papszIter = papszTrustedModules;
                !bIsTrustedModule && papszIter && *papszIter;
                ++papszIter )
            {
                const char* pszIterModule = *papszIter;
                size_t nIterModuleLen = strlen(pszIterModule);
                if( nIterModuleLen > 2 &&
                    strncmp(pszIterModule + nIterModuleLen - 2, ".*", 2) == 0 )
                {
                    bIsTrustedModule =
                        (strncmp( osPythonModule, pszIterModule,
                                                  nIterModuleLen - 2 ) == 0) &&
                        (osPythonModule.size() == nIterModuleLen - 2 ||
                         (osPythonModule.size() >= nIterModuleLen &&
                          osPythonModule[nIterModuleLen-1] == '.') );
                }
                else if( nIterModuleLen >= 1 &&
                        pszIterModule[nIterModuleLen-1] == '*' )
                {
                    bIsTrustedModule = (strncmp( osPythonModule, pszIterModule,
                                                nIterModuleLen - 1 ) == 0);
                }
                else
                {
                    bIsTrustedModule =
                                (strcmp(osPythonModule, pszIterModule) == 0);
                }
            }
            CSLDestroy(papszTrustedModules);
        }

        if( !bIsTrustedModule )
        {
            if( osPythonModule.empty() )
            {
                CPLError(CE_Failure, CPLE_AppDefined,
                         "Python code needs to be executed, but it uses online code "
                         "in the VRT whereas the current policy is to trust only "
                         "code from external trusted modules (defined in the "
                         "GDAL_VRT_PYTHON_TRUSTED_MODULES configuration option). "
                         "If you trust the code in %s, you can set the "
                         "GDAL_VRT_ENABLE_PYTHON configuration option to YES.",
                         GetDataset() ? GetDataset()->GetDescription() :
                                    "(unknown VRT)");
            }
            else if( osVRTTrustedModules.empty() )
            {
                CPLError(CE_Failure, CPLE_AppDefined,
                         "Python code needs to be executed, but it uses code "
                         "from module '%s', whereas the current policy is to "
                         "trust only code from modules defined in the "
                         "GDAL_VRT_PYTHON_TRUSTED_MODULES configuration option, "
                         "which is currently unset. "
                         "If you trust the code in '%s', you can add module '%s' "
                         "to GDAL_VRT_PYTHON_TRUSTED_MODULES (or set the "
                         "GDAL_VRT_ENABLE_PYTHON configuration option to YES).",
                         osPythonModule.c_str(),
                         GetDataset() ? GetDataset()->GetDescription() :
                                    "(unknown VRT)",
                         osPythonModule.c_str());
            }
            else
            {
                CPLError(CE_Failure, CPLE_AppDefined,
                         "Python code needs to be executed, but it uses code "
                         "from module '%s', whereas the current policy is to "
                         "trust only code from modules '%s' (defined in the "
                         "GDAL_VRT_PYTHON_TRUSTED_MODULES configuration option). "
                         "If you trust the code in '%s', you can add module '%s' "
                         "to GDAL_VRT_PYTHON_TRUSTED_MODULES (or set the "
                         "GDAL_VRT_ENABLE_PYTHON configuration option to YES).",
                         osPythonModule.c_str(),
                         osVRTTrustedModules.c_str(),
                         GetDataset() ? GetDataset()->GetDescription() :
                                    "(unknown VRT)",
                         osPythonModule.c_str());
            }
            return false;
        }
    }

#ifdef disabled_because_this_is_probably_broken_by_design
    // See https://lwn.net/Articles/574215/
    // and http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html
    else if( EQUAL(osPythonEnabled, "IF_SAFE") )
    {
        bool bSafe = true;
        // If the function comes from another module, then we don't know
        if( !osPythonModule.empty() )
        {
            CPLDebug("VRT", "Python function is from another module");
            bSafe = false;
        }

        CPLString osCode(m_poPrivate->m_osCode);

        // Reject all imports except a few trusted modules
        const char* const apszTrustedImports[] = {
                "import math",
                "from math import",
                "import numpy", // caution: numpy has lots of I/O functions !
                "from numpy import",
                // TODO: not sure if importing arbitrary stuff from numba is OK
                // so let's just restrict to jit.
                "from numba import jit",

                // Not imports but still whitelisted, whereas other __ is banned
                "__init__",
                "__call__",
        };
        for( size_t i = 0; i < CPL_ARRAYSIZE(apszTrustedImports); ++i )
        {
            osCode.replaceAll(CPLString(apszTrustedImports[i]), "");
        }

        // Some dangerous built-in functions or numpy functions
        const char* const apszUntrusted[] = { "import", // and __import__
                                              "eval",
                                              "compile",
                                              "open",
                                              "load", // reload, numpy.load
                                              "file", // and exec_file, numpy.fromfile, numpy.tofile
                                              "input", // and raw_input
                                              "save", // numpy.save
                                              "memmap", // numpy.memmap
                                              "DataSource", // numpy.DataSource
                                              "genfromtxt", // numpy.genfromtxt
                                              "getattr",
                                              "ctypeslib", // numpy.ctypeslib
                                              "testing", // numpy.testing
                                              "dump", // numpy.ndarray.dump
                                              "fromregex", // numpy.fromregex
                                              "__"
                                             };
        for( size_t i = 0; i < CPL_ARRAYSIZE(apszUntrusted); ++i )
        {
            if( osCode.find(apszUntrusted[i]) != std::string::npos )
            {
                CPLDebug("VRT", "Found '%s' word in Python code",
                         apszUntrusted[i]);
                bSafe = false;
            }
        }

        if( !bSafe )
        {
            CPLError(CE_Failure, CPLE_AppDefined,
                     "Python code needs to be executed, but we cannot verify "
                     "if it is safe, so this is disabled by default. "
                     "If you trust the code in %s, you can set the "
                     "GDAL_VRT_ENABLE_PYTHON configuration option to YES.",
                     GetDataset() ? GetDataset()->GetDescription() :
                                    "(unknown VRT)");
            return false;
        }
    }
#endif //disabled_because_this_is_probably_broken_by_design

    else if( !EQUAL(osPythonEnabled, "YES") &&
             !EQUAL(osPythonEnabled, "ON") &&
             !EQUAL(osPythonEnabled, "TRUE") )
    {
        if( pszPythonEnabled == NULL )
        {
            // Note: this is dead code with our current default policy
            // GDAL_VRT_ENABLE_PYTHON == "TRUSTED_MODULES"
            CPLError(CE_Failure, CPLE_AppDefined,
                 "Python code needs to be executed, but this is "
                 "disabled by default. If you trust the code in %s, "
                 "you can set the GDAL_VRT_ENABLE_PYTHON configuration "
                 "option to YES.",
                GetDataset() ? GetDataset()->GetDescription() :
                                                    "(unknown VRT)");
        }
        else
        {
            CPLError(CE_Failure, CPLE_AppDefined,
                    "Python code in %s needs to be executed, but this has been "
                    "explicitly disabled.",
                     GetDataset() ? GetDataset()->GetDescription() :
                                                            "(unknown VRT)");
        }
        return false;
    }

    if( !LoadPythonAPI() )
        return false;

    // Make sure the python interpreter is initialized
    {
        CPLMutexHolder oHolder(&ghMutex);
        int bIsInitialized = Py_IsInitialized();
        if( !bIsInitialized)
        {
            gbHasInitializedPython = true;
            Py_InitializeEx(0);
            CPLDebug("VRT", "Py_Initialize()");
            PyEval_InitThreads();
            gphThreadState = PyEval_SaveThread();
        }
        gnPythonInstanceCounter ++;
    }

    // Whether we should just use our own global mutex, in addition to Python
    // GIL locking.
    m_poPrivate->m_bExclusiveLock =
        CPLTestBool(CPLGetConfigOption("GDAL_VRT_PYTHON_EXCLUSIVE_LOCK", "NO"));

    // numba jit'ification doesn't seem to be thread-safe, so force use of
    // lock now and at first execution of function. Later executions seem to
    // be thread-safe. This problem doesn't seem to appear for code in
    // regular files
    const bool bUseExclusiveLock = m_poPrivate->m_bExclusiveLock ||
                    m_poPrivate->m_osCode.find("@jit") != std::string::npos;
    VRT_GIL_Holder oHolder(bUseExclusiveLock);

    // As we don't want to depend on numpy C API/ABI, we use a trick to build
    // a numpy array object. We define a Python function to which we pass a
    // Python buffer oject.

    // We need to build a unique module name, otherwise this will crash in
    // multithreaded use cases.
    CPLString osModuleName( CPLSPrintf("gdal_vrt_module_%p", this) );
    PyObject* poCompiledString = Py_CompileString(
        ("import numpy\n"
        "def GDALCreateNumpyArray(buffer, dtype, height, width):\n"
        "    return numpy.frombuffer(buffer, str(dtype.decode('ascii')))."
                                                "reshape([height, width])\n"
        "\n" + m_poPrivate->m_osCode).c_str(),
        osModuleName, Py_file_input);
    if( poCompiledString == NULL || PyErr_Occurred() )
    {
        CPLError(CE_Failure, CPLE_AppDefined,
                 "Couldn't compile code:\n%s",
                 GetPyExceptionString().c_str());
        return false;
    }
    PyObject* poModule =
        PyImport_ExecCodeModule(osModuleName, poCompiledString);
    Py_DecRef(poCompiledString);

    if( poModule == NULL || PyErr_Occurred() )
    {
        CPLError(CE_Failure, CPLE_AppDefined,
                 "%s", GetPyExceptionString().c_str());
        return false;
    }

    // Fetch user computation function
    if( !osPythonModule.empty() )
    {
        PyObject* poUserModule = PyImport_ImportModule(osPythonModule);
        if (poUserModule == NULL || PyErr_Occurred())
        {
            CPLError(CE_Failure, CPLE_AppDefined,
                 "%s", GetPyExceptionString().c_str());
            Py_DecRef(poModule);
            return false;
        }
        m_poPrivate->m_poUserFunction = PyObject_GetAttrString(poUserModule,
                                                            osPythonFunction );
        Py_DecRef(poUserModule);
    }
    else
    {
        m_poPrivate->m_poUserFunction = PyObject_GetAttrString(poModule,
                                            osPythonFunction );
    }
    if (m_poPrivate->m_poUserFunction == NULL || PyErr_Occurred())
    {
        CPLError(CE_Failure, CPLE_AppDefined,
                 "%s", GetPyExceptionString().c_str());
        Py_DecRef(poModule);
        return false;
    }
    if( !PyCallable_Check(m_poPrivate->m_poUserFunction) )
    {
        CPLError(CE_Failure, CPLE_AppDefined, "Object '%s' is not callable",
                 osPythonFunction.c_str());
        Py_DecRef(poModule);
        return false;
    }

    // Fetch our GDALCreateNumpyArray python function
    m_poPrivate->m_poGDALCreateNumpyArray =
        PyObject_GetAttrString(poModule, "GDALCreateNumpyArray" );
    if (m_poPrivate->m_poGDALCreateNumpyArray == NULL || PyErr_Occurred())
    {
        // Shouldn't happen normally...
        CPLError(CE_Failure, CPLE_AppDefined,
                 "%s", GetPyExceptionString().c_str());
        Py_DecRef(poModule);
        return false;
    }
    Py_DecRef(poModule);

    m_poPrivate->m_bPythonInitializationSuccess = true;
    return true;
}

/************************************************************************/
/*                             IRasterIO()                              */
/************************************************************************/

/**
 * Read/write a region of image data for this band.
 *
 * Each of the sources for this derived band will be read and passed to
 * the derived band pixel function.  The pixel function is responsible
 * for applying whatever algorithm is necessary to generate this band's
 * pixels from the sources.
 *
 * The sources will be read using the transfer type specified for sources
 * using SetSourceTransferType().  If no transfer type has been set for
 * this derived band, the band's data type will be used as the transfer type.
 *
 * @see gdalrasterband
 *
 * @param eRWFlag Either GF_Read to read a region of data, or GT_Write to
 * write a region of data.
 *
 * @param nXOff The pixel offset to the top left corner of the region
 * of the band to be accessed.  This would be zero to start from the left side.
 *
 * @param nYOff The line offset to the top left corner of the region
 * of the band to be accessed.  This would be zero to start from the top.
 *
 * @param nXSize The width of the region of the band to be accessed in pixels.
 *
 * @param nYSize The height of the region of the band to be accessed in lines.
 *
 * @param pData The buffer into which the data should be read, or from which
 * it should be written.  This buffer must contain at least nBufXSize *
 * nBufYSize words of type eBufType.  It is organized in left to right,
 * top to bottom pixel order.  Spacing is controlled by the nPixelSpace,
 * and nLineSpace parameters.
 *
 * @param nBufXSize The width of the buffer image into which the desired
 * region is to be read, or from which it is to be written.
 *
 * @param nBufYSize The height of the buffer image into which the desired
 * region is to be read, or from which it is to be written.
 *
 * @param eBufType The type of the pixel values in the pData data buffer.  The
 * pixel values will automatically be translated to/from the GDALRasterBand
 * data type as needed.
 *
 * @param nPixelSpace The byte offset from the start of one pixel value in
 * pData to the start of the next pixel value within a scanline.  If defaulted
 * (0) the size of the datatype eBufType is used.
 *
 * @param nLineSpace The byte offset from the start of one scanline in
 * pData to the start of the next.  If defaulted the size of the datatype
 * eBufType * nBufXSize is used.
 *
 * @return CE_Failure if the access fails, otherwise CE_None.
 */
CPLErr VRTDerivedRasterBand::IRasterIO( GDALRWFlag eRWFlag,
                                        int nXOff, int nYOff, int nXSize,
                                        int nYSize, void * pData, int nBufXSize,
                                        int nBufYSize, GDALDataType eBufType,
                                        GSpacing nPixelSpace,
                                        GSpacing nLineSpace,
                                        GDALRasterIOExtraArg* psExtraArg )
{
    if( eRWFlag == GF_Write )
    {
        CPLError( CE_Failure, CPLE_AppDefined,
                  "Writing through VRTSourcedRasterBand is not supported." );
        return CE_Failure;
    }

    const int nBufTypeSize = GDALGetDataTypeSizeBytes(eBufType);
    GDALDataType eSrcType = eSourceTransferType;
    if( eSrcType == GDT_Unknown || eSrcType >= GDT_TypeCount ) {
        eSrcType = eBufType;
    }
    const int nSrcTypeSize = GDALGetDataTypeSizeBytes(eSrcType);

/* -------------------------------------------------------------------- */
/*      Initialize the buffer to some background value. Use the         */
/*      nodata value if available.                                      */
/* -------------------------------------------------------------------- */
    if( bSkipBufferInitialization )
    {
        // Do nothing
    }
    else if( nPixelSpace == nBufTypeSize &&
        (!m_bNoDataValueSet || m_dfNoDataValue == 0) ) {
        memset( pData, 0,
                static_cast<size_t>(nBufXSize * nBufYSize * nPixelSpace) );
    }
    else if( m_bNoDataValueSet )
    {
        double dfWriteValue = 0.0;
        if( m_bNoDataValueSet )
            dfWriteValue = m_dfNoDataValue;

        for( int iLine = 0; iLine < nBufYSize; iLine++ )
        {
            GDALCopyWords(
                &dfWriteValue, GDT_Float64, 0,
                reinterpret_cast<GByte *>( pData ) + nLineSpace * iLine,
                eBufType, static_cast<int>(nPixelSpace), nBufXSize );
        }
    }

/* -------------------------------------------------------------------- */
/*      Do we have overviews that would be appropriate to satisfy       */
/*      this request?                                                   */
/* -------------------------------------------------------------------- */
    if( (nBufXSize < nXSize || nBufYSize < nYSize)
        && GetOverviewCount() > 0 )
    {
        if( OverviewRasterIO(
               eRWFlag, nXOff, nYOff, nXSize, nYSize,
               pData, nBufXSize, nBufYSize,
               eBufType, nPixelSpace, nLineSpace, psExtraArg ) == CE_None )
            return CE_None;
    }

    /* ---- Get pixel function for band ---- */
    GDALDerivedPixelFunc pfnPixelFunc = NULL;

    if( EQUAL(m_poPrivate->m_osLanguage, "C") )
    {
        pfnPixelFunc = VRTDerivedRasterBand::GetPixelFunction(pszFuncName);
        if( pfnPixelFunc == NULL )
        {
            CPLError( CE_Failure, CPLE_IllegalArg,
                    "VRTDerivedRasterBand::IRasterIO:"
                    "Derived band pixel function '%s' not registered.",
                    this->pszFuncName) ;
            return CE_Failure;
        }
    }

    /* TODO: It would be nice to use a MallocBlock function for each
       individual buffer that would recycle blocks of memory from a
       cache by reassigning blocks that are nearly the same size.
       A corresponding FreeBlock might only truly free if the total size
       of freed blocks gets to be too great of a percentage of the size
       of the allocated blocks. */

    // Get buffers for each source.
    const int nBufferRadius = m_poPrivate->m_nBufferRadius;
    if( nBufferRadius > (INT_MAX - nBufXSize) / 2 ||
        nBufferRadius > (INT_MAX - nBufYSize) / 2 )
    {
        return CE_Failure;
    }
    const int nExtBufXSize = nBufXSize + 2 * nBufferRadius;
    const int nExtBufYSize = nBufYSize + 2 * nBufferRadius;
    void **pBuffers
        = reinterpret_cast<void **>( CPLMalloc(sizeof(void *) * nSources) );
    for( int iSource = 0; iSource < nSources; iSource++ ) {
        pBuffers[iSource] =
            VSI_MALLOC3_VERBOSE(nSrcTypeSize, nExtBufXSize, nExtBufYSize);
        if( pBuffers[iSource] == NULL )
        {
            for (int i = 0; i < iSource; i++) {
                VSIFree(pBuffers[i]);
            }
            CPLFree(pBuffers);
            return CE_Failure;
        }

        /* ------------------------------------------------------------ */
        /* #4045: Initialize the newly allocated buffers before handing */
        /* them off to the sources. These buffers are packed, so we     */
        /* don't need any special line-by-line handling when a nonzero  */
        /* nodata value is set.                                         */
        /* ------------------------------------------------------------ */
        if( !m_bNoDataValueSet || m_dfNoDataValue == 0 )
        {
            memset( pBuffers[iSource], 0, static_cast<size_t>(nSrcTypeSize) *
                    nExtBufXSize * nExtBufYSize );
        }
        else
        {
            GDALCopyWords( &m_dfNoDataValue, GDT_Float64, 0,
                           reinterpret_cast<GByte *>( pBuffers[iSource] ),
                           eSrcType, nSrcTypeSize,
                           nExtBufXSize * nExtBufYSize );
        }
    }

    GDALRasterIOExtraArg sExtraArg;
    GDALCopyRasterIOExtraArg(&sExtraArg, psExtraArg);

    int nXShiftInBuffer = 0;
    int nYShiftInBuffer = 0;
    int nExtBufXSizeReq = nExtBufXSize;
    int nExtBufYSizeReq = nExtBufYSize;

    int nXOffExt = nXOff;
    int nYOffExt = nYOff;
    int nXSizeExt = nXSize;
    int nYSizeExt = nYSize;

    if( nBufferRadius )
    {
        double dfXRatio = static_cast<double>(nXSize) / nBufXSize;
        double dfYRatio = static_cast<double>(nYSize) / nBufYSize;

        if( !sExtraArg.bFloatingPointWindowValidity )
        {
            sExtraArg.dfXOff = nXOff;
            sExtraArg.dfYOff = nYOff;
            sExtraArg.dfXSize = nXSize;
            sExtraArg.dfYSize = nYSize;
        }

        sExtraArg.dfXOff -= dfXRatio * nBufferRadius;
        sExtraArg.dfYOff -= dfYRatio * nBufferRadius;
        sExtraArg.dfXSize += 2 * dfXRatio * nBufferRadius;
        sExtraArg.dfYSize += 2 * dfYRatio * nBufferRadius;
        if( sExtraArg.dfXOff < 0 )
        {
            nXShiftInBuffer = -static_cast<int>(sExtraArg.dfXOff / dfXRatio);
            nExtBufXSizeReq -= nXShiftInBuffer;
            sExtraArg.dfXSize += sExtraArg.dfXOff;
            sExtraArg.dfXOff = 0;
        }
        if( sExtraArg.dfYOff < 0 )
        {
            nYShiftInBuffer = -static_cast<int>(sExtraArg.dfYOff / dfYRatio);
            nExtBufYSizeReq -= nYShiftInBuffer;
            sExtraArg.dfYSize += sExtraArg.dfYOff;
            sExtraArg.dfYOff = 0;
        }
        if( sExtraArg.dfXOff + sExtraArg.dfXSize > nRasterXSize )
        {
            nExtBufXSizeReq -= static_cast<int>((sExtraArg.dfXOff +
                        sExtraArg.dfXSize - nRasterXSize) / dfXRatio);
            sExtraArg.dfXSize = nRasterXSize - sExtraArg.dfXOff;
        }
        if( sExtraArg.dfYOff + sExtraArg.dfYSize > nRasterYSize )
        {
            nExtBufYSizeReq -= static_cast<int>((sExtraArg.dfYOff +
                        sExtraArg.dfYSize - nRasterYSize) / dfYRatio);
            sExtraArg.dfYSize = nRasterYSize - sExtraArg.dfYOff;
        }

        nXOffExt = static_cast<int>(sExtraArg.dfXOff);
        nYOffExt = static_cast<int>(sExtraArg.dfYOff);
        nXSizeExt = std::min(static_cast<int>(sExtraArg.dfXSize + 0.5),
                             nRasterXSize - nXOffExt);
        nYSizeExt = std::min(static_cast<int>(sExtraArg.dfYSize + 0.5),
                             nRasterYSize - nYOffExt);
    }

    // Load values for sources into packed buffers.
    CPLErr eErr = CE_None;
    for( int iSource = 0; iSource < nSources && eErr == CE_None; iSource++ ) {
        GByte* pabyBuffer = reinterpret_cast<GByte*>(pBuffers[iSource]);
        eErr = reinterpret_cast<VRTSource *>( papoSources[iSource] )->RasterIO(
            nXOffExt, nYOffExt, nXSizeExt, nYSizeExt,
            pabyBuffer + (nYShiftInBuffer * nExtBufXSize +
                                            nXShiftInBuffer) * nSrcTypeSize,
            nExtBufXSizeReq, nExtBufYSizeReq,
            eSrcType,
            nSrcTypeSize,
            nSrcTypeSize * nExtBufXSize,
            &sExtraArg );

        // Extend first lines
        for( int iY = 0; iY < nYShiftInBuffer; iY++ )
        {
            memcpy( pabyBuffer + iY * nExtBufXSize * nSrcTypeSize,
                    pabyBuffer + nYShiftInBuffer * nExtBufXSize * nSrcTypeSize,
                    nExtBufXSize * nSrcTypeSize );
        }
        // Extend last lines
        for( int iY = nYShiftInBuffer + nExtBufYSizeReq; iY < nExtBufYSize; iY++ )
        {
            memcpy( pabyBuffer + iY * nExtBufXSize * nSrcTypeSize,
                    pabyBuffer + (nYShiftInBuffer + nExtBufYSizeReq - 1) *
                                                    nExtBufXSize * nSrcTypeSize,
                    nExtBufXSize * nSrcTypeSize );
        }
        // Extend first cols
        if( nXShiftInBuffer )
        {
            for( int iY = 0; iY < nExtBufYSize; iY ++ )
            {
                for( int iX = 0; iX < nXShiftInBuffer; iX++ )
                {
                    memcpy( pabyBuffer + (iY * nExtBufXSize + iX) * nSrcTypeSize,
                            pabyBuffer + (iY * nExtBufXSize +
                                                nXShiftInBuffer) * nSrcTypeSize,
                            nSrcTypeSize );
                }
            }
        }
        // Extent last cols
        if( nXShiftInBuffer + nExtBufXSizeReq < nExtBufXSize )
        {
            for( int iY = 0; iY < nExtBufYSize; iY ++ )
            {
                for( int iX = nXShiftInBuffer + nExtBufXSizeReq;
                         iX < nExtBufXSize; iX++ )
                {
                    memcpy( pabyBuffer + (iY * nExtBufXSize + iX) * nSrcTypeSize,
                            pabyBuffer + (iY * nExtBufXSize + nXShiftInBuffer +
                                            nExtBufXSizeReq - 1) * nSrcTypeSize,
                            nSrcTypeSize );
                }
            }
        }
    }

    // Apply pixel function.
    if( eErr == CE_None && EQUAL(m_poPrivate->m_osLanguage, "Python") )
    {
        eErr = CE_Failure;

        // numpy doesn't have native cint16/cint32
        if( eSrcType == GDT_CInt16 || eSrcType == GDT_CInt32 )
        {
            CPLError(CE_Failure, CPLE_AppDefined,
                     "CInt16/CInt32 data type not supported for SourceTransferType");
            goto end;
        }
        if( eDataType == GDT_CInt16 || eDataType == GDT_CInt32 )
        {
            CPLError(CE_Failure, CPLE_AppDefined,
                     "CInt16/CInt32 data type not supported for data type");
            goto end;
        }

        if( !InitializePython() )
            goto end;

        GByte* pabyTmpBuffer = NULL;
        // Do we need a temporary buffer or can we use directly the output
        // buffer ?
        if( nBufferRadius != 0 ||
            eDataType != eBufType ||
            nPixelSpace != nBufTypeSize ||
            nLineSpace != static_cast<GSpacing>(nBufTypeSize) * nBufXSize )
        {
            pabyTmpBuffer = reinterpret_cast<GByte*>(VSI_CALLOC_VERBOSE(
                            static_cast<size_t>(nExtBufXSize) * nExtBufYSize,
                            GDALGetDataTypeSizeBytes(eDataType)));
            if( !pabyTmpBuffer )
                goto end;
        }

        {
        const bool bUseExclusiveLock = m_poPrivate->m_bExclusiveLock ||
                    ( m_poPrivate->m_bFirstTime &&
                    m_poPrivate->m_osCode.find("@jit") != std::string::npos);
        m_poPrivate->m_bFirstTime = false;
        VRT_GIL_Holder oHolder(bUseExclusiveLock);

        // Prepare target numpy array
        PyObject* poPyDstArray = GDALCreateNumpyArray(
                                    m_poPrivate->m_poGDALCreateNumpyArray,
                                    pabyTmpBuffer ? pabyTmpBuffer : pData,
                                    eDataType,
                                    nExtBufYSize,
                                    nExtBufXSize);
        if( !poPyDstArray )
        {
            VSIFree(pabyTmpBuffer);
            goto end;
        }

        // Wrap source buffers as input numpy arrays
        PyObject* pyArgInputArray = PyTuple_New(nSources);
        for( int i = 0; i < nSources; i++ )
        {
            GByte* pabyBuffer = reinterpret_cast<GByte*>(pBuffers[i]);
            PyObject* poPySrcArray = GDALCreateNumpyArray(
                        m_poPrivate->m_poGDALCreateNumpyArray,
                        pabyBuffer,
                        eSrcType,
                        nExtBufYSize,
                        nExtBufXSize);
            CPLAssert(poPySrcArray);
            PyTuple_SetItem(pyArgInputArray, i, poPySrcArray);
        }

        // Create arguments
        PyObject* pyArgs = PyTuple_New(10);
        PyTuple_SetItem(pyArgs, 0, pyArgInputArray);
        PyTuple_SetItem(pyArgs, 1, poPyDstArray);
        PyTuple_SetItem(pyArgs, 2, PyInt_FromLong(nXOff));
        PyTuple_SetItem(pyArgs, 3, PyInt_FromLong(nYOff));
        PyTuple_SetItem(pyArgs, 4, PyInt_FromLong(nXSize));
        PyTuple_SetItem(pyArgs, 5, PyInt_FromLong(nYSize));
        PyTuple_SetItem(pyArgs, 6, PyInt_FromLong(nRasterXSize));
        PyTuple_SetItem(pyArgs, 7, PyInt_FromLong(nRasterYSize));
        PyTuple_SetItem(pyArgs, 8, PyInt_FromLong(nBufferRadius));

        double adfGeoTransform[6];
        adfGeoTransform[0] = 0;
        adfGeoTransform[1] = 1;
        adfGeoTransform[2] = 0;
        adfGeoTransform[3] = 0;
        adfGeoTransform[4] = 0;
        adfGeoTransform[5] = 1;
        if( GetDataset() )
            GetDataset()->GetGeoTransform(adfGeoTransform);
        PyObject* pyGT = PyTuple_New(6);
        for(int i = 0; i < 6; i++ )
            PyTuple_SetItem(pyGT, i, PyFloat_FromDouble(adfGeoTransform[i]));
        PyTuple_SetItem(pyArgs, 9, pyGT);

        // Prepare kwargs
        PyObject* pyKwargs = PyDict_New();
        for( size_t i = 0; i < m_poPrivate->m_oFunctionArgs.size(); ++i )
        {
            const char* pszKey =
                m_poPrivate->m_oFunctionArgs[i].first.c_str();
            const char* pszValue =
                m_poPrivate->m_oFunctionArgs[i].second.c_str();
            PyDict_SetItemString(pyKwargs, pszKey,
                PyString_FromStringAndSize(pszValue, strlen(pszValue)));
        }

        // Call user function
        PyObject* pRetValue = PyObject_Call(
                                        m_poPrivate->m_poUserFunction,
                                        pyArgs, pyKwargs);

        Py_DecRef(pyArgs);
        Py_DecRef(pyKwargs);

        if (PyErr_Occurred())
        {
            CPLError(CE_Failure, CPLE_AppDefined,
                     "%s", GetPyExceptionString().c_str());
        }
        else
        {
            eErr = CE_None;
        }
        if( pRetValue )
            Py_DecRef(pRetValue);
        } // End of GIL section

        if( pabyTmpBuffer )
        {
            // Copy numpy destination array to user buffer
            for( int iY = 0; iY < nBufYSize; iY++ )
            {
                size_t nSrcOffset = (static_cast<size_t>(iY + nBufferRadius) *
                    nExtBufXSize + nBufferRadius) *
                    GDALGetDataTypeSizeBytes(eDataType);
                GDALCopyWords(pabyTmpBuffer + nSrcOffset,
                              eDataType,
                              GDALGetDataTypeSizeBytes(eDataType),
                              reinterpret_cast<GByte*>(pData) + iY * nLineSpace,
                              eBufType,
                              static_cast<int>(nPixelSpace),
                              nBufXSize);
            }

            VSIFree(pabyTmpBuffer);
        }
    }
    else if( eErr == CE_None && pfnPixelFunc != NULL ) {
        eErr = pfnPixelFunc( reinterpret_cast<void **>( pBuffers ), nSources,
                             pData, nBufXSize, nBufYSize,
                             eSrcType, eBufType, static_cast<int>(nPixelSpace),
                             static_cast<int>(nLineSpace) );
    }
end:
    // Release buffers.
    for ( int iSource = 0; iSource < nSources; iSource++ ) {
        VSIFree(pBuffers[iSource]);
    }
    CPLFree(pBuffers);

    return eErr;
}

/************************************************************************/
/*                         IGetDataCoverageStatus()                     */
/************************************************************************/

int  VRTDerivedRasterBand::IGetDataCoverageStatus( int /* nXOff */,
                                                   int /* nYOff */,
                                                   int /* nXSize */,
                                                   int /* nYSize */,
                                                   int /* nMaskFlagStop */,
                                                   double* pdfDataPct)
{
    if( pdfDataPct != NULL )
        *pdfDataPct = -1.0;
    return GDAL_DATA_COVERAGE_STATUS_UNIMPLEMENTED | GDAL_DATA_COVERAGE_STATUS_DATA;
}

/************************************************************************/
/*                              XMLInit()                               */
/************************************************************************/

CPLErr VRTDerivedRasterBand::XMLInit( CPLXMLNode *psTree,
                                      const char *pszVRTPath )

{
    const CPLErr eErr = VRTSourcedRasterBand::XMLInit( psTree, pszVRTPath );
    if( eErr != CE_None )
        return eErr;

    // Read derived pixel function type.
    SetPixelFunctionName( CPLGetXMLValue( psTree, "PixelFunctionType", NULL ) );
    if( pszFuncName == NULL || EQUAL(pszFuncName, "") )
    {
        CPLError(CE_Failure, CPLE_AppDefined,
                 "PixelFunctionType missing");
        return CE_Failure;
    }

    m_poPrivate->m_osLanguage = CPLGetXMLValue( psTree,
                                                "PixelFunctionLanguage", "C" );
    if( !EQUAL(m_poPrivate->m_osLanguage, "C") &&
        !EQUAL(m_poPrivate->m_osLanguage, "Python") )
    {
        CPLError(CE_Failure, CPLE_NotSupported,
                 "Unsupported PixelFunctionLanguage");
        return CE_Failure;
    }

    m_poPrivate->m_osCode =
                        CPLGetXMLValue( psTree, "PixelFunctionCode", "" );
    if( !m_poPrivate->m_osCode.empty() &&
        !EQUAL(m_poPrivate->m_osLanguage, "Python") )
    {
        CPLError(CE_Failure, CPLE_NotSupported,
                 "PixelFunctionCode can only be used with Python");
        return CE_Failure;
    }

    m_poPrivate->m_nBufferRadius =
                        atoi(CPLGetXMLValue( psTree, "BufferRadius", "0" ));
    if( m_poPrivate->m_nBufferRadius < 0 )
    {
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid value for BufferRadius");
        return CE_Failure;
    }
    if( m_poPrivate->m_nBufferRadius != 0 &&
        !EQUAL(m_poPrivate->m_osLanguage, "Python") )
    {
        CPLError(CE_Failure, CPLE_NotSupported,
                 "BufferRadius can only be used with Python");
        return CE_Failure;
    }

    CPLXMLNode* psArgs = CPLGetXMLNode( psTree, "PixelFunctionArguments" );
    if( psArgs != NULL )
    {
        if( !EQUAL(m_poPrivate->m_osLanguage, "Python") )
        {
            CPLError(CE_Failure, CPLE_NotSupported,
                     "PixelFunctionArguments can only be used with Python");
            return CE_Failure;
        }
        for( CPLXMLNode* psIter = psArgs->psChild;
                         psIter != NULL;
                         psIter = psIter->psNext )
        {
            if( psIter->eType == CXT_Attribute )
            {
                m_poPrivate->m_oFunctionArgs.push_back(
                    std::pair<CPLString,CPLString>(psIter->pszValue,
                                                   psIter->psChild->pszValue));
            }
        }
    }

    // Read optional source transfer data type.
    const char *pszTypeName = CPLGetXMLValue(psTree, "SourceTransferType", NULL);
    if( pszTypeName != NULL )
    {
        eSourceTransferType = GDALGetDataTypeByName( pszTypeName );
    }

    return CE_None;
}

/************************************************************************/
/*                           SerializeToXML()                           */
/************************************************************************/

CPLXMLNode *VRTDerivedRasterBand::SerializeToXML( const char *pszVRTPath )
{
    CPLXMLNode *psTree = VRTSourcedRasterBand::SerializeToXML( pszVRTPath );

/* -------------------------------------------------------------------- */
/*      Set subclass.                                                   */
/* -------------------------------------------------------------------- */
    CPLCreateXMLNode(
        CPLCreateXMLNode( psTree, CXT_Attribute, "subClass" ),
        CXT_Text, "VRTDerivedRasterBand" );

    /* ---- Encode DerivedBand-specific fields ---- */
    if( !EQUAL( m_poPrivate->m_osLanguage, "C" ) )
    {
        CPLSetXMLValue( psTree, "PixelFunctionLanguage",
                        m_poPrivate->m_osLanguage );
    }
    if( pszFuncName != NULL && strlen(pszFuncName) > 0 )
        CPLSetXMLValue( psTree, "PixelFunctionType", pszFuncName );
    if( !m_poPrivate->m_oFunctionArgs.empty() )
    {
        CPLXMLNode* psArgs = CPLCreateXMLNode( psTree, CXT_Element,
                                               "PixelFunctionArguments" );
        for( size_t i = 0; i < m_poPrivate->m_oFunctionArgs.size(); ++i )
        {
            const char* pszKey =
                m_poPrivate->m_oFunctionArgs[i].first.c_str();
            const char* pszValue =
                m_poPrivate->m_oFunctionArgs[i].second.c_str();
            CPLCreateXMLNode(
                CPLCreateXMLNode( psArgs, CXT_Attribute, pszKey ),
                                  CXT_Text, pszValue );
        }
    }
    if( !m_poPrivate->m_osCode.empty() )
    {
        if( m_poPrivate->m_osCode.find("<![CDATA[") == std::string::npos )
        {
            CPLCreateXMLNode(
                CPLCreateXMLNode( psTree,
                                  CXT_Element, "PixelFunctionCode" ),
                 CXT_Literal,
                 ("<![CDATA[" + m_poPrivate->m_osCode + "]]>").c_str() );
        }
        else
        {
            CPLSetXMLValue( psTree, "PixelFunctionCode",
                            m_poPrivate->m_osCode );
        }
    }
    if( m_poPrivate->m_nBufferRadius != 0 )
        CPLSetXMLValue( psTree, "BufferRadius",
                        CPLSPrintf("%d",m_poPrivate->m_nBufferRadius) );
    if( this->eSourceTransferType != GDT_Unknown)
        CPLSetXMLValue( psTree, "SourceTransferType",
                        GDALGetDataTypeName( eSourceTransferType ) );

    return psTree;
}

/************************************************************************/
/*                             GetMinimum()                             */
/************************************************************************/

double VRTDerivedRasterBand::GetMinimum( int *pbSuccess )
{
    return GDALRasterBand::GetMinimum(pbSuccess);
}

/************************************************************************/
/*                             GetMaximum()                             */
/************************************************************************/

double VRTDerivedRasterBand::GetMaximum( int *pbSuccess )
{
    return GDALRasterBand::GetMaximum(pbSuccess);
}

/************************************************************************/
/*                       ComputeRasterMinMax()                          */
/************************************************************************/

CPLErr VRTDerivedRasterBand::ComputeRasterMinMax( int bApproxOK, double* adfMinMax )
{
    return GDALRasterBand::ComputeRasterMinMax( bApproxOK, adfMinMax );
}

/************************************************************************/
/*                         ComputeStatistics()                          */
/************************************************************************/

CPLErr
VRTDerivedRasterBand::ComputeStatistics( int bApproxOK,
                                         double *pdfMin, double *pdfMax,
                                         double *pdfMean, double *pdfStdDev,
                                         GDALProgressFunc pfnProgress,
                                         void *pProgressData )

{
    return GDALRasterBand::ComputeStatistics(  bApproxOK,
                                            pdfMin, pdfMax,
                                            pdfMean, pdfStdDev,
                                            pfnProgress, pProgressData );
}

/************************************************************************/
/*                            GetHistogram()                            */
/************************************************************************/

CPLErr VRTDerivedRasterBand::GetHistogram( double dfMin, double dfMax,
                                           int nBuckets, GUIntBig *panHistogram,
                                           int bIncludeOutOfRange, int bApproxOK,
                                           GDALProgressFunc pfnProgress,
                                           void *pProgressData )

{
    return VRTRasterBand::GetHistogram( dfMin, dfMax,
                                            nBuckets, panHistogram,
                                            bIncludeOutOfRange, bApproxOK,
                                            pfnProgress, pProgressData );
}

/*! @endcond */
