LCOV - code coverage report
Current view: top level - port - cpl_vsil_buffered_reader.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 100 123 81.3 %
Date: 2026-01-24 06:33:45 Functions: 15 16 93.8 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  VSI Virtual File System
       4             :  * Purpose:  Implementation of buffered reader IO functions.
       5             :  * Author:   Even Rouault, even.rouault at spatialys.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2010-2011, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : //! @cond Doxygen_Suppress
      14             : 
      15             : // The intent of this class is to be a wrapper around an underlying virtual
      16             : // handle and add very basic caching of last read bytes, so that a backward
      17             : // seek of a few bytes doesn't require a seek on the underlying virtual handle.
      18             : // This enable us to improve dramatically the performance of CPLReadLine2L() on
      19             : // a gzip file.
      20             : 
      21             : #include "cpl_port.h"
      22             : #include "cpl_vsi_virtual.h"
      23             : 
      24             : #include <cstddef>
      25             : #include <cstring>
      26             : 
      27             : #include <algorithm>
      28             : #include <vector>
      29             : 
      30             : #include "cpl_conv.h"
      31             : #include "cpl_error.h"
      32             : #include "cpl_vsi.h"
      33             : 
      34             : constexpr int MAX_BUFFER_SIZE = 65536;
      35             : 
      36             : class VSIBufferedReaderHandle final : public VSIVirtualHandle
      37             : {
      38             :     CPL_DISALLOW_COPY_ASSIGN(VSIBufferedReaderHandle)
      39             : 
      40             :     VSIVirtualHandle *m_poBaseHandle = nullptr;
      41             :     GByte *pabyBuffer = nullptr;
      42             :     GUIntBig nBufferOffset = 0;
      43             :     int nBufferSize = 0;
      44             :     GUIntBig nCurOffset = 0;
      45             :     bool bNeedBaseHandleSeek = false;
      46             :     bool bEOF = false;
      47             :     bool bError = false;
      48             :     vsi_l_offset nCheatFileSize = 0;
      49             : 
      50             :     int SeekBaseTo(vsi_l_offset nTargetOffset);
      51             : 
      52             :   public:
      53             :     explicit VSIBufferedReaderHandle(VSIVirtualHandle *poBaseHandle);
      54             :     VSIBufferedReaderHandle(VSIVirtualHandle *poBaseHandle,
      55             :                             const GByte *pabyBeginningContent,
      56             :                             vsi_l_offset nCheatFileSizeIn);
      57             :     // TODO(schwehr): Add override when support dropped for VS2008.
      58             :     ~VSIBufferedReaderHandle() override;
      59             : 
      60             :     int Seek(vsi_l_offset nOffset, int nWhence) override;
      61             :     vsi_l_offset Tell() override;
      62             :     size_t Read(void *pBuffer, size_t nBytes) override;
      63             :     size_t Write(const void *pBuffer, size_t nBytes) override;
      64             :     int Eof() override;
      65             :     int Error() override;
      66             :     void ClearErr() override;
      67             :     int Flush() override;
      68             :     int Close() override;
      69             : };
      70             : 
      71             : //! @endcond
      72             : 
      73             : /************************************************************************/
      74             : /*                    VSICreateBufferedReaderHandle()                   */
      75             : /************************************************************************/
      76             : 
      77        5373 : VSIVirtualHandle *VSICreateBufferedReaderHandle(VSIVirtualHandle *poBaseHandle)
      78             : {
      79        5373 :     return new VSIBufferedReaderHandle(poBaseHandle);
      80             : }
      81             : 
      82             : VSIVirtualHandle *
      83           7 : VSICreateBufferedReaderHandle(VSIVirtualHandle *poBaseHandle,
      84             :                               const GByte *pabyBeginningContent,
      85             :                               vsi_l_offset nCheatFileSizeIn)
      86             : {
      87             :     return new VSIBufferedReaderHandle(poBaseHandle, pabyBeginningContent,
      88           7 :                                        nCheatFileSizeIn);
      89             : }
      90             : 
      91             : //! @cond Doxygen_Suppress
      92             : 
      93             : /************************************************************************/
      94             : /*                        VSIBufferedReaderHandle()                     */
      95             : /************************************************************************/
      96             : 
      97        5373 : VSIBufferedReaderHandle::VSIBufferedReaderHandle(VSIVirtualHandle *poBaseHandle)
      98             :     : m_poBaseHandle(poBaseHandle),
      99        5373 :       pabyBuffer(static_cast<GByte *>(CPLMalloc(MAX_BUFFER_SIZE)))
     100             : {
     101        5373 : }
     102             : 
     103           7 : VSIBufferedReaderHandle::VSIBufferedReaderHandle(
     104             :     VSIVirtualHandle *poBaseHandle, const GByte *pabyBeginningContent,
     105           7 :     vsi_l_offset nCheatFileSizeIn)
     106             :     : m_poBaseHandle(poBaseHandle),
     107          14 :       pabyBuffer(static_cast<GByte *>(CPLMalloc(
     108           7 :           std::max(MAX_BUFFER_SIZE, static_cast<int>(poBaseHandle->Tell()))))),
     109          14 :       nBufferOffset(0), nBufferSize(static_cast<int>(poBaseHandle->Tell())),
     110             :       nCurOffset(0), bNeedBaseHandleSeek(true), bEOF(false),
     111          14 :       nCheatFileSize(nCheatFileSizeIn)
     112             : {
     113           7 :     memcpy(pabyBuffer, pabyBeginningContent, nBufferSize);
     114           7 : }
     115             : 
     116             : /************************************************************************/
     117             : /*                        ~VSIBufferedReaderHandle()                    */
     118             : /************************************************************************/
     119             : 
     120       10758 : VSIBufferedReaderHandle::~VSIBufferedReaderHandle()
     121             : {
     122        5379 :     delete m_poBaseHandle;
     123        5379 :     CPLFree(pabyBuffer);
     124       10758 : }
     125             : 
     126             : /************************************************************************/
     127             : /*                               Seek()                                 */
     128             : /************************************************************************/
     129             : 
     130      235764 : int VSIBufferedReaderHandle::Seek(vsi_l_offset nOffset, int nWhence)
     131             : {
     132             : #ifdef DEBUG_VERBOSE
     133             :     CPLDebug("BUFFERED", "Seek(%d,%d)", static_cast<int>(nOffset),
     134             :              static_cast<int>(nWhence));
     135             : #endif
     136      235764 :     bEOF = false;
     137      235764 :     int ret = 0;
     138      235764 :     if (nWhence == SEEK_CUR)
     139             :     {
     140        1446 :         nCurOffset += nOffset;
     141             :     }
     142      234318 :     else if (nWhence == SEEK_END)
     143             :     {
     144        2261 :         if (nCheatFileSize)
     145             :         {
     146           2 :             nCurOffset = nCheatFileSize;
     147             :         }
     148             :         else
     149             :         {
     150        2259 :             ret = m_poBaseHandle->Seek(nOffset, nWhence);
     151        2259 :             nCurOffset = m_poBaseHandle->Tell();
     152        2259 :             bNeedBaseHandleSeek = true;
     153             :         }
     154             :     }
     155             :     else
     156             :     {
     157      232057 :         nCurOffset = nOffset;
     158             :     }
     159             : 
     160      235764 :     return ret;
     161             : }
     162             : 
     163             : /************************************************************************/
     164             : /*                               Tell()                                 */
     165             : /************************************************************************/
     166             : 
     167      117955 : vsi_l_offset VSIBufferedReaderHandle::Tell()
     168             : {
     169             : #ifdef DEBUG_VERBOSE
     170             :     CPLDebug("BUFFERED", "Tell() = %d", static_cast<int>(nCurOffset));
     171             : #endif
     172      117955 :     return nCurOffset;
     173             : }
     174             : 
     175             : /************************************************************************/
     176             : /*                           SeekBaseTo()                               */
     177             : /************************************************************************/
     178             : 
     179       35134 : int VSIBufferedReaderHandle::SeekBaseTo(vsi_l_offset nTargetOffset)
     180             : {
     181       35134 :     if (m_poBaseHandle->Seek(nTargetOffset, SEEK_SET) == 0)
     182       35134 :         return TRUE;
     183             : 
     184           0 :     nCurOffset = m_poBaseHandle->Tell();
     185           0 :     if (nCurOffset > nTargetOffset)
     186           0 :         return FALSE;
     187             : 
     188           0 :     const vsi_l_offset nMaxOffset = 8192;
     189             : 
     190           0 :     std::vector<char> oTemp(nMaxOffset, 0);
     191           0 :     char *pabyTemp = &oTemp[0];
     192             : 
     193             :     while (true)
     194             :     {
     195             :         const size_t nToRead = static_cast<size_t>(
     196           0 :             std::min(nMaxOffset, nTargetOffset - nCurOffset));
     197           0 :         const size_t nRead = m_poBaseHandle->Read(pabyTemp, 1, nToRead);
     198             : 
     199           0 :         nCurOffset += nRead;
     200             : 
     201           0 :         if (nRead < nToRead)
     202             :         {
     203           0 :             bEOF = CPL_TO_BOOL(m_poBaseHandle->Eof());
     204           0 :             bError = CPL_TO_BOOL(m_poBaseHandle->Error());
     205           0 :             return FALSE;
     206             :         }
     207           0 :         if (nToRead < nMaxOffset)
     208           0 :             break;
     209           0 :     }
     210           0 :     return TRUE;
     211             : }
     212             : 
     213             : /************************************************************************/
     214             : /*                               Read()                                 */
     215             : /************************************************************************/
     216             : 
     217      354984 : size_t VSIBufferedReaderHandle::Read(void *pBuffer, size_t nTotalToRead)
     218             : {
     219             : #ifdef DEBUG_VERBOSE
     220             :     CPLDebug("BUFFERED", "Read(%d)", static_cast<int>(nTotalToRead));
     221             : #endif
     222             : 
     223      354984 :     if (nTotalToRead == 0)
     224          12 :         return 0;
     225             : 
     226      354972 :     if (nBufferSize != 0 && nCurOffset >= nBufferOffset &&
     227      329204 :         nCurOffset <= nBufferOffset + nBufferSize)
     228             :     {
     229             :         // We try to read from an offset located within the buffer.
     230             :         const size_t nReadInBuffer = static_cast<size_t>(std::min(
     231             :             nTotalToRead,
     232      319952 :             static_cast<size_t>(nBufferOffset + nBufferSize - nCurOffset)));
     233      319952 :         memcpy(pBuffer, pabyBuffer + nCurOffset - nBufferOffset, nReadInBuffer);
     234      319952 :         const size_t nToReadInFile = nTotalToRead - nReadInBuffer;
     235      319952 :         if (nToReadInFile > 0)
     236             :         {
     237             :             // The beginning of the data to read is located in the buffer
     238             :             // but the end must be read from the file.
     239      312565 :             if (bNeedBaseHandleSeek)
     240             :             {
     241         114 :                 if (!SeekBaseTo(nBufferOffset + nBufferSize))
     242             :                 {
     243           0 :                     nCurOffset += nReadInBuffer;
     244           0 :                     return nReadInBuffer;
     245             :                 }
     246             :             }
     247      312565 :             bNeedBaseHandleSeek = false;
     248             : #ifdef DEBUG_VERBOSE
     249             :             CPLAssert(m_poBaseHandle->Tell() == nBufferOffset + nBufferSize);
     250             : #endif
     251             : 
     252      625130 :             const size_t nReadInFile = m_poBaseHandle->Read(
     253      312565 :                 static_cast<GByte *>(pBuffer) + nReadInBuffer, nToReadInFile);
     254      312565 :             if (nReadInFile < nToReadInFile)
     255             :             {
     256        5461 :                 if (m_poBaseHandle->Eof())
     257        5459 :                     bEOF = true;
     258             :                 else
     259             :                 {
     260           2 :                     CPLAssert(m_poBaseHandle->Error());
     261           2 :                     bError = true;
     262             :                 }
     263             :             }
     264      312565 :             const size_t nRead = nReadInBuffer + nReadInFile;
     265             : 
     266      312565 :             nBufferSize = static_cast<int>(
     267      312565 :                 std::min(nRead, static_cast<size_t>(MAX_BUFFER_SIZE)));
     268      312565 :             nBufferOffset = nCurOffset + nRead - nBufferSize;
     269      312565 :             memcpy(pabyBuffer,
     270      312565 :                    static_cast<GByte *>(pBuffer) + nRead - nBufferSize,
     271      312565 :                    nBufferSize);
     272             : 
     273      312565 :             nCurOffset += nRead;
     274             : #ifdef DEBUG_VERBOSE
     275             :             CPLAssert(m_poBaseHandle->Tell() == nBufferOffset + nBufferSize);
     276             :             CPLAssert(m_poBaseHandle->Tell() == nCurOffset);
     277             : #endif
     278             : 
     279      312565 :             return nRead;
     280             :         }
     281             :         else
     282             :         {
     283             :             // The data to read is completely located within the buffer.
     284        7387 :             nCurOffset += nTotalToRead;
     285        7387 :             return nTotalToRead;
     286             :         }
     287             :     }
     288             :     else
     289             :     {
     290             :         // We try either to read before or after the buffer, so a seek is
     291             :         // necessary.
     292       35020 :         if (!SeekBaseTo(nCurOffset))
     293           0 :             return 0;
     294       35020 :         bNeedBaseHandleSeek = false;
     295       35020 :         const size_t nReadInFile = m_poBaseHandle->Read(pBuffer, nTotalToRead);
     296       35020 :         if (nReadInFile < nTotalToRead)
     297             :         {
     298        3971 :             if (m_poBaseHandle->Eof())
     299        3970 :                 bEOF = true;
     300             :             else
     301             :             {
     302           1 :                 CPLAssert(m_poBaseHandle->Error());
     303           1 :                 bError = true;
     304             :             }
     305             :         }
     306       35020 :         nBufferSize = static_cast<int>(
     307       35020 :             std::min(nReadInFile, static_cast<size_t>(MAX_BUFFER_SIZE)));
     308       35020 :         nBufferOffset = nCurOffset + nReadInFile - nBufferSize;
     309       35020 :         memcpy(pabyBuffer,
     310       35020 :                static_cast<GByte *>(pBuffer) + nReadInFile - nBufferSize,
     311       35020 :                nBufferSize);
     312             : 
     313       35020 :         nCurOffset += nReadInFile;
     314             : #ifdef DEBUG_VERBOSE
     315             :         CPLAssert(m_poBaseHandle->Tell() == nBufferOffset + nBufferSize);
     316             :         CPLAssert(m_poBaseHandle->Tell() == nCurOffset);
     317             : #endif
     318             : 
     319       35020 :         return nReadInFile;
     320             :     }
     321             : }
     322             : 
     323             : /************************************************************************/
     324             : /*                              Write()                                 */
     325             : /************************************************************************/
     326             : 
     327           0 : size_t VSIBufferedReaderHandle::Write(const void * /* pBuffer */,
     328             :                                       size_t /* nBytes */)
     329             : {
     330           0 :     CPLError(CE_Failure, CPLE_NotSupported,
     331             :              "VSIFWriteL is not supported on buffer reader streams");
     332           0 :     return 0;
     333             : }
     334             : 
     335             : /************************************************************************/
     336             : /*                             ClearErr()                               */
     337             : /************************************************************************/
     338             : 
     339         132 : void VSIBufferedReaderHandle::ClearErr()
     340             : 
     341             : {
     342         132 :     m_poBaseHandle->ClearErr();
     343         132 :     bEOF = false;
     344         132 :     bError = false;
     345         132 : }
     346             : 
     347             : /************************************************************************/
     348             : /*                               Eof()                                  */
     349             : /************************************************************************/
     350             : 
     351         360 : int VSIBufferedReaderHandle::Eof()
     352             : {
     353         360 :     return bEOF;
     354             : }
     355             : 
     356             : /************************************************************************/
     357             : /*                              Error()                                 */
     358             : /************************************************************************/
     359             : 
     360        3126 : int VSIBufferedReaderHandle::Error()
     361             : {
     362        3126 :     return bError;
     363             : }
     364             : 
     365             : /************************************************************************/
     366             : /*                              Flush()                                 */
     367             : /************************************************************************/
     368             : 
     369          16 : int VSIBufferedReaderHandle::Flush()
     370             : {
     371          16 :     return 0;
     372             : }
     373             : 
     374             : /************************************************************************/
     375             : /*                              Close()                                 */
     376             : /************************************************************************/
     377             : 
     378        5455 : int VSIBufferedReaderHandle::Close()
     379             : {
     380        5455 :     if (m_poBaseHandle)
     381             :     {
     382        5379 :         m_poBaseHandle->Close();
     383        5379 :         delete m_poBaseHandle;
     384        5379 :         m_poBaseHandle = nullptr;
     385             :     }
     386        5455 :     return 0;
     387             : }
     388             : 
     389             : //! @endcond

Generated by: LCOV version 1.14