CPLErr GDALRasterBlock::Internalize()

{
    CPLMutexHolderD(&hRBMutex);
    void    *pNewData;
    int     nSizeInBytes;
    GIntBig nCurCacheMax = GDALGetCacheMax64();

    /* No risk of overflow as it is checked in GDALRasterBand::InitBlockInfo() */
    nSizeInBytes = nXSize * nYSize * (GDALGetDataTypeSize(eType) / 8);

    pNewData = VSIMalloc(nSizeInBytes);
    if (pNewData == NULL)
    {
        CPLError(CE_Failure, CPLE_OutOfMemory,
                 "GDALRasterBlock::Internalize : Out of memory allocating %d bytes.",
                 nSizeInBytes);
        return(CE_Failure);
    }

    if (pData != NULL)
        memcpy(pNewData, pData, nSizeInBytes);

    pData = pNewData;

/* -------------------------------------------------------------------- */
/*      Flush old blocks if we are nearing our memory limit.            */
/* -------------------------------------------------------------------- */
    AddLock(); /* don't flush this block! */

    nCacheUsed += nSizeInBytes;

    while (nCacheUsed > nCurCacheMax)
    {
        GIntBig nOldCacheUsed = nCacheUsed;

        GDALFlushCacheBlock();

        if (nCacheUsed == nOldCacheUsed)
            break;
    }

/* -------------------------------------------------------------------- */
/*      Add this block to the list.                                     */
/* -------------------------------------------------------------------- */
    Touch();
    DropLock();

    return(CE_None);
}
int CPL_STDCALL GDALGetCacheMax()
{
    GIntBig nRes = GDALGetCacheMax64();
    if (nRes > INT_MAX)
    {
        static int bHasWarned = FALSE;
        if (!bHasWarned)
        {
            CPLError(CE_Warning, CPLE_AppDefined,
                     "Cache max value doesn't fit on a 32 bit integer. "
                     "Call GDALGetCacheMax64() instead");
            bHasWarned = TRUE;
        }
        nRes = INT_MAX;
    }
    return (int)nRes;
}
CPLErr PostGISRasterRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff,
    int nYOff, int nXSize, int nYSize, void * pData, int nBufXSize,
    int nBufYSize, GDALDataType eBufType, int nPixelSpace,
    int nLineSpace)
{
    PostGISRasterDataset * poRDS = (PostGISRasterDataset *)poDS;

    CPLDebug("PostGIS_Raster",
        "PostGISRasterRasterBand::IRasterIO(): Table %s.%s (%s), Srid : %d",
        pszSchema, pszTable, pszColumn, poRDS->nSrid);

    if (eRWFlag == GF_Write) {

        /*
        ReportError(CE_Failure, CPLE_NotSupported,
            "Writing through PostGIS Raster band not supported yet");

        return CE_Failure;
        */

        // Delegate in generic IRasterIO to force IWriteBlock call.
        // TODO: This is not a solution for UPDATE scenario, but it works
        // for CREATE scenario.
        return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize,
            nYSize, pData, nBufXSize, nBufYSize, eBufType, nPixelSpace,
            nLineSpace);
    }

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

        return CE_None;
    }

    int bSameWindowAsOtherBand =
        (nXOff == poRDS->nXOffPrev &&
         nYOff == poRDS->nYOffPrev &&
         nXSize == poRDS->nXSizePrev &&
         nYSize == poRDS->nYSizePrev);
    poRDS->nXOffPrev = nXOff;
    poRDS->nYOffPrev = nYOff;
    poRDS->nXSizePrev = nXSize;
    poRDS->nYSizePrev = nYSize;

    /* Logic to determine if bands are read in order 1, 2, ... N */
    /* If so, then use multi-band caching, otherwise do just single band caching */
    if( poRDS->bAssumeMultiBandReadPattern )
    {
        if( nBand != poRDS->nNextExpectedBand )
        {
            CPLDebug("PostGIS_Raster",
                    "Disabling multi-band caching since band access pattern does not match");
            poRDS->bAssumeMultiBandReadPattern = false;
            poRDS->nNextExpectedBand = 1;
        }
        else
        {
            poRDS->nNextExpectedBand ++;
            if( poRDS->nNextExpectedBand > poRDS->GetRasterCount() )
                poRDS->nNextExpectedBand = 1;
        }
    }
    else
    {
        if( nBand == poRDS->nNextExpectedBand )
        {
            poRDS->nNextExpectedBand ++;
            if( poRDS->nNextExpectedBand > poRDS->GetRasterCount() )
            {
                CPLDebug("PostGIS_Raster", "Re-enabling multi-band caching");
                poRDS->bAssumeMultiBandReadPattern = true;
                poRDS->nNextExpectedBand = 1;
            }
        }
    }

#ifdef DEBUG_VERBOSE
    CPLDebug("PostGIS_Raster",
            "PostGISRasterRasterBand::IRasterIO: "
            "nBand = %d, nXOff = %d, nYOff = %d, nXSize = %d, nYSize = %d, nBufXSize = %d, nBufYSize = %d",
             nBand, nXOff, nYOff, nXSize,  nYSize, nBufXSize, nBufYSize);
#endif

#ifdef notdef
    /*******************************************************************
     * Optimization: We just have one tile. So, we can read it with
     * IReadBlock
     *
     * TODO: Review it. It's not working (see comment in
     * PostGISRasterDataset::ConstructOneDatasetFromTiles)
     ******************************************************************/

    if (poRDS->nTiles == 1) {

        return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize,
            nYSize, pData, nBufXSize, nBufYSize, eBufType, nPixelSpace,
            nLineSpace);
    }
#endif

    /*******************************************************************
     * Several tiles: we first look in all our sources caches. Missing
     * blocks are queried
     ******************************************************************/
    double adfProjWin[8];
    int nFeatureCount = 0;
    CPLRectObj sAoi;

    poRDS->PolygonFromCoords(nXOff, nYOff, nXOff + nXSize, nYOff + nYSize, adfProjWin);
    // (p[6], p[7]) is the minimum (x, y), and (p[2], p[3]) the max
    sAoi.minx = adfProjWin[6];
    sAoi.maxx = adfProjWin[2];
    if( adfProjWin[7] < adfProjWin[3] )
    {
        sAoi.miny = adfProjWin[7];
        sAoi.maxy = adfProjWin[3];
    }
    else
    {
        sAoi.maxy = adfProjWin[7];
        sAoi.miny = adfProjWin[3];
    }

#ifdef DEBUG_VERBOSE
    CPLDebug("PostGIS_Raster",
            "PostGISRasterRasterBand::IRasterIO: "
            "Intersection box: (%f, %f) - (%f, %f)", sAoi.minx,
            sAoi.miny, sAoi.maxx, sAoi.maxy);
#endif

    if (poRDS->hQuadTree == NULL)
    {
        ReportError(CE_Failure, CPLE_AppDefined,
            "Could not read metadata index.");
        return CE_Failure;
    }

    NullBuffer(pData, nBufXSize, nBufYSize, eBufType, nPixelSpace, nLineSpace);

    if( poRDS->bBuildQuadTreeDynamically && !bSameWindowAsOtherBand )
    {
        if( !(poRDS->LoadSources(nXOff, nYOff, nXSize, nYSize, nBand)) )
            return CE_Failure;
    }

    // Matching sources, to avoid a dumb for loop over the sources
    PostGISRasterTileDataset ** papsMatchingTiles =
        (PostGISRasterTileDataset **)
            CPLQuadTreeSearch(poRDS->hQuadTree, &sAoi, &nFeatureCount);

    // No blocks found. This is not an error (the raster may have holes)
    if (nFeatureCount == 0) {
        CPLFree(papsMatchingTiles);

        return CE_None;
    }

    int i;

    /**
     * We need to store the max, min coords for the missing tiles in
     * any place. This is as good as any other
     **/
    sAoi.minx = 0.0;
    sAoi.miny = 0.0;
    sAoi.maxx = 0.0;
    sAoi.maxy = 0.0;

    GIntBig nMemoryRequiredForTiles = 0;
    CPLString osIDsToFetch;
    int nTilesToFetch = 0;
    int nBandDataTypeSize = GDALGetDataTypeSize(eDataType) / 8;

    // Loop just over the intersecting sources
    for(i = 0; i < nFeatureCount; i++) {
        PostGISRasterTileDataset *poTile = papsMatchingTiles[i];
        PostGISRasterTileRasterBand* poTileBand =
            (PostGISRasterTileRasterBand *)poTile->GetRasterBand(nBand);

        nMemoryRequiredForTiles += poTileBand->GetXSize() * poTileBand->GetYSize() *
            nBandDataTypeSize;

        // Missing tile: we'll need to query for it
        if (!poTileBand->IsCached()) {

            // If we have a PKID, add the tile PKID to the list
            if (poTile->pszPKID != NULL)
            {
                if( osIDsToFetch.size() != 0 )
                    osIDsToFetch += ",";
                osIDsToFetch += "'";
                osIDsToFetch += poTile->pszPKID;
                osIDsToFetch += "'";
            }

            double dfTileMinX, dfTileMinY, dfTileMaxX, dfTileMaxY;
            poTile->GetExtent(&dfTileMinX, &dfTileMinY,
                              &dfTileMaxX, &dfTileMaxY);

            /**
             * We keep the general max and min values of all the missing
             * tiles, to raise a query that intersect just that area.
             *
             * TODO: In case of just a few tiles and very separated,
             * this strategy is clearly suboptimal. We'll get our
             * missing tiles, but with a lot of other not needed tiles.
             *
             * A possible optimization will be to simply rely on the
             * I/O method of the source (must be implemented), in case
             * we have minus than a reasonable amount of tiles missing.
             * Another criteria to decide would be how separated the
             * tiles are. Two queries for just two adjacent tiles is
             * also a dumb strategy.
             **/
            if( nTilesToFetch == 0 )
            {
                sAoi.minx = dfTileMinX;
                sAoi.miny = dfTileMinY;
                sAoi.maxx = dfTileMaxX;
                sAoi.maxy = dfTileMaxY;
            }
            else
            {
                if (dfTileMinX < sAoi.minx)
                    sAoi.minx = dfTileMinX;

                if (dfTileMinY < sAoi.miny)
                    sAoi.miny = dfTileMinY;

                if (dfTileMaxX > sAoi.maxx)
                    sAoi.maxx = dfTileMaxX;

                if (dfTileMaxY > sAoi.maxy)
                    sAoi.maxy = dfTileMaxY;
            }

            nTilesToFetch ++;

        }
    }

    /* Determine caching strategy */
    int bAllBandCaching = FALSE;
    if (nTilesToFetch > 0)
    {
        GIntBig nCacheMax = (GIntBig) GDALGetCacheMax64();
        if( nMemoryRequiredForTiles > nCacheMax )
        {
            CPLDebug("PostGIS_Raster",
                    "For best performance, the block cache should be able to store " CPL_FRMT_GIB
                    " bytes for the tiles of the requested window, "
                    "but it is only " CPL_FRMT_GIB " byte large",
                    nMemoryRequiredForTiles, nCacheMax );
            nTilesToFetch = 0;
        }

        if( poRDS->GetRasterCount() > 1 && poRDS->bAssumeMultiBandReadPattern )
        {
            GIntBig nMemoryRequiredForTilesAllBands =
                nMemoryRequiredForTiles * poRDS->GetRasterCount();
            if( nMemoryRequiredForTilesAllBands <= nCacheMax )
            {
                bAllBandCaching = TRUE;
            }
            else
            {
                CPLDebug("PostGIS_Raster", "Caching only this band, but not all bands. "
                         "Cache should be " CPL_FRMT_GIB " byte large for that",
                         nMemoryRequiredForTilesAllBands);
            }
        }
    }

    // Raise a query for missing tiles and cache them
    if (nTilesToFetch > 0) {

        /**
         * There are several options here, to raise the query.
         * - Get all the tiles which PKID is in a list of missing
         *   PKIDs.
         * - Get all the tiles that intersect a polygon constructed
         *   based on the (min - max) values calculated before.
         * - Get all the tiles with upper left pixel included in the
         *   range (min - max) calculated before.
         *
         * The first option is the most efficient one when a PKID exists.
         * After that, the second one is the most efficient one when a
         * spatial index exists.
         * The third one is the only one available when neither a PKID or spatial
         * index exist.
         **/
        CPLString osCommand;
        PGresult * poResult;

        CPLString osRasterToFetch;
        if (bAllBandCaching)
            osRasterToFetch = pszColumn;
        else
            osRasterToFetch.Printf("ST_Band(%s, %d)", pszColumn, nBand);

        int bHasWhere = FALSE;
        if (osIDsToFetch.size() && (poRDS->bIsFastPK || !(poRDS->HasSpatialIndex())) ) {
            osCommand.Printf("SELECT %s, "
                "ST_Metadata(%s), %s FROM %s.%s",
                osRasterToFetch.c_str(), pszColumn,
                poRDS->GetPrimaryKeyRef(), pszSchema, pszTable);
            if( nTilesToFetch < poRDS->nTiles || poRDS->bBuildQuadTreeDynamically )
            {
                bHasWhere = TRUE;
                osCommand += " WHERE ";
                osCommand += poRDS->pszPrimaryKeyName;
                osCommand += " IN (";
                osCommand += osIDsToFetch;
                osCommand += ")";
            }
        }

        else {
            CPLLocaleC oCLocale; // Force C locale to avoid commas instead of decimal points (for QGIS e.g.)
            bHasWhere = TRUE;
            osCommand.Printf("SELECT %s, ST_Metadata(%s), %s FROM %s.%s WHERE ",
                             osRasterToFetch.c_str(), pszColumn,
                             (poRDS->GetPrimaryKeyRef()) ? poRDS->GetPrimaryKeyRef() : "'foo'",
                             pszSchema, pszTable);
            if( poRDS->HasSpatialIndex() )
            {
                osCommand += CPLSPrintf("%s && "
                        "ST_GeomFromText('POLYGON((%.18f %.18f,%.18f %.18f,%.18f %.18f,%.18f %.18f,%.18f %.18f))')",
                        pszColumn,
                        adfProjWin[0], adfProjWin[1],
                        adfProjWin[2], adfProjWin[3],
                        adfProjWin[4], adfProjWin[5],
                        adfProjWin[6], adfProjWin[7],
                        adfProjWin[0], adfProjWin[1]);
            }
            else
            {
                #define EPS 1e-5
                osCommand += CPLSPrintf("ST_UpperLeftX(%s)"
                    " BETWEEN %f AND %f AND ST_UpperLeftY(%s) BETWEEN "
                    "%f AND %f", pszColumn, sAoi.minx-EPS, sAoi.maxx+EPS,
                    pszColumn, sAoi.miny-EPS, sAoi.maxy+EPS);
            }
        }

        if( poRDS->pszWhere != NULL )
        {
            if( bHasWhere )
                osCommand += " AND (";
            else
                osCommand += " WHERE (";
            osCommand += poRDS->pszWhere;
            osCommand += ")";
        }

        poResult = PQexec(poRDS->poConn, osCommand.c_str());

#ifdef DEBUG_QUERY
        CPLDebug("PostGIS_Raster",
            "PostGISRasterRasterBand::IRasterIO(): Query = \"%s\" --> number of rows = %d",
            osCommand.c_str(), poResult ? PQntuples(poResult) : 0 );
#endif

        if (poResult == NULL ||
            PQresultStatus(poResult) != PGRES_TUPLES_OK ||
            PQntuples(poResult) < 0) {

            if (poResult)
                PQclear(poResult);

            CPLError(CE_Failure, CPLE_AppDefined,
                "PostGISRasterRasterBand::IRasterIO(): %s",
                PQerrorMessage(poRDS->poConn));

            // Free the object that holds pointers to matching tiles
            CPLFree(papsMatchingTiles);
            return CE_Failure;
        }

        /**
         * No data. Return the buffer filled with nodata values
         **/
        else if (PQntuples(poResult) == 0) {
            PQclear(poResult);

            // Free the object that holds pointers to matching tiles
            CPLFree(papsMatchingTiles);
            return CE_None;
        }

        /**
         * Ok, we loop over the results
         **/
        int nTuples = PQntuples(poResult);
        for(i = 0; i < nTuples; i++)
        {
            const char* pszMetadata = PQgetvalue(poResult, i, 1);
            const char* pszRaster = PQgetvalue(poResult, i, 0);
            const char *pszPKID = (poRDS->GetPrimaryKeyRef() != NULL) ?  PQgetvalue(poResult, i, 2) : NULL;
            poRDS->CacheTile(pszMetadata, pszRaster, pszPKID, nBand, bAllBandCaching);
        } // All tiles have been added to cache

        PQclear(poResult);
    } // End missing tiles

/* -------------------------------------------------------------------- */
/*      Overlay each source in turn over top this.                      */
/* -------------------------------------------------------------------- */

    CPLErr eErr = CE_None;
    /* Sort tiles by ascending PKID, so that the draw order is determinist */
    if( poRDS->GetPrimaryKeyRef() != NULL )
    {
        qsort(papsMatchingTiles, nFeatureCount, sizeof(PostGISRasterTileDataset*),
              SortTilesByPKID);
    }

    for(i = 0; i < nFeatureCount && eErr == CE_None; i++)
    {
        PostGISRasterTileDataset *poTile = papsMatchingTiles[i];
        PostGISRasterTileRasterBand* poTileBand =
            (PostGISRasterTileRasterBand *)poTile->GetRasterBand(nBand);
        eErr =
            poTileBand->poSource->RasterIO( nXOff, nYOff, nXSize, nYSize,
                                            pData, nBufXSize, nBufYSize,
                                            eBufType, nPixelSpace, nLineSpace);
    }

    // Free the object that holds pointers to matching tiles
    CPLFree(papsMatchingTiles);

    return eErr;

}
CPLErr GDALRasterBlock::Internalize()

{
    void        *pNewData = NULL;
    int         nSizeInBytes;

    CPLAssert( pData == NULL );

    // This call will initialize the hRBLock mutex. Other call places can
    // only be called if we have go through there.
    GIntBig     nCurCacheMax = GDALGetCacheMax64();

    /* No risk of overflow as it is checked in GDALRasterBand::InitBlockInfo() */
    nSizeInBytes = GetBlockSize();

    /* -------------------------------------------------------------------- */
    /*      Flush old blocks if we are nearing our memory limit.            */
    /* -------------------------------------------------------------------- */
    int bFirstIter = TRUE;
    int bLoopAgain;
    do
    {
        bLoopAgain = FALSE;
        GDALRasterBlock* apoBlocksToFree[64];
        int nBlocksToFree = 0;
        {
            TAKE_LOCK;

            if( bFirstIter )
                nCacheUsed += nSizeInBytes;
            GDALRasterBlock *poTarget = poOldest;
            while( nCacheUsed > nCurCacheMax )
            {
                while( poTarget != NULL && poTarget->GetLockCount() > 0 )
                    poTarget = poTarget->poPrevious;

                if( poTarget != NULL )
                {
                    GDALRasterBlock* _poPrevious = poTarget->poPrevious;

                    poTarget->Detach_unlocked();
                    poTarget->GetBand()->UnreferenceBlock(poTarget->GetXOff(),poTarget->GetYOff());

                    apoBlocksToFree[nBlocksToFree++] = poTarget;
                    if( poTarget->GetDirty() )
                    {
                        // Only free one dirty block at a time so that
                        // other dirty blocks of other bands with the same coordinates
                        // can be found with TryGetLockedBlock()
                        bLoopAgain = ( nCacheUsed > nCurCacheMax );
                        break;
                    }
                    if( nBlocksToFree == 64 )
                    {
                        CPLDebug("GDAL", "More than 64 blocks are flagged to be flushed. Not trying more");
                        break;
                    }

                    poTarget = _poPrevious;
                }
                else
                    break;
            }

            /* -------------------------------------------------------------------- */
            /*      Add this block to the list.                                     */
            /* -------------------------------------------------------------------- */
            if( !bLoopAgain )
                Touch_unlocked();
        }

        bFirstIter = FALSE;

        /* Now free blocks we have detached and removed from their band */
        for(int i=0; i<nBlocksToFree; i++)
        {
            GDALRasterBlock *poBlock = apoBlocksToFree[i];

            if( poBlock->GetDirty() )
            {
                CPLErr eErr = poBlock->Write();
                if( eErr != CE_None )
                {
                    /* Save the error for later reporting */
                    poBlock->GetBand()->SetFlushBlockErr(eErr);
                }
            }

            /* Try to recycle the data of an existing block */
            void* pDataBlock = poBlock->pData;
            if( pNewData == NULL && pDataBlock != NULL &&
                    poBlock->GetBlockSize() >= nSizeInBytes )
            {
                pNewData = pDataBlock;
                poBlock->pData = NULL;
            }

            delete poBlock;
        }
    }
    while(bLoopAgain);

    if( pNewData == NULL )
    {
        pNewData = VSIMalloc( nSizeInBytes );
        if( pNewData == NULL )
        {
            CPLError( CE_Failure, CPLE_OutOfMemory,
                      "GDALRasterBlock::Internalize : Out of memory allocating %d bytes.",
                      nSizeInBytes);
            return( CE_Failure );
        }
    }

    pData = pNewData;

    return( CE_None );
}
CPLErr GDALRasterBlock::Internalize()

{
    CPLAssert( pData == NULL );

    void        *pNewData = NULL;

    // This call will initialize the hRBLock mutex. Other call places can
    // only be called if we have go through there.
    const GIntBig nCurCacheMax = GDALGetCacheMax64();

    // No risk of overflow as it is checked in GDALRasterBand::InitBlockInfo().
    const int nSizeInBytes = GetBlockSize();

/* -------------------------------------------------------------------- */
/*      Flush old blocks if we are nearing our memory limit.            */
/* -------------------------------------------------------------------- */
    bool bFirstIter = true;
    bool bLoopAgain = false;
    do
    {
        bLoopAgain = false;
        GDALRasterBlock* apoBlocksToFree[64] = { NULL };
        int nBlocksToFree = 0;
        {
            TAKE_LOCK;

            if( bFirstIter )
                nCacheUsed += nSizeInBytes;
            GDALRasterBlock *poTarget = poOldest;
            while( nCacheUsed > nCurCacheMax )
            {
                while( poTarget != NULL )
                {
                    if( CPLAtomicCompareAndExchange(
                            &(poTarget->nLockCount), 0, -1) )
                        break;
                    poTarget = poTarget->poPrevious;
                }

                if( poTarget != NULL )
                {
                    if( bSleepsForBockCacheDebug )
                        CPLSleep(CPLAtof(
                            CPLGetConfigOption(
                                "GDAL_RB_INTERNALIZE_SLEEP_AFTER_DROP_LOCK",
                                "0")));

                    GDALRasterBlock* _poPrevious = poTarget->poPrevious;

                    poTarget->Detach_unlocked();
                    poTarget->GetBand()->UnreferenceBlock(poTarget);

                    apoBlocksToFree[nBlocksToFree++] = poTarget;
                    if( poTarget->GetDirty() )
                    {
                        // Only free one dirty block at a time so that
                        // other dirty blocks of other bands with the same
                        // coordinates can be found with TryGetLockedBlock()
                        bLoopAgain = nCacheUsed > nCurCacheMax;
                        break;
                    }
                    if( nBlocksToFree == 64 )
                    {
                        bLoopAgain = ( nCacheUsed > nCurCacheMax );
                        break;
                    }

                    poTarget = _poPrevious;
                }
                else
                {
                    break;
                }
            }

        /* ------------------------------------------------------------------ */
        /*      Add this block to the list.                                   */
        /* ------------------------------------------------------------------ */
            if( !bLoopAgain )
                Touch_unlocked();
        }

        bFirstIter = false;

        // Now free blocks we have detached and removed from their band.
        for( int i = 0; i < nBlocksToFree; ++i)
        {
            GDALRasterBlock * const poBlock = apoBlocksToFree[i];

            if( poBlock->GetDirty() )
            {
                CPLErr eErr = poBlock->Write();
                if( eErr != CE_None )
                {
                    // Save the error for later reporting.
                    poBlock->GetBand()->SetFlushBlockErr(eErr);
                }
            }

            // Try to recycle the data of an existing block.
            void* pDataBlock = poBlock->pData;
            if( pNewData == NULL && pDataBlock != NULL &&
                poBlock->GetBlockSize() == nSizeInBytes )
            {
                pNewData = pDataBlock;
            }
            else
            {
                VSIFree(poBlock->pData);
            }
            poBlock->pData = NULL;

            poBlock->GetBand()->AddBlockToFreeList(poBlock);
        }
    }
    while(bLoopAgain);

    if( pNewData == NULL )
    {
        pNewData = VSI_MALLOC_VERBOSE( nSizeInBytes );
        if( pNewData == NULL )
        {
            return( CE_Failure );
        }
    }

    pData = pNewData;

    return CE_None;
}
CPLErr GDALRasterizeLayers(GDALDatasetH hDS,
                           int nBandCount, int *panBandList,
                           int nLayerCount, OGRLayerH *pahLayers,
                           GDALTransformerFunc pfnTransformer,
                           void *pTransformArg,
                           double *padfLayerBurnValues,
                           char **papszOptions,
                           GDALProgressFunc pfnProgress,
                           void *pProgressArg)

{
#ifndef OGR_ENABLED
    CPLError(CE_Failure, CPLE_NotSupported, "GDALRasterizeLayers() unimplemented in a non OGR build");
    return CE_Failure;
#else
    GDALDataType  eType;
    unsigned char *pabyChunkBuf;
    GDALDataset   *poDS = (GDALDataset*) hDS;

    if (pfnProgress == NULL)
        pfnProgress = GDALDummyProgress;

/* -------------------------------------------------------------------- */
/*      Do some rudimentary arg checking.                               */
/* -------------------------------------------------------------------- */
    if (nBandCount == 0 || nLayerCount == 0)
        return CE_None;

    // prototype band.
    GDALRasterBand *poBand = poDS->GetRasterBand(panBandList[0]);
    if (poBand == NULL)
        return CE_Failure;

    int              bAllTouched      = CSLFetchBoolean(papszOptions, "ALL_TOUCHED", FALSE);
    const char       *pszOpt          = CSLFetchNameValue(papszOptions, "BURN_VALUE_FROM");
    GDALBurnValueSrc eBurnValueSource = GBV_UserBurnValue;
    if (pszOpt)
    {
        if (EQUAL(pszOpt, "Z"))
            eBurnValueSource = GBV_Z;

        /*else if( EQUAL(pszOpt,"M"))
            eBurnValueSource = GBV_M;*/
    }

/* -------------------------------------------------------------------- */
/*      Establish a chunksize to operate on.  The larger the chunk      */
/*      size the less times we need to make a pass through all the      */
/*      shapes.                                                         */
/* -------------------------------------------------------------------- */
    int        nYChunkSize, nScanlineBytes;
    const char *pszYChunkSize =
        CSLFetchNameValue(papszOptions, "CHUNKYSIZE");

    if (poBand->GetRasterDataType() == GDT_Byte)
        eType = GDT_Byte;
    else
        eType = GDT_Float32;

    nScanlineBytes = nBandCount * poDS->GetRasterXSize()
                     * (GDALGetDataTypeSize(eType) / 8);

    if (pszYChunkSize && ((nYChunkSize = atoi(pszYChunkSize))) != 0)
        ;
    else
    {
        GIntBig nYChunkSize64 = GDALGetCacheMax64() / nScanlineBytes;
        if (nYChunkSize64 > INT_MAX)
            nYChunkSize = INT_MAX;
        else
            nYChunkSize = (int)nYChunkSize64;
    }

    if (nYChunkSize < 1)
        nYChunkSize = 1;

    if (nYChunkSize > poDS->GetRasterYSize())
        nYChunkSize = poDS->GetRasterYSize();

    pabyChunkBuf = (unsigned char*) VSIMalloc(nYChunkSize * nScanlineBytes);
    if (pabyChunkBuf == NULL)
    {
        CPLError(CE_Failure, CPLE_OutOfMemory,
                 "Unable to allocate rasterization buffer.");
        return CE_Failure;
    }

/* -------------------------------------------------------------------- */
/*      Read the image once for all layers if user requested to render  */
/*      the whole raster in single chunk.                               */
/* -------------------------------------------------------------------- */
    if (nYChunkSize == poDS->GetRasterYSize())
    {
        if (poDS->RasterIO(GF_Read, 0, 0, poDS->GetRasterXSize(),
                           nYChunkSize, pabyChunkBuf,
                           poDS->GetRasterXSize(), nYChunkSize,
                           eType, nBandCount, panBandList, 0, 0, 0)
            != CE_None)
        {
            CPLError(CE_Failure, CPLE_OutOfMemory,
                     "Unable to read buffer.");
            CPLFree(pabyChunkBuf);
            return CE_Failure;
        }
    }

/* ==================================================================== */
/*      Read the specified layers transfoming and rasterizing           */
/*      geometries.                                                     */
/* ==================================================================== */
    CPLErr     eErr = CE_None;
    int        iLayer;
    const char *pszBurnAttribute =
        CSLFetchNameValue(papszOptions, "ATTRIBUTE");

    pfnProgress(0.0, NULL, pProgressArg);

    for (iLayer = 0; iLayer < nLayerCount; iLayer++)
    {
        int      iBurnField      = -1;
        double   *padfBurnValues = NULL;
        OGRLayer *poLayer        = (OGRLayer*) pahLayers[iLayer];

        if (!poLayer)
        {
            CPLError(CE_Warning, CPLE_AppDefined,
                     "Layer element number %d is NULL, skipping.\n", iLayer);
            continue;
        }

/* -------------------------------------------------------------------- */
/*      If the layer does not contain any features just skip it.        */
/*      Do not force the feature count, so if driver doesn't know       */
/*      exact number of features, go down the normal way.               */
/* -------------------------------------------------------------------- */
        if (poLayer->GetFeatureCount(FALSE) == 0)
            continue;

        if (pszBurnAttribute)
        {
            iBurnField =
                poLayer->GetLayerDefn()->GetFieldIndex(pszBurnAttribute);
            if (iBurnField == -1)
            {
                CPLError(CE_Warning, CPLE_AppDefined,
                         "Failed to find field %s on layer %s, skipping.\n",
                         pszBurnAttribute,
                         poLayer->GetLayerDefn()->GetName());
                continue;
            }
        }
        else
            padfBurnValues = padfLayerBurnValues + iLayer * nBandCount;

/* -------------------------------------------------------------------- */
/*      If we have no transformer, create the one from input file       */
/*      projection. Note that each layer can be georefernced            */
/*      separately.                                                     */
/* -------------------------------------------------------------------- */
        int bNeedToFreeTransformer = FALSE;

        if (pfnTransformer == NULL)
        {
            char *pszProjection = NULL;
            bNeedToFreeTransformer = TRUE;

            OGRSpatialReference *poSRS = poLayer->GetSpatialRef();
            if (!poSRS)
            {
                CPLError(CE_Warning, CPLE_AppDefined,
                         "Failed to fetch spatial reference on layer %s "
                         "to build transformer, assuming matching coordinate systems.\n",
                         poLayer->GetLayerDefn()->GetName());
            }
            else
                poSRS->exportToWkt(&pszProjection);

            pTransformArg =
                GDALCreateGenImgProjTransformer(NULL, pszProjection,
                                                hDS, NULL, FALSE, 0.0, 0);
            pfnTransformer = GDALGenImgProjTransform;

            CPLFree(pszProjection);
        }

        OGRFeature *poFeat;

        poLayer->ResetReading();

/* -------------------------------------------------------------------- */
/*      Loop over image in designated chunks.                           */
/* -------------------------------------------------------------------- */
        int iY;

        for (iY = 0;
             iY < poDS->GetRasterYSize() && eErr == CE_None;
             iY += nYChunkSize)
        {
            int nThisYChunkSize;

            nThisYChunkSize = nYChunkSize;
            if (nThisYChunkSize + iY > poDS->GetRasterYSize())
                nThisYChunkSize = poDS->GetRasterYSize() - iY;

            // Only re-read image if not a single chunk is being rendered
            if (nYChunkSize < poDS->GetRasterYSize())
            {
                eErr =
                    poDS->RasterIO(GF_Read, 0, iY,
                                   poDS->GetRasterXSize(), nThisYChunkSize,
                                   pabyChunkBuf,
                                   poDS->GetRasterXSize(), nThisYChunkSize,
                                   eType, nBandCount, panBandList, 0, 0, 0);
                if (eErr != CE_None)
                    break;
            }

            double *padfAttrValues = (double*) VSIMalloc(sizeof(double) * nBandCount);

            while ((poFeat = poLayer->GetNextFeature()) != NULL)
            {
                OGRGeometry *poGeom = poFeat->GetGeometryRef();

                if (pszBurnAttribute)
                {
                    int    iBand;
                    double dfAttrValue;

                    dfAttrValue = poFeat->GetFieldAsDouble(iBurnField);

                    for (iBand = 0; iBand < nBandCount; iBand++)
                        padfAttrValues[iBand] = dfAttrValue;

                    padfBurnValues = padfAttrValues;
                }

                gv_rasterize_one_shape(pabyChunkBuf, iY,
                                       poDS->GetRasterXSize(),
                                       nThisYChunkSize,
                                       nBandCount, eType, bAllTouched, poGeom,
                                       padfBurnValues, eBurnValueSource,
                                       pfnTransformer, pTransformArg);

                delete poFeat;
            }

            VSIFree(padfAttrValues);

            // Only write image if not a single chunk is being rendered
            if (nYChunkSize < poDS->GetRasterYSize())
            {
                eErr =
                    poDS->RasterIO(GF_Write, 0, iY,
                                   poDS->GetRasterXSize(), nThisYChunkSize,
                                   pabyChunkBuf,
                                   poDS->GetRasterXSize(), nThisYChunkSize,
                                   eType, nBandCount, panBandList, 0, 0, 0);
            }

            poLayer->ResetReading();

            if (!pfnProgress((iY + nThisYChunkSize) / ((double)poDS->GetRasterYSize()),
                             "", pProgressArg))
            {
                CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
                eErr = CE_Failure;
            }
        }

        if (bNeedToFreeTransformer)
        {
            GDALDestroyTransformer(pTransformArg);
            pTransformArg  = NULL;
            pfnTransformer = NULL;
        }
    }

/* -------------------------------------------------------------------- */
/*      Write out the image once for all layers if user requested       */
/*      to render the whole raster in single chunk.                     */
/* -------------------------------------------------------------------- */
    if (nYChunkSize == poDS->GetRasterYSize())
    {
        poDS->RasterIO(GF_Write, 0, 0,
                       poDS->GetRasterXSize(), nYChunkSize,
                       pabyChunkBuf,
                       poDS->GetRasterXSize(), nYChunkSize,
                       eType, nBandCount, panBandList, 0, 0, 0);
    }

/* -------------------------------------------------------------------- */
/*      cleanup                                                         */
/* -------------------------------------------------------------------- */
    VSIFree(pabyChunkBuf);

    return eErr;
#endif /* def OGR_ENABLED */
}