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 124 80.6 %
Date: 2025-09-10 17:48:50 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 nSize, size_t nMemb) override;
      63             :     size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) 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        5335 : VSIVirtualHandle *VSICreateBufferedReaderHandle(VSIVirtualHandle *poBaseHandle)
      78             : {
      79        5335 :     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        5335 : VSIBufferedReaderHandle::VSIBufferedReaderHandle(VSIVirtualHandle *poBaseHandle)
      98             :     : m_poBaseHandle(poBaseHandle),
      99        5335 :       pabyBuffer(static_cast<GByte *>(CPLMalloc(MAX_BUFFER_SIZE)))
     100             : {
     101        5335 : }
     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       10682 : VSIBufferedReaderHandle::~VSIBufferedReaderHandle()
     121             : {
     122        5341 :     delete m_poBaseHandle;
     123        5341 :     CPLFree(pabyBuffer);
     124       10682 : }
     125             : 
     126             : /************************************************************************/
     127             : /*                               Seek()                                 */
     128             : /************************************************************************/
     129             : 
     130      233908 : 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      233908 :     bEOF = false;
     137      233908 :     int ret = 0;
     138      233908 :     if (nWhence == SEEK_CUR)
     139             :     {
     140        1445 :         nCurOffset += nOffset;
     141             :     }
     142      232463 :     else if (nWhence == SEEK_END)
     143             :     {
     144        2212 :         if (nCheatFileSize)
     145             :         {
     146           2 :             nCurOffset = nCheatFileSize;
     147             :         }
     148             :         else
     149             :         {
     150        2210 :             ret = m_poBaseHandle->Seek(nOffset, nWhence);
     151        2210 :             nCurOffset = m_poBaseHandle->Tell();
     152        2210 :             bNeedBaseHandleSeek = true;
     153             :         }
     154             :     }
     155             :     else
     156             :     {
     157      230251 :         nCurOffset = nOffset;
     158             :     }
     159             : 
     160      233908 :     return ret;
     161             : }
     162             : 
     163             : /************************************************************************/
     164             : /*                               Tell()                                 */
     165             : /************************************************************************/
     166             : 
     167      115907 : vsi_l_offset VSIBufferedReaderHandle::Tell()
     168             : {
     169             : #ifdef DEBUG_VERBOSE
     170             :     CPLDebug("BUFFERED", "Tell() = %d", static_cast<int>(nCurOffset));
     171             : #endif
     172      115907 :     return nCurOffset;
     173             : }
     174             : 
     175             : /************************************************************************/
     176             : /*                           SeekBaseTo()                               */
     177             : /************************************************************************/
     178             : 
     179       34985 : int VSIBufferedReaderHandle::SeekBaseTo(vsi_l_offset nTargetOffset)
     180             : {
     181       34985 :     if (m_poBaseHandle->Seek(nTargetOffset, SEEK_SET) == 0)
     182       34985 :         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      353262 : size_t VSIBufferedReaderHandle::Read(void *pBuffer, size_t nSize, size_t nMemb)
     218             : {
     219      353262 :     const size_t nTotalToRead = nSize * nMemb;
     220             : #ifdef DEBUG_VERBOSE
     221             :     CPLDebug("BUFFERED", "Read(%d)", static_cast<int>(nTotalToRead));
     222             : #endif
     223             : 
     224      353262 :     if (nSize == 0)
     225           0 :         return 0;
     226             : 
     227      353262 :     if (nBufferSize != 0 && nCurOffset >= nBufferOffset &&
     228      327633 :         nCurOffset <= nBufferOffset + nBufferSize)
     229             :     {
     230             :         // We try to read from an offset located within the buffer.
     231             :         const size_t nReadInBuffer = static_cast<size_t>(std::min(
     232             :             nTotalToRead,
     233      318384 :             static_cast<size_t>(nBufferOffset + nBufferSize - nCurOffset)));
     234      318384 :         memcpy(pBuffer, pabyBuffer + nCurOffset - nBufferOffset, nReadInBuffer);
     235      318384 :         const size_t nToReadInFile = nTotalToRead - nReadInBuffer;
     236      318384 :         if (nToReadInFile > 0)
     237             :         {
     238             :             // The beginning of the data to read is located in the buffer
     239             :             // but the end must be read from the file.
     240      311109 :             if (bNeedBaseHandleSeek)
     241             :             {
     242         107 :                 if (!SeekBaseTo(nBufferOffset + nBufferSize))
     243             :                 {
     244           0 :                     nCurOffset += nReadInBuffer;
     245           0 :                     return nReadInBuffer / nSize;
     246             :                 }
     247             :             }
     248      311109 :             bNeedBaseHandleSeek = false;
     249             : #ifdef DEBUG_VERBOSE
     250             :             CPLAssert(m_poBaseHandle->Tell() == nBufferOffset + nBufferSize);
     251             : #endif
     252             : 
     253      622218 :             const size_t nReadInFile = m_poBaseHandle->Read(
     254             :                 static_cast<GByte *>(pBuffer) + nReadInBuffer, 1,
     255      311109 :                 nToReadInFile);
     256      311109 :             if (nReadInFile < nToReadInFile)
     257             :             {
     258        5342 :                 if (m_poBaseHandle->Eof())
     259        5340 :                     bEOF = true;
     260             :                 else
     261             :                 {
     262           2 :                     CPLAssert(m_poBaseHandle->Error());
     263           2 :                     bError = true;
     264             :                 }
     265             :             }
     266      311109 :             const size_t nRead = nReadInBuffer + nReadInFile;
     267             : 
     268      311109 :             nBufferSize = static_cast<int>(
     269      311109 :                 std::min(nRead, static_cast<size_t>(MAX_BUFFER_SIZE)));
     270      311109 :             nBufferOffset = nCurOffset + nRead - nBufferSize;
     271      311109 :             memcpy(pabyBuffer,
     272      311109 :                    static_cast<GByte *>(pBuffer) + nRead - nBufferSize,
     273      311109 :                    nBufferSize);
     274             : 
     275      311109 :             nCurOffset += nRead;
     276             : #ifdef DEBUG_VERBOSE
     277             :             CPLAssert(m_poBaseHandle->Tell() == nBufferOffset + nBufferSize);
     278             :             CPLAssert(m_poBaseHandle->Tell() == nCurOffset);
     279             : #endif
     280             : 
     281      311109 :             return nRead / nSize;
     282             :         }
     283             :         else
     284             :         {
     285             :             // The data to read is completely located within the buffer.
     286        7275 :             nCurOffset += nTotalToRead;
     287        7275 :             return nTotalToRead / nSize;
     288             :         }
     289             :     }
     290             :     else
     291             :     {
     292             :         // We try either to read before or after the buffer, so a seek is
     293             :         // necessary.
     294       34878 :         if (!SeekBaseTo(nCurOffset))
     295           0 :             return 0;
     296       34878 :         bNeedBaseHandleSeek = false;
     297             :         const size_t nReadInFile =
     298       34878 :             m_poBaseHandle->Read(pBuffer, 1, nTotalToRead);
     299       34878 :         if (nReadInFile < nTotalToRead)
     300             :         {
     301        3900 :             if (m_poBaseHandle->Eof())
     302        3899 :                 bEOF = true;
     303             :             else
     304             :             {
     305           1 :                 CPLAssert(m_poBaseHandle->Error());
     306           1 :                 bError = true;
     307             :             }
     308             :         }
     309       34878 :         nBufferSize = static_cast<int>(
     310       34878 :             std::min(nReadInFile, static_cast<size_t>(MAX_BUFFER_SIZE)));
     311       34878 :         nBufferOffset = nCurOffset + nReadInFile - nBufferSize;
     312       34878 :         memcpy(pabyBuffer,
     313       34878 :                static_cast<GByte *>(pBuffer) + nReadInFile - nBufferSize,
     314       34878 :                nBufferSize);
     315             : 
     316       34878 :         nCurOffset += nReadInFile;
     317             : #ifdef DEBUG_VERBOSE
     318             :         CPLAssert(m_poBaseHandle->Tell() == nBufferOffset + nBufferSize);
     319             :         CPLAssert(m_poBaseHandle->Tell() == nCurOffset);
     320             : #endif
     321             : 
     322       34878 :         return nReadInFile / nSize;
     323             :     }
     324             : }
     325             : 
     326             : /************************************************************************/
     327             : /*                              Write()                                 */
     328             : /************************************************************************/
     329             : 
     330           0 : size_t VSIBufferedReaderHandle::Write(const void * /* pBuffer */,
     331             :                                       size_t /* nSize */, size_t /* nMemb */)
     332             : {
     333           0 :     CPLError(CE_Failure, CPLE_NotSupported,
     334             :              "VSIFWriteL is not supported on buffer reader streams");
     335           0 :     return 0;
     336             : }
     337             : 
     338             : /************************************************************************/
     339             : /*                             ClearErr()                               */
     340             : /************************************************************************/
     341             : 
     342          60 : void VSIBufferedReaderHandle::ClearErr()
     343             : 
     344             : {
     345          60 :     m_poBaseHandle->ClearErr();
     346          60 :     bEOF = false;
     347          60 :     bError = false;
     348          60 : }
     349             : 
     350             : /************************************************************************/
     351             : /*                               Eof()                                  */
     352             : /************************************************************************/
     353             : 
     354         356 : int VSIBufferedReaderHandle::Eof()
     355             : {
     356         356 :     return bEOF;
     357             : }
     358             : 
     359             : /************************************************************************/
     360             : /*                              Error()                                 */
     361             : /************************************************************************/
     362             : 
     363        3122 : int VSIBufferedReaderHandle::Error()
     364             : {
     365        3122 :     return bError;
     366             : }
     367             : 
     368             : /************************************************************************/
     369             : /*                              Flush()                                 */
     370             : /************************************************************************/
     371             : 
     372          16 : int VSIBufferedReaderHandle::Flush()
     373             : {
     374          16 :     return 0;
     375             : }
     376             : 
     377             : /************************************************************************/
     378             : /*                              Close()                                 */
     379             : /************************************************************************/
     380             : 
     381        5417 : int VSIBufferedReaderHandle::Close()
     382             : {
     383        5417 :     if (m_poBaseHandle)
     384             :     {
     385        5341 :         m_poBaseHandle->Close();
     386        5341 :         delete m_poBaseHandle;
     387        5341 :         m_poBaseHandle = nullptr;
     388             :     }
     389        5417 :     return 0;
     390             : }
     391             : 
     392             : //! @endcond

Generated by: LCOV version 1.14