LCOV - code coverage report
Current view: directory - port - cpl_vsil_sparsefile.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 129 101 78.3 %
Date: 2012-04-28 Functions: 25 15 60.0 %

       1                 : /******************************************************************************
       2                 :  * $Id: cpl_vsil_sparsefile.cpp 21167 2010-11-24 15:19:51Z warmerdam $
       3                 :  *
       4                 :  * Project:  VSI Virtual File System
       5                 :  * Purpose:  Implementation of sparse file virtual io driver.
       6                 :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       7                 :  *
       8                 :  ******************************************************************************
       9                 :  * Copyright (c) 2010, Frank Warmerdam <warmerdam@pobox.com>
      10                 :  *
      11                 :  * Permission is hereby granted, free of charge, to any person obtaining a
      12                 :  * copy of this software and associated documentation files (the "Software"),
      13                 :  * to deal in the Software without restriction, including without limitation
      14                 :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15                 :  * and/or sell copies of the Software, and to permit persons to whom the
      16                 :  * Software is furnished to do so, subject to the following conditions:
      17                 :  *
      18                 :  * The above copyright notice and this permission notice shall be included
      19                 :  * in all copies or substantial portions of the Software.
      20                 :  *
      21                 :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      22                 :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23                 :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      24                 :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25                 :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26                 :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      27                 :  * DEALINGS IN THE SOFTWARE.
      28                 :  ****************************************************************************/
      29                 : 
      30                 : #include "cpl_vsi_virtual.h"
      31                 : #include "cpl_string.h"
      32                 : #include "cpl_multiproc.h"
      33                 : #include "cpl_minixml.h"
      34                 : #include <map>
      35                 : 
      36                 : #if defined(WIN32CE)
      37                 : #  include <wce_errno.h>
      38                 : #endif
      39                 : 
      40                 : CPL_CVSID("$Id: cpl_vsil_sparsefile.cpp 21167 2010-11-24 15:19:51Z warmerdam $");
      41                 : 
      42             108 : class SFRegion { 
      43                 : public:
      44              24 :     SFRegion() : fp(NULL), nDstOffset(0), nSrcOffset(0), nLength(0), 
      45              24 :                  byValue(0), bTriedOpen(FALSE) {}
      46                 : 
      47                 :     CPLString     osFilename;
      48                 :     VSILFILE     *fp;
      49                 :     GUIntBig      nDstOffset;
      50                 :     GUIntBig      nSrcOffset;
      51                 :     GUIntBig      nLength;
      52                 :     GByte         byValue;
      53                 :     int           bTriedOpen;
      54                 : };
      55                 : 
      56                 : /************************************************************************/
      57                 : /* ==================================================================== */
      58                 : /*                         VSISparseFileHandle                          */
      59                 : /* ==================================================================== */
      60                 : /************************************************************************/
      61                 : 
      62                 : class VSISparseFileHandle : public VSIVirtualHandle
      63               6 : { 
      64                 :   public:
      65               6 :     VSISparseFileHandle() : nCurOffset(0) {}
      66                 : 
      67                 :     GUIntBig           nOverallLength;
      68                 :     GUIntBig           nCurOffset;
      69                 : 
      70                 :     std::vector<SFRegion> aoRegions;
      71                 : 
      72                 :     virtual int       Seek( vsi_l_offset nOffset, int nWhence );
      73                 :     virtual vsi_l_offset Tell();
      74                 :     virtual size_t    Read( void *pBuffer, size_t nSize, size_t nMemb );
      75                 :     virtual size_t    Write( const void *pBuffer, size_t nSize, size_t nMemb );
      76                 :     virtual int       Eof();
      77                 :     virtual int       Close();
      78                 : };
      79                 : 
      80                 : /************************************************************************/
      81                 : /* ==================================================================== */
      82                 : /*                   VSISparseFileFilesystemHandler                     */
      83                 : /* ==================================================================== */
      84                 : /************************************************************************/
      85                 : 
      86                 : class VSISparseFileFilesystemHandler : public VSIFilesystemHandler 
      87                 : {
      88                 : public:
      89                 :                      VSISparseFileFilesystemHandler();
      90                 :     virtual          ~VSISparseFileFilesystemHandler();
      91                 : 
      92                 :     int              DecomposePath( const char *pszPath, 
      93                 :                                     CPLString &osFilename, 
      94                 :                                     vsi_l_offset &nSparseFileOffset,
      95                 :                                     vsi_l_offset &nSparseFileSize );
      96                 : 
      97                 :     virtual VSIVirtualHandle *Open( const char *pszFilename, 
      98                 :                                     const char *pszAccess);
      99                 :     virtual int      Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags );
     100                 :     virtual int      Unlink( const char *pszFilename );
     101                 :     virtual int      Mkdir( const char *pszDirname, long nMode );
     102                 :     virtual int      Rmdir( const char *pszDirname );
     103                 :     virtual char   **ReadDir( const char *pszDirname );
     104                 : };
     105                 : 
     106                 : /************************************************************************/
     107                 : /* ==================================================================== */
     108                 : /*                             VSISparseFileHandle                      */
     109                 : /* ==================================================================== */
     110                 : /************************************************************************/
     111                 : 
     112                 : /************************************************************************/
     113                 : /*                               Close()                                */
     114                 : /************************************************************************/
     115                 : 
     116               4 : int VSISparseFileHandle::Close()
     117                 : 
     118                 : {
     119                 :     unsigned int i;
     120                 : 
     121              20 :     for( i = 0; i < aoRegions.size(); i++ )
     122                 :     {
     123              16 :         if( aoRegions[i].fp != NULL )
     124               8 :             VSIFCloseL( aoRegions[i].fp );
     125                 :     }
     126                 : 
     127               4 :     return 0;
     128                 : }
     129                 : 
     130                 : /************************************************************************/
     131                 : /*                                Seek()                                */
     132                 : /************************************************************************/
     133                 : 
     134              70 : int VSISparseFileHandle::Seek( vsi_l_offset nOffset, int nWhence )
     135                 : 
     136                 : {
     137              70 :     if( nWhence == SEEK_SET )
     138              66 :         nCurOffset = nOffset;
     139               4 :     else if( nWhence == SEEK_CUR )
     140                 :     {
     141               0 :         nCurOffset += nOffset;
     142                 :     }
     143               4 :     else if( nWhence == SEEK_END )
     144                 :     {
     145               4 :         nCurOffset = nOverallLength + nOffset;
     146                 :     }
     147                 :     else
     148                 :     {
     149               0 :         errno = EINVAL;
     150               0 :         return -1;
     151                 :     }
     152                 : 
     153              70 :     return 0;
     154                 : }
     155                 : 
     156                 : /************************************************************************/
     157                 : /*                                Tell()                                */
     158                 : /************************************************************************/
     159                 : 
     160               4 : vsi_l_offset VSISparseFileHandle::Tell()
     161                 : 
     162                 : {
     163               4 :     return nCurOffset;
     164                 : }
     165                 : 
     166                 : /************************************************************************/
     167                 : /*                                Read()                                */
     168                 : /************************************************************************/
     169                 : 
     170            9958 : size_t VSISparseFileHandle::Read( void * pBuffer, size_t nSize, size_t nCount )
     171                 : 
     172                 : {
     173                 : /* -------------------------------------------------------------------- */
     174                 : /*      Find what region we are in, searching linearly from the         */
     175                 : /*      start.                                                          */
     176                 : /* -------------------------------------------------------------------- */
     177                 :     unsigned int iRegion;
     178                 : 
     179           29636 :     for( iRegion = 0; iRegion < aoRegions.size(); iRegion++ )
     180                 :     {
     181           29634 :         if( nCurOffset >= aoRegions[iRegion].nDstOffset
     182                 :             && nCurOffset < aoRegions[iRegion].nDstOffset + aoRegions[iRegion].nLength )
     183            9956 :             break;
     184                 :     }
     185                 : 
     186                 : /* -------------------------------------------------------------------- */
     187                 : /*      Default to zeroing the buffer if no corresponding region was    */
     188                 : /*      found.                                                          */
     189                 : /* -------------------------------------------------------------------- */
     190            9958 :     if( iRegion == aoRegions.size() )
     191                 :     {
     192               2 :         memset( pBuffer, 0, nSize * nCount );
     193               2 :         nCurOffset += nSize * nSize;
     194               2 :         return nCount;
     195                 :     }
     196                 : 
     197                 : /* -------------------------------------------------------------------- */
     198                 : /*      If this request crosses region boundaries, split it into two    */
     199                 : /*      requests.                                                       */
     200                 : /* -------------------------------------------------------------------- */
     201            9956 :     size_t nReturnCount = nCount;
     202            9956 :     GUIntBig nBytesRequested = nSize * nCount;
     203                 :     GUIntBig nBytesAvailable = 
     204            9956 :         aoRegions[iRegion].nDstOffset + aoRegions[iRegion].nLength;
     205                 : 
     206            9956 :     if( nCurOffset + nBytesRequested > nBytesAvailable )
     207                 :     {
     208                 :         size_t nExtraBytes = 
     209               2 :             (size_t) (nCurOffset + nBytesRequested - nBytesAvailable);
     210                 :         // Recurse to get the rest of the request.
     211                 :         
     212               2 :         GUIntBig nCurOffsetSave = nCurOffset;
     213               2 :         nCurOffset += nBytesRequested - nExtraBytes;
     214                 :         size_t nBytesRead = 
     215                 :             this->Read( ((char *) pBuffer) + nBytesRequested - nExtraBytes,
     216               2 :                         1, nExtraBytes );
     217               2 :         nCurOffset = nCurOffsetSave;
     218                 : 
     219               2 :         if( nBytesRead < nExtraBytes )
     220               0 :             nReturnCount -= (nExtraBytes-nBytesRead) / nSize;
     221                 : 
     222               2 :         nBytesRequested -= nExtraBytes;
     223                 :     }
     224                 : 
     225                 : /* -------------------------------------------------------------------- */
     226                 : /*      Handle a constant region.                                       */
     227                 : /* -------------------------------------------------------------------- */
     228            9956 :     if( aoRegions[iRegion].osFilename.size() == 0 )
     229                 :     {
     230                 :         memset( pBuffer, aoRegions[iRegion].byValue, 
     231               2 :                 (size_t) nBytesRequested );
     232                 :     }
     233                 : 
     234                 : /* -------------------------------------------------------------------- */
     235                 : /*      Otherwise handle as a file.                                     */
     236                 : /* -------------------------------------------------------------------- */
     237                 :     else
     238                 :     {
     239            9954 :         if( aoRegions[iRegion].fp == NULL )
     240                 :         {
     241               8 :             if( !aoRegions[iRegion].bTriedOpen )
     242                 :             {
     243                 :                 aoRegions[iRegion].fp = 
     244               8 :                     VSIFOpenL( aoRegions[iRegion].osFilename, "r" );
     245               8 :                 if( aoRegions[iRegion].fp == NULL )
     246                 :                 {
     247                 :                     CPLDebug( "/vsisparse/", "Failed to open '%s'.", 
     248               0 :                               aoRegions[iRegion].osFilename.c_str() );
     249                 :                 }
     250               8 :                 aoRegions[iRegion].bTriedOpen = TRUE;
     251                 :             }
     252               8 :             if( aoRegions[iRegion].fp == NULL )
     253                 :             {
     254               0 :                 return 0;
     255                 :             }
     256                 :         }
     257                 : 
     258            9954 :         if( VSIFSeekL( aoRegions[iRegion].fp, 
     259                 :                        nCurOffset 
     260                 :                        - aoRegions[iRegion].nDstOffset 
     261                 :                        + aoRegions[iRegion].nSrcOffset,
     262                 :                        SEEK_SET ) != 0 )
     263               0 :             return 0;
     264                 : 
     265                 :         size_t nBytesRead = VSIFReadL( pBuffer, 1, (size_t) nBytesRequested, 
     266            9954 :                                        aoRegions[iRegion].fp );
     267                 : 
     268            9954 :         if( nBytesAvailable < nBytesRequested )
     269               0 :             nReturnCount = nBytesRead / nSize;
     270                 :     }
     271                 : 
     272            9956 :     nCurOffset += nReturnCount * nSize;
     273                 : 
     274            9956 :     return nReturnCount;
     275                 : }
     276                 : 
     277                 : /************************************************************************/
     278                 : /*                               Write()                                */
     279                 : /************************************************************************/
     280                 : 
     281               0 : size_t VSISparseFileHandle::Write( const void * pBuffer, size_t nSize, size_t nCount )
     282                 : 
     283                 : {
     284               0 :     errno = EBADF;
     285               0 :     return 0;
     286                 : }
     287                 : 
     288                 : /************************************************************************/
     289                 : /*                                Eof()                                 */
     290                 : /************************************************************************/
     291                 : 
     292               0 : int VSISparseFileHandle::Eof()
     293                 : 
     294                 : {
     295               0 :     return nCurOffset >= nOverallLength;
     296                 : }
     297                 : 
     298                 : /************************************************************************/
     299                 : /* ==================================================================== */
     300                 : /*                       VSISparseFileFilesystemHandler                 */
     301                 : /* ==================================================================== */
     302                 : /************************************************************************/
     303                 : 
     304                 : /************************************************************************/
     305                 : /*                      VSISparseFileFilesystemHandler()                */
     306                 : /************************************************************************/
     307                 : 
     308            1341 : VSISparseFileFilesystemHandler::VSISparseFileFilesystemHandler()
     309                 : 
     310                 : {
     311            1341 : }
     312                 : 
     313                 : /************************************************************************/
     314                 : /*                      ~VSISparseFileFilesystemHandler()               */
     315                 : /************************************************************************/
     316                 : 
     317            1297 : VSISparseFileFilesystemHandler::~VSISparseFileFilesystemHandler()
     318                 : 
     319                 : {
     320            1297 : }
     321                 : 
     322                 : /************************************************************************/
     323                 : /*                                Open()                                */
     324                 : /************************************************************************/
     325                 : 
     326                 : VSIVirtualHandle *
     327              16 : VSISparseFileFilesystemHandler::Open( const char *pszFilename, 
     328                 :                                       const char *pszAccess )
     329                 : 
     330                 : {
     331              16 :     CPLAssert( EQUALN(pszFilename,"/vsisparse/", 11) );
     332                 : 
     333              16 :     if( !EQUAL(pszAccess,"r") && !EQUAL(pszAccess,"rb") )
     334                 :     {
     335               0 :         errno = EACCES;
     336               0 :         return NULL;
     337                 :     }
     338                 : 
     339              16 :     CPLString osSparseFilePath = pszFilename + 11;
     340                 : 
     341                 : /* -------------------------------------------------------------------- */
     342                 : /*      Does this file even exist?                                      */
     343                 : /* -------------------------------------------------------------------- */
     344              16 :     VSILFILE *fp = VSIFOpenL( osSparseFilePath, "r" );
     345              16 :     if( fp == NULL )
     346              10 :         return NULL;
     347               6 :     VSIFCloseL( fp );
     348                 : 
     349                 : /* -------------------------------------------------------------------- */
     350                 : /*      Read the XML file.                                              */
     351                 : /* -------------------------------------------------------------------- */
     352               6 :     CPLXMLNode *psXMLRoot = CPLParseXMLFile( osSparseFilePath );
     353                 : 
     354               6 :     if( psXMLRoot == NULL )
     355               0 :         return NULL;
     356                 : 
     357                 : /* -------------------------------------------------------------------- */
     358                 : /*      Setup the file handle on this file.                             */
     359                 : /* -------------------------------------------------------------------- */
     360               6 :     VSISparseFileHandle *poHandle = new VSISparseFileHandle;
     361                 : 
     362                 : /* -------------------------------------------------------------------- */
     363                 : /*      Translate the desired fields out of the XML tree.               */
     364                 : /* -------------------------------------------------------------------- */
     365                 :     CPLXMLNode *psRegion;
     366                 : 
     367              30 :     for( psRegion = psXMLRoot->psChild;
     368                 :          psRegion != NULL;
     369                 :          psRegion = psRegion->psNext )
     370                 :     {
     371              30 :         if( psRegion->eType != CXT_Element )
     372               0 :             continue;
     373                 : 
     374              30 :         if( !EQUAL(psRegion->pszValue,"SubfileRegion")
     375                 :             && !EQUAL(psRegion->pszValue,"ConstantRegion") )
     376               6 :             continue;
     377                 : 
     378              24 :         SFRegion oRegion;
     379                 : 
     380              24 :         oRegion.osFilename = CPLGetXMLValue( psRegion, "Filename", "" );
     381              24 :         if( atoi(CPLGetXMLValue( psRegion, "Filename.relative", "0" )) != 0 )
     382                 :         {
     383              18 :             CPLString osSFPath = CPLGetPath(osSparseFilePath);
     384                 :             oRegion.osFilename = CPLFormFilename( osSFPath,
     385              18 :                                                   oRegion.osFilename, NULL );
     386                 :         }
     387                 : 
     388                 :         oRegion.nDstOffset = 
     389                 :             CPLScanUIntBig( CPLGetXMLValue(psRegion,"DestinationOffset","0" ),
     390              24 :                             32 );
     391                 :                             
     392                 :         oRegion.nSrcOffset = 
     393              24 :             CPLScanUIntBig( CPLGetXMLValue(psRegion,"SourceOffset","0" ), 32);
     394                 : 
     395                 :         oRegion.nLength = 
     396              24 :             CPLScanUIntBig( CPLGetXMLValue(psRegion,"RegionLength","0" ), 32);
     397                 : 
     398              24 :         oRegion.byValue = (GByte) atoi(CPLGetXMLValue(psRegion,"Value","0" ));
     399                 : 
     400              24 :         poHandle->aoRegions.push_back( oRegion );
     401                 :     }
     402                 : 
     403                 : /* -------------------------------------------------------------------- */
     404                 : /*      Get sparse file length, use maximum bound of regions if not     */
     405                 : /*      explicit in file.                                               */
     406                 : /* -------------------------------------------------------------------- */
     407                 :     poHandle->nOverallLength = 
     408               6 :         CPLScanUIntBig( CPLGetXMLValue(psXMLRoot,"Length","0" ), 32);
     409               6 :     if( poHandle->nOverallLength == 0 )
     410                 :     {
     411                 :         unsigned int i;
     412                 : 
     413               0 :         for( i = 0; i < poHandle->aoRegions.size(); i++ )
     414                 :         {
     415                 :             poHandle->nOverallLength = MAX(poHandle->nOverallLength,
     416                 :                                            poHandle->aoRegions[i].nDstOffset
     417               0 :                                            + poHandle->aoRegions[i].nLength);
     418                 :         }
     419                 :     }
     420                 : 
     421               6 :     CPLDestroyXMLNode( psXMLRoot );
     422                 : 
     423               6 :     return poHandle;
     424                 : }
     425                 : 
     426                 : /************************************************************************/
     427                 : /*                                Stat()                                */
     428                 : /************************************************************************/
     429                 : 
     430               4 : int VSISparseFileFilesystemHandler::Stat( const char * pszFilename, 
     431                 :                                           VSIStatBufL * psStatBuf,
     432                 :                                           int nFlags )
     433                 :     
     434                 : {
     435               4 :     VSIVirtualHandle *poFile = Open( pszFilename, "r" );
     436                 : 
     437               4 :     memset( psStatBuf, 0, sizeof(VSIStatBufL) );
     438                 : 
     439               4 :     if( poFile == NULL )
     440               2 :         return -1;
     441                 : 
     442               2 :     poFile->Seek( 0, SEEK_END );
     443               2 :     size_t nLength = (size_t) poFile->Tell();
     444               2 :     delete poFile;
     445                 : 
     446                 :     int nResult = VSIStatExL( pszFilename + strlen("/vsisparse/"), 
     447               2 :                             psStatBuf, nFlags );
     448                 : 
     449               2 :     psStatBuf->st_size = nLength;
     450                 : 
     451               2 :     return nResult;
     452                 : }
     453                 : 
     454                 : /************************************************************************/
     455                 : /*                               Unlink()                               */
     456                 : /************************************************************************/
     457                 : 
     458               0 : int VSISparseFileFilesystemHandler::Unlink( const char * pszFilename )
     459                 : 
     460                 : {
     461               0 :     errno = EACCES;
     462               0 :     return -1;
     463                 : }
     464                 : 
     465                 : /************************************************************************/
     466                 : /*                               Mkdir()                                */
     467                 : /************************************************************************/
     468                 : 
     469               0 : int VSISparseFileFilesystemHandler::Mkdir( const char * pszPathname,
     470                 :                                     long nMode )
     471                 : 
     472                 : {
     473               0 :     errno = EACCES;
     474               0 :     return -1;
     475                 : }
     476                 : 
     477                 : /************************************************************************/
     478                 : /*                               Rmdir()                                */
     479                 : /************************************************************************/
     480                 : 
     481               0 : int VSISparseFileFilesystemHandler::Rmdir( const char * pszPathname )
     482                 : 
     483                 : {
     484               0 :     errno = EACCES;
     485               0 :     return -1;
     486                 : }
     487                 : 
     488                 : /************************************************************************/
     489                 : /*                              ReadDir()                               */
     490                 : /************************************************************************/
     491                 : 
     492               2 : char **VSISparseFileFilesystemHandler::ReadDir( const char *pszPath )
     493                 : 
     494                 : {
     495               2 :     errno = EACCES;
     496               2 :     return NULL;
     497                 : }
     498                 : 
     499                 : /************************************************************************/
     500                 : /*                 VSIInstallSparseFileFilesystemHandler()              */
     501                 : /************************************************************************/
     502                 : 
     503                 : /**
     504                 :  * Install /vsisparse/ virtual file handler. 
     505                 :  *
     506                 :  * The sparse virtual file handler allows a virtual file to be composed
     507                 :  * from chunks of data in other files, potentially with large spaces in
     508                 :  * the virtual file set to a constant value.  This can make it possible to
     509                 :  * test some sorts of operations on what seems to be a large file with
     510                 :  * image data set to a constant value.  It is also helpful when wanting to
     511                 :  * add test files to the test suite that are too large, but for which most
     512                 :  * of the data can be ignored.  It could, in theory, also be used to 
     513                 :  * treat several files on different file systems as one large virtual file. 
     514                 :  *
     515                 :  * The file referenced by /vsisparse/ should be an XML control file 
     516                 :  * formatted something like:
     517                 :  *
     518                 :  * 
     519                 : \verbatim
     520                 : <VSISparseFile>
     521                 :   <Length>87629264</Length>
     522                 :   <SubfileRegion>  Stuff at start of file.
     523                 :     <Filename relative="1">251_head.dat</Filename>
     524                 :     <DestinationOffset>0</DestinationOffset>
     525                 :     <SourceOffset>0</SourceOffset>
     526                 :     <RegionLength>2768</RegionLength>
     527                 :   </SubfileRegion>
     528                 : 
     529                 :   <SubfileRegion>  RasterDMS node.
     530                 :     <Filename relative="1">251_rasterdms.dat</Filename>
     531                 :     <DestinationOffset>87313104</DestinationOffset>
     532                 :     <SourceOffset>0</SourceOffset>
     533                 :     <RegionLength>160</RegionLength>
     534                 :   </SubfileRegion>
     535                 : 
     536                 :   <SubfileRegion>  Stuff at end of file.
     537                 :     <Filename relative="1">251_tail.dat</Filename>
     538                 :     <DestinationOffset>87611924</DestinationOffset>
     539                 :     <SourceOffset>0</SourceOffset>
     540                 :     <RegionLength>17340</RegionLength>
     541                 :   </SubfileRegion>
     542                 : 
     543                 :   <ConstantRegion>  Default for the rest of the file.
     544                 :     <DestinationOffset>0</DestinationOffset>
     545                 :     <RegionLength>87629264</RegionLength>
     546                 :     <Value>0</Value>
     547                 :   </ConstantRegion>
     548                 : </VSISparseFile>
     549                 : \endverbatim
     550                 :  *
     551                 :  * Hopefully the values and semantics are fairly obvious. 
     552                 :  *
     553                 :  * This driver is installed by default. 
     554                 :  */
     555                 : 
     556            1341 : void VSIInstallSparseFileHandler()
     557                 : {
     558                 :     VSIFileManager::InstallHandler( "/vsisparse/", 
     559            1341 :                                     new VSISparseFileFilesystemHandler );
     560            1341 : }
     561                 :                             

Generated by: LCOV version 1.7