LTP GCOV extension - code coverage report
Current view: directory - gcore - gdalrasterblock.cpp
Test: gdal_filtered.info
Date: 2010-07-12 Instrumented lines: 150
Code covered: 81.3 % Executed lines: 122

       1                 : /******************************************************************************
       2                 :  * $Id: gdalrasterblock.cpp 19341 2010-04-07 22:15:28Z rouault $
       3                 :  *
       4                 :  * Project:  GDAL Core
       5                 :  * Purpose:  Implementation of GDALRasterBlock class and related global 
       6                 :  *           raster block cache management.
       7                 :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       8                 :  *
       9                 :  **********************************************************************
      10                 :  * Copyright (c) 1998, Frank Warmerdam <warmerdam@pobox.com>
      11                 :  *
      12                 :  * Permission is hereby granted, free of charge, to any person obtaining a
      13                 :  * copy of this software and associated documentation files (the "Software"),
      14                 :  * to deal in the Software without restriction, including without limitation
      15                 :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      16                 :  * and/or sell copies of the Software, and to permit persons to whom the
      17                 :  * Software is furnished to do so, subject to the following conditions:
      18                 :  *
      19                 :  * The above copyright notice and this permission notice shall be included
      20                 :  * in all copies or substantial portions of the Software.
      21                 :  *
      22                 :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      23                 :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      24                 :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      25                 :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      26                 :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      27                 :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      28                 :  * DEALINGS IN THE SOFTWARE.
      29                 :  ****************************************************************************/
      30                 : 
      31                 : #include "gdal_priv.h"
      32                 : #include "cpl_multiproc.h"
      33                 : 
      34                 : CPL_CVSID("$Id: gdalrasterblock.cpp 19341 2010-04-07 22:15:28Z rouault $");
      35                 : 
      36                 : static int bCacheMaxInitialized = FALSE;
      37                 : static int nCacheMax = 40 * 1024*1024;
      38                 : static volatile int nCacheUsed = 0;
      39                 : 
      40                 : static volatile GDALRasterBlock *poOldest = NULL;    /* tail */
      41                 : static volatile GDALRasterBlock *poNewest = NULL;    /* head */
      42                 : 
      43                 : static void *hRBMutex = NULL;
      44                 : 
      45                 : 
      46                 : /************************************************************************/
      47                 : /*                          GDALSetCacheMax()                           */
      48                 : /************************************************************************/
      49                 : 
      50                 : /**
      51                 :  * \brief Set maximum cache memory.
      52                 :  *
      53                 :  * This function sets the maximum amount of memory that GDAL is permitted
      54                 :  * to use for GDALRasterBlock caching.
      55                 :  *
      56                 :  * @param nNewSize the maximum number of bytes for caching.  Maximum is 2GB.
      57                 :  */
      58                 : 
      59              13 : void CPL_STDCALL GDALSetCacheMax( int nNewSize )
      60                 : 
      61                 : {
      62              13 :     nCacheMax = nNewSize;
      63                 : 
      64                 : /* -------------------------------------------------------------------- */
      65                 : /*      Flush blocks till we are under the new limit or till we         */
      66                 : /*      can't seem to flush anymore.                                    */
      67                 : /* -------------------------------------------------------------------- */
      68              26 :     while( nCacheUsed > nCacheMax )
      69                 :     {
      70               0 :         int nOldCacheUsed = nCacheUsed;
      71                 : 
      72               0 :         GDALFlushCacheBlock();
      73                 : 
      74               0 :         if( nCacheUsed == nOldCacheUsed )
      75               0 :             break;
      76                 :     }
      77              13 : }
      78                 : 
      79                 : /************************************************************************/
      80                 : /*                          GDALGetCacheMax()                           */
      81                 : /************************************************************************/
      82                 : 
      83                 : /**
      84                 :  * \brief Get maximum cache memory.
      85                 :  *
      86                 :  * Gets the maximum amount of memory available to the GDALRasterBlock
      87                 :  * caching system for caching GDAL read/write imagery. 
      88                 :  *
      89                 :  * @return maximum in bytes. 
      90                 :  */
      91                 : 
      92          289892 : int CPL_STDCALL GDALGetCacheMax()
      93                 : {
      94          289892 :     if( !bCacheMaxInitialized )
      95                 :     {
      96             333 :         const char* pszCacheMax = CPLGetConfigOption("GDAL_CACHEMAX",NULL);
      97             333 :         bCacheMaxInitialized = TRUE;
      98             333 :         if( pszCacheMax != NULL )
      99                 :         {
     100               2 :             int nNewCacheMax = atoi(pszCacheMax);
     101               2 :             if( nNewCacheMax < 100000 )
     102                 :             {
     103               2 :                 if (nNewCacheMax < 0)
     104                 :                 {
     105                 :                     CPLError(CE_Failure, CPLE_NotSupported,
     106               0 :                              "Invalid value for GDAL_CACHEMAX. Using default value.");
     107               0 :                     return nCacheMax;
     108                 :                 }
     109               2 :                 if (nNewCacheMax > 2047)
     110                 :                 {
     111                 :                     CPLError(CE_Failure, CPLE_NotSupported,
     112               0 :                              "Maximum value supported for GDAL_CACHEMAX is 2 GB. Using default value.");
     113               0 :                     return nCacheMax;
     114                 :                 }
     115               2 :                 nNewCacheMax *= 1024 * 1024;
     116                 :             }
     117               2 :             nCacheMax = nNewCacheMax;
     118                 :         }
     119                 :     }
     120                 :     
     121          289892 :     return nCacheMax;
     122                 : }
     123                 : 
     124                 : /************************************************************************/
     125                 : /*                          GDALGetCacheUsed()                          */
     126                 : /************************************************************************/
     127                 : 
     128                 : /**
     129                 :  * \brief Get cache memory used.
     130                 :  *
     131                 :  * @return the number of bytes of memory currently in use by the 
     132                 :  * GDALRasterBlock memory caching.
     133                 :  */
     134                 : 
     135               2 : int CPL_STDCALL GDALGetCacheUsed()
     136                 : {
     137               2 :     return nCacheUsed;
     138                 : }
     139                 : 
     140                 : /************************************************************************/
     141                 : /*                        GDALFlushCacheBlock()                         */
     142                 : /*                                                                      */
     143                 : /*      The workhorse of cache management!                              */
     144                 : /************************************************************************/
     145                 : 
     146                 : /**
     147                 :  * \brief Try to flush one cached raster block
     148                 :  *
     149                 :  * This function will search the first unlocked raster block and will
     150                 :  * flush it to release the associated memory.
     151                 :  *
     152                 :  * @return TRUE if one block was flushed, FALSE if there are no cached blocks
     153                 :  *         or if they are currently locked.
     154                 :  */
     155            1109 : int CPL_STDCALL GDALFlushCacheBlock()
     156                 : 
     157                 : {
     158            1109 :     return GDALRasterBlock::FlushCacheBlock();
     159                 : }
     160                 : 
     161                 : /************************************************************************/
     162                 : /* ==================================================================== */
     163                 : /*                           GDALRasterBlock                            */
     164                 : /* ==================================================================== */
     165                 : /************************************************************************/
     166                 : 
     167                 : /**
     168                 :  * \class GDALRasterBlock "gdal_priv.h"
     169                 :  *
     170                 :  * GDALRasterBlock objects hold one block of raster data for one band
     171                 :  * that is currently stored in the GDAL raster cache.  The cache holds
     172                 :  * some blocks of raster data for zero or more GDALRasterBand objects
     173                 :  * across zero or more GDALDataset objects in a global raster cache with
     174                 :  * a least recently used (LRU) list and an upper cache limit (see
     175                 :  * GDALSetCacheMax()) under which the cache size is normally kept. 
     176                 :  *
     177                 :  * Some blocks in the cache may be modified relative to the state on disk
     178                 :  * (they are marked "Dirty") and must be flushed to disk before they can
     179                 :  * be discarded.  Other (Clean) blocks may just be discarded if their memory
     180                 :  * needs to be recovered. 
     181                 :  *
     182                 :  * In normal situations applications do not interact directly with the
     183                 :  * GDALRasterBlock - instead it it utilized by the RasterIO() interfaces
     184                 :  * to implement caching. 
     185                 :  *
     186                 :  * Some driver classes are implemented in a fashion that completely avoids
     187                 :  * use of the GDAL raster cache (and GDALRasterBlock) though this is not very
     188                 :  * common.
     189                 :  */
     190                 : 
     191                 : /************************************************************************/
     192                 : /*                          FlushCacheBlock()                           */
     193                 : /*                                                                      */
     194                 : /*      Note, if we have alot of blocks locked for a long time, this    */
     195                 : /*      method is going to get slow because it will have to traverse    */
     196                 : /*      the linked list a long ways looking for a flushing              */
     197                 : /*      candidate.   It might help to re-touch locked blocks to push    */
     198                 : /*      them to the top of the list.                                    */
     199                 : /************************************************************************/
     200                 : 
     201                 : /**
     202                 :  * \brief Attempt to flush at least one block from the cache.
     203                 :  *
     204                 :  * This static method is normally used to recover memory when a request
     205                 :  * for a new cache block would put cache memory use over the established
     206                 :  * limit.   
     207                 :  *
     208                 :  * C++ analog to the C function GDALFlushCacheBlock().
     209                 :  * 
     210                 :  * @return TRUE if successful or FALSE if no flushable block is found.
     211                 :  */
     212                 : 
     213            1109 : int GDALRasterBlock::FlushCacheBlock()
     214                 : 
     215                 : {
     216                 :     int nXOff, nYOff;
     217                 :     GDALRasterBand *poBand;
     218                 : 
     219                 :     {
     220            1109 :         CPLMutexHolderD( &hRBMutex );
     221            1109 :         GDALRasterBlock *poTarget = (GDALRasterBlock *) poOldest;
     222                 : 
     223            2218 :         while( poTarget != NULL && poTarget->GetLockCount() > 0 ) 
     224               0 :             poTarget = poTarget->poPrevious;
     225                 :         
     226            1109 :         if( poTarget == NULL )
     227             950 :             return FALSE;
     228                 : 
     229             634 :         poTarget->Detach();
     230                 : 
     231             634 :         nXOff = poTarget->GetXOff();
     232             634 :         nYOff = poTarget->GetYOff();
     233             634 :         poBand = poTarget->GetBand();
     234                 :     }
     235                 : 
     236             634 :     poBand->FlushBlock( nXOff, nYOff );
     237                 : 
     238             634 :     return TRUE;
     239                 : }
     240                 : 
     241                 : /************************************************************************/
     242                 : /*                          GDALRasterBlock()                           */
     243                 : /************************************************************************/
     244                 : 
     245                 : /**
     246                 :  * @brief GDALRasterBlock Constructor 
     247                 :  *
     248                 :  * Normally only called from GDALRasterBand::GetLockedBlockRef().
     249                 :  *
     250                 :  * @param poBandIn the raster band used as source of raster block
     251                 :  * being constructed.
     252                 :  *
     253                 :  * @param nXOffIn the horizontal block offset, with zero indicating
     254                 :  * the left most block, 1 the next block and so forth. 
     255                 :  *
     256                 :  * @param nYOffIn the vertical block offset, with zero indicating
     257                 :  * the top most block, 1 the next block and so forth.
     258                 :  */
     259                 : 
     260                 : GDALRasterBlock::GDALRasterBlock( GDALRasterBand *poBandIn, 
     261          288957 :                                   int nXOffIn, int nYOffIn )
     262                 : 
     263                 : {
     264          288957 :     CPLAssert( NULL != poBandIn );
     265                 : 
     266          288957 :     poBand = poBandIn;
     267                 : 
     268          288957 :     poBand->GetBlockSize( &nXSize, &nYSize );
     269          288957 :     eType = poBand->GetRasterDataType();
     270          288957 :     pData = NULL;
     271          288957 :     bDirty = FALSE;
     272          288957 :     nLockCount = 0;
     273                 : 
     274          288957 :     poNext = poPrevious = NULL;
     275                 : 
     276          288957 :     nXOff = nXOffIn;
     277          288957 :     nYOff = nYOffIn;
     278          288957 : }
     279                 : 
     280                 : /************************************************************************/
     281                 : /*                          ~GDALRasterBlock()                          */
     282                 : /************************************************************************/
     283                 : 
     284                 : /**
     285                 :  * Block destructor. 
     286                 :  *
     287                 :  * Normally called from GDALRasterBand::FlushBlock().
     288                 :  */
     289                 : 
     290          288957 : GDALRasterBlock::~GDALRasterBlock()
     291                 : 
     292                 : {
     293          288957 :     Detach();
     294                 : 
     295          288957 :     if( pData != NULL )
     296                 :     {
     297                 :         int nSizeInBytes;
     298                 : 
     299          288957 :         VSIFree( pData );
     300                 : 
     301          288957 :         nSizeInBytes = (nXSize * nYSize * GDALGetDataTypeSize(eType)+7)/8;
     302                 : 
     303                 :         {
     304          288957 :             CPLMutexHolderD( &hRBMutex );
     305          288957 :             nCacheUsed -= nSizeInBytes;
     306                 :         }
     307                 :     }
     308                 : 
     309          288957 :     CPLAssert( nLockCount == 0 );
     310                 : 
     311                 : #ifdef ENABLE_DEBUG
     312                 :     Verify();
     313                 : #endif
     314          288957 : }
     315                 : 
     316                 : /************************************************************************/
     317                 : /*                               Detach()                               */
     318                 : /************************************************************************/
     319                 : 
     320                 : /**
     321                 :  * Remove block from cache.
     322                 :  *
     323                 :  * This method removes the current block from the linked list used to keep
     324                 :  * track of all cached blocks in order of age.  It does not affect whether
     325                 :  * the block is referenced by a GDALRasterBand nor does it destroy or flush
     326                 :  * the block.
     327                 :  */
     328                 : 
     329          578548 : void GDALRasterBlock::Detach()
     330                 : 
     331                 : {
     332          578548 :     CPLMutexHolderD( &hRBMutex );
     333                 : 
     334          578548 :     if( poOldest == this )
     335            3390 :         poOldest = poPrevious;
     336                 : 
     337          578548 :     if( poNewest == this )
     338                 :     {
     339          289424 :         poNewest = poNext;
     340                 :     }
     341                 : 
     342          578548 :     if( poPrevious != NULL )
     343             167 :         poPrevious->poNext = poNext;
     344                 : 
     345          578548 :     if( poNext != NULL )
     346          286201 :         poNext->poPrevious = poPrevious;
     347                 : 
     348          578548 :     poPrevious = NULL;
     349          578548 :     poNext = NULL;
     350          578548 : }
     351                 : 
     352                 : /************************************************************************/
     353                 : /*                               Verify()                               */
     354                 : /************************************************************************/
     355                 : 
     356                 : /**
     357                 :  * Confirms (via assertions) that the block cache linked list is in a
     358                 :  * consistent state. 
     359                 :  */
     360                 : 
     361               0 : void GDALRasterBlock::Verify()
     362                 : 
     363                 : {
     364               0 :     CPLMutexHolderD( &hRBMutex );
     365                 : 
     366               0 :     CPLAssert( (poNewest == NULL && poOldest == NULL)
     367                 :                || (poNewest != NULL && poOldest != NULL) );
     368                 : 
     369               0 :     if( poNewest != NULL )
     370                 :     {
     371               0 :         CPLAssert( poNewest->poPrevious == NULL );
     372               0 :         CPLAssert( poOldest->poNext == NULL );
     373                 :         
     374               0 :         for( GDALRasterBlock *poBlock = (GDALRasterBlock *) poNewest; 
     375                 :              poBlock != NULL;
     376                 :              poBlock = poBlock->poNext )
     377                 :         {
     378               0 :             if( poBlock->poPrevious )
     379                 :             {
     380               0 :                 CPLAssert( poBlock->poPrevious->poNext == poBlock );
     381                 :             }
     382                 : 
     383               0 :             if( poBlock->poNext )
     384                 :             {
     385               0 :                 CPLAssert( poBlock->poNext->poPrevious == poBlock );
     386                 :             }
     387                 :         }
     388               0 :     }
     389               0 : }
     390                 : 
     391                 : /************************************************************************/
     392                 : /*                               Write()                                */
     393                 : /************************************************************************/
     394                 : 
     395                 : /**
     396                 :  * Force writing of the current block, if dirty.
     397                 :  *
     398                 :  * The block is written using GDALRasterBand::IWriteBlock() on it's 
     399                 :  * corresponding band object.  Even if the write fails the block will 
     400                 :  * be marked clean. 
     401                 :  *
     402                 :  * @return CE_None otherwise the error returned by IWriteBlock().
     403                 :  */
     404                 : 
     405           28892 : CPLErr GDALRasterBlock::Write()
     406                 : 
     407                 : {
     408           28892 :     if( !GetDirty() )
     409               0 :         return CE_None;
     410                 : 
     411           28892 :     if( poBand == NULL )
     412               0 :         return CE_Failure;
     413                 : 
     414           28892 :     MarkClean();
     415                 : 
     416           28892 :     return poBand->IWriteBlock( nXOff, nYOff, pData );
     417                 : }
     418                 : 
     419                 : /************************************************************************/
     420                 : /*                               Touch()                                */
     421                 : /************************************************************************/
     422                 : 
     423                 : /**
     424                 :  * Push block to top of LRU (least-recently used) list.
     425                 :  *
     426                 :  * This method is normally called when a block is used to keep track 
     427                 :  * that it has been recently used. 
     428                 :  */
     429                 : 
     430         4605742 : void GDALRasterBlock::Touch()
     431                 : 
     432                 : {
     433         4605742 :     CPLMutexHolderD( &hRBMutex );
     434                 : 
     435         4605742 :     if( poNewest == this )
     436          705070 :         return;
     437                 : 
     438         3900672 :     if( poOldest == this )
     439         1275144 :         poOldest = this->poPrevious;
     440                 :     
     441         3900672 :     if( poPrevious != NULL )
     442         3611081 :         poPrevious->poNext = poNext;
     443                 : 
     444         3900672 :     if( poNext != NULL )
     445         2335937 :         poNext->poPrevious = poPrevious;
     446                 : 
     447         3900672 :     poPrevious = NULL;
     448         3900672 :     poNext = (GDALRasterBlock *) poNewest;
     449                 : 
     450         3900672 :     if( poNewest != NULL )
     451                 :     {
     452         3897449 :         CPLAssert( poNewest->poPrevious == NULL );
     453         3897449 :         poNewest->poPrevious = this;
     454                 :     }
     455         3900672 :     poNewest = this;
     456                 :     
     457         3900672 :     if( poOldest == NULL )
     458                 :     {
     459            3223 :         CPLAssert( poPrevious == NULL && poNext == NULL );
     460            3223 :         poOldest = this;
     461               0 :     }
     462                 : #ifdef ENABLE_DEBUG
     463                 :     Verify();
     464                 : #endif
     465                 : }
     466                 : 
     467                 : /************************************************************************/
     468                 : /*                            Internalize()                             */
     469                 : /************************************************************************/
     470                 : 
     471                 : /**
     472                 :  * Allocate memory for block.
     473                 :  *
     474                 :  * This method allocates memory for the block, and attempts to flush other
     475                 :  * blocks, if necessary, to bring the total cache size back within the limits.
     476                 :  * The newly allocated block is touched and will be considered most recently
     477                 :  * used in the LRU list. 
     478                 :  * 
     479                 :  * @return CE_None on success or CE_Failure if memory allocation fails. 
     480                 :  */
     481                 : 
     482          288957 : CPLErr GDALRasterBlock::Internalize()
     483                 : 
     484                 : {
     485          288957 :     CPLMutexHolderD( &hRBMutex );
     486                 :     void        *pNewData;
     487                 :     int         nSizeInBytes;
     488          288957 :     int         nCurCacheMax = GDALGetCacheMax();
     489                 : 
     490                 :     /* No risk of overflow as it is checked in GDALRasterBand::InitBlockInfo() */
     491          288957 :     nSizeInBytes = nXSize * nYSize * (GDALGetDataTypeSize(eType) / 8);
     492                 : 
     493          288957 :     pNewData = VSIMalloc( nSizeInBytes );
     494          288957 :     if( pNewData == NULL )
     495                 :     {
     496                 :         CPLError( CE_Failure, CPLE_OutOfMemory, 
     497                 :                   "GDALRasterBlock::Internalize : Out of memory allocating %d bytes.",
     498               0 :                   nSizeInBytes);
     499               0 :         return( CE_Failure );
     500                 :     }
     501                 : 
     502          288957 :     if( pData != NULL )
     503               0 :         memcpy( pNewData, pData, nSizeInBytes );
     504                 :     
     505          288957 :     pData = pNewData;
     506                 : 
     507                 : /* -------------------------------------------------------------------- */
     508                 : /*      Flush old blocks if we are nearing our memory limit.            */
     509                 : /* -------------------------------------------------------------------- */
     510          288957 :     AddLock(); /* don't flush this block! */
     511                 : 
     512          288957 :     nCacheUsed += nSizeInBytes;
     513          578548 :     while( nCacheUsed > nCurCacheMax )
     514                 :     {
     515            1109 :         int nOldCacheUsed = nCacheUsed;
     516                 : 
     517            1109 :         GDALFlushCacheBlock();
     518                 : 
     519            1109 :         if( nCacheUsed == nOldCacheUsed )
     520             475 :             break;
     521                 :     }
     522                 : 
     523                 : /* -------------------------------------------------------------------- */
     524                 : /*      Add this block to the list.                                     */
     525                 : /* -------------------------------------------------------------------- */
     526          288957 :     Touch();
     527          288957 :     DropLock();
     528                 : 
     529          288957 :     return( CE_None );
     530                 : }
     531                 : 
     532                 : /************************************************************************/
     533                 : /*                             MarkDirty()                              */
     534                 : /************************************************************************/
     535                 : 
     536                 : /**
     537                 :  * Mark the block as modified.
     538                 :  *
     539                 :  * A dirty block is one that has been modified and will need to be written
     540                 :  * to disk before it can be flushed.
     541                 :  */
     542                 : 
     543         1411798 : void GDALRasterBlock::MarkDirty()
     544                 : 
     545                 : {
     546         1411798 :     bDirty = TRUE;
     547         1411798 : }
     548                 : 
     549                 : 
     550                 : /************************************************************************/
     551                 : /*                             MarkClean()                              */
     552                 : /************************************************************************/
     553                 : 
     554                 : /**
     555                 :  * Mark the block as unmodified.
     556                 :  *
     557                 :  * A dirty block is one that has been modified and will need to be written
     558                 :  * to disk before it can be flushed.
     559                 :  */
     560                 : 
     561           30258 : void GDALRasterBlock::MarkClean()
     562                 : 
     563                 : {
     564           30258 :     bDirty = FALSE;
     565           30258 : }
     566                 : 
     567                 : /************************************************************************/
     568                 : /*                           SafeLockBlock()                            */
     569                 : /************************************************************************/
     570                 : 
     571                 : /**
     572                 :  * \brief Safely lock block.
     573                 :  *
     574                 :  * This method locks a GDALRasterBlock (and touches it) in a thread-safe
     575                 :  * manner.  The global block cache mutex is held while locking the block,
     576                 :  * in order to avoid race conditions with other threads that might be
     577                 :  * trying to expire the block at the same time.  The block pointer may be
     578                 :  * safely NULL, in which case this method does nothing. 
     579                 :  *
     580                 :  * @param ppBlock Pointer to the block pointer to try and lock/touch.
     581                 :  */
     582                 :  
     583         4316840 : int GDALRasterBlock::SafeLockBlock( GDALRasterBlock ** ppBlock )
     584                 : 
     585                 : {
     586         4316840 :     CPLAssert( NULL != ppBlock );
     587                 : 
     588         4316840 :     CPLMutexHolderD( &hRBMutex );
     589                 : 
     590         4316840 :     if( *ppBlock != NULL )
     591                 :     {
     592         4027828 :         (*ppBlock)->AddLock();
     593         4027828 :         (*ppBlock)->Touch();
     594                 :         
     595         4027828 :         return TRUE;
     596                 :     }
     597                 :     else
     598          289012 :         return FALSE;
     599                 : }

Generated by: LTP GCOV extension version 1.5