Coverage Report

Created: 2025-08-28 06:57

/src/gdal/port/cpl_vsil_buffered_reader.cpp
Line
Count
Source (jump to first uncovered line)
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
VSIVirtualHandle *VSICreateBufferedReaderHandle(VSIVirtualHandle *poBaseHandle)
78
0
{
79
0
    return new VSIBufferedReaderHandle(poBaseHandle);
80
0
}
81
82
VSIVirtualHandle *
83
VSICreateBufferedReaderHandle(VSIVirtualHandle *poBaseHandle,
84
                              const GByte *pabyBeginningContent,
85
                              vsi_l_offset nCheatFileSizeIn)
86
0
{
87
0
    return new VSIBufferedReaderHandle(poBaseHandle, pabyBeginningContent,
88
0
                                       nCheatFileSizeIn);
89
0
}
90
91
//! @cond Doxygen_Suppress
92
93
/************************************************************************/
94
/*                        VSIBufferedReaderHandle()                     */
95
/************************************************************************/
96
97
VSIBufferedReaderHandle::VSIBufferedReaderHandle(VSIVirtualHandle *poBaseHandle)
98
0
    : m_poBaseHandle(poBaseHandle),
99
0
      pabyBuffer(static_cast<GByte *>(CPLMalloc(MAX_BUFFER_SIZE)))
100
0
{
101
0
}
102
103
VSIBufferedReaderHandle::VSIBufferedReaderHandle(
104
    VSIVirtualHandle *poBaseHandle, const GByte *pabyBeginningContent,
105
    vsi_l_offset nCheatFileSizeIn)
106
0
    : m_poBaseHandle(poBaseHandle),
107
0
      pabyBuffer(static_cast<GByte *>(CPLMalloc(
108
0
          std::max(MAX_BUFFER_SIZE, static_cast<int>(poBaseHandle->Tell()))))),
109
0
      nBufferOffset(0), nBufferSize(static_cast<int>(poBaseHandle->Tell())),
110
0
      nCurOffset(0), bNeedBaseHandleSeek(true), bEOF(false),
111
0
      nCheatFileSize(nCheatFileSizeIn)
112
0
{
113
0
    memcpy(pabyBuffer, pabyBeginningContent, nBufferSize);
114
0
}
115
116
/************************************************************************/
117
/*                        ~VSIBufferedReaderHandle()                    */
118
/************************************************************************/
119
120
VSIBufferedReaderHandle::~VSIBufferedReaderHandle()
121
0
{
122
0
    delete m_poBaseHandle;
123
0
    CPLFree(pabyBuffer);
124
0
}
125
126
/************************************************************************/
127
/*                               Seek()                                 */
128
/************************************************************************/
129
130
int VSIBufferedReaderHandle::Seek(vsi_l_offset nOffset, int nWhence)
131
0
{
132
#ifdef DEBUG_VERBOSE
133
    CPLDebug("BUFFERED", "Seek(%d,%d)", static_cast<int>(nOffset),
134
             static_cast<int>(nWhence));
135
#endif
136
0
    bEOF = false;
137
0
    int ret = 0;
138
0
    if (nWhence == SEEK_CUR)
139
0
    {
140
0
        nCurOffset += nOffset;
141
0
    }
142
0
    else if (nWhence == SEEK_END)
143
0
    {
144
0
        if (nCheatFileSize)
145
0
        {
146
0
            nCurOffset = nCheatFileSize;
147
0
        }
148
0
        else
149
0
        {
150
0
            ret = m_poBaseHandle->Seek(nOffset, nWhence);
151
0
            nCurOffset = m_poBaseHandle->Tell();
152
0
            bNeedBaseHandleSeek = true;
153
0
        }
154
0
    }
155
0
    else
156
0
    {
157
0
        nCurOffset = nOffset;
158
0
    }
159
160
0
    return ret;
161
0
}
162
163
/************************************************************************/
164
/*                               Tell()                                 */
165
/************************************************************************/
166
167
vsi_l_offset VSIBufferedReaderHandle::Tell()
168
0
{
169
#ifdef DEBUG_VERBOSE
170
    CPLDebug("BUFFERED", "Tell() = %d", static_cast<int>(nCurOffset));
171
#endif
172
0
    return nCurOffset;
173
0
}
174
175
/************************************************************************/
176
/*                           SeekBaseTo()                               */
177
/************************************************************************/
178
179
int VSIBufferedReaderHandle::SeekBaseTo(vsi_l_offset nTargetOffset)
180
0
{
181
0
    if (m_poBaseHandle->Seek(nTargetOffset, SEEK_SET) == 0)
182
0
        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
0
    while (true)
194
0
    {
195
0
        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
0
        {
203
0
            bEOF = CPL_TO_BOOL(m_poBaseHandle->Eof());
204
0
            bError = CPL_TO_BOOL(m_poBaseHandle->Error());
205
0
            return FALSE;
206
0
        }
207
0
        if (nToRead < nMaxOffset)
208
0
            break;
209
0
    }
210
0
    return TRUE;
211
0
}
212
213
/************************************************************************/
214
/*                               Read()                                 */
215
/************************************************************************/
216
217
size_t VSIBufferedReaderHandle::Read(void *pBuffer, size_t nSize, size_t nMemb)
218
0
{
219
0
    const size_t nTotalToRead = nSize * nMemb;
220
#ifdef DEBUG_VERBOSE
221
    CPLDebug("BUFFERED", "Read(%d)", static_cast<int>(nTotalToRead));
222
#endif
223
224
0
    if (nSize == 0)
225
0
        return 0;
226
227
0
    if (nBufferSize != 0 && nCurOffset >= nBufferOffset &&
228
0
        nCurOffset <= nBufferOffset + nBufferSize)
229
0
    {
230
        // We try to read from an offset located within the buffer.
231
0
        const size_t nReadInBuffer = static_cast<size_t>(std::min(
232
0
            nTotalToRead,
233
0
            static_cast<size_t>(nBufferOffset + nBufferSize - nCurOffset)));
234
0
        memcpy(pBuffer, pabyBuffer + nCurOffset - nBufferOffset, nReadInBuffer);
235
0
        const size_t nToReadInFile = nTotalToRead - nReadInBuffer;
236
0
        if (nToReadInFile > 0)
237
0
        {
238
            // The beginning of the data to read is located in the buffer
239
            // but the end must be read from the file.
240
0
            if (bNeedBaseHandleSeek)
241
0
            {
242
0
                if (!SeekBaseTo(nBufferOffset + nBufferSize))
243
0
                {
244
0
                    nCurOffset += nReadInBuffer;
245
0
                    return nReadInBuffer / nSize;
246
0
                }
247
0
            }
248
0
            bNeedBaseHandleSeek = false;
249
#ifdef DEBUG_VERBOSE
250
            CPLAssert(m_poBaseHandle->Tell() == nBufferOffset + nBufferSize);
251
#endif
252
253
0
            const size_t nReadInFile = m_poBaseHandle->Read(
254
0
                static_cast<GByte *>(pBuffer) + nReadInBuffer, 1,
255
0
                nToReadInFile);
256
0
            if (nReadInFile < nToReadInFile)
257
0
            {
258
0
                if (m_poBaseHandle->Eof())
259
0
                    bEOF = true;
260
0
                else
261
0
                {
262
0
                    CPLAssert(m_poBaseHandle->Error());
263
0
                    bError = true;
264
0
                }
265
0
            }
266
0
            const size_t nRead = nReadInBuffer + nReadInFile;
267
268
0
            nBufferSize = static_cast<int>(
269
0
                std::min(nRead, static_cast<size_t>(MAX_BUFFER_SIZE)));
270
0
            nBufferOffset = nCurOffset + nRead - nBufferSize;
271
0
            memcpy(pabyBuffer,
272
0
                   static_cast<GByte *>(pBuffer) + nRead - nBufferSize,
273
0
                   nBufferSize);
274
275
0
            nCurOffset += nRead;
276
#ifdef DEBUG_VERBOSE
277
            CPLAssert(m_poBaseHandle->Tell() == nBufferOffset + nBufferSize);
278
            CPLAssert(m_poBaseHandle->Tell() == nCurOffset);
279
#endif
280
281
0
            return nRead / nSize;
282
0
        }
283
0
        else
284
0
        {
285
            // The data to read is completely located within the buffer.
286
0
            nCurOffset += nTotalToRead;
287
0
            return nTotalToRead / nSize;
288
0
        }
289
0
    }
290
0
    else
291
0
    {
292
        // We try either to read before or after the buffer, so a seek is
293
        // necessary.
294
0
        if (!SeekBaseTo(nCurOffset))
295
0
            return 0;
296
0
        bNeedBaseHandleSeek = false;
297
0
        const size_t nReadInFile =
298
0
            m_poBaseHandle->Read(pBuffer, 1, nTotalToRead);
299
0
        if (nReadInFile < nTotalToRead)
300
0
        {
301
0
            if (m_poBaseHandle->Eof())
302
0
                bEOF = true;
303
0
            else
304
0
            {
305
0
                CPLAssert(m_poBaseHandle->Error());
306
0
                bError = true;
307
0
            }
308
0
        }
309
0
        nBufferSize = static_cast<int>(
310
0
            std::min(nReadInFile, static_cast<size_t>(MAX_BUFFER_SIZE)));
311
0
        nBufferOffset = nCurOffset + nReadInFile - nBufferSize;
312
0
        memcpy(pabyBuffer,
313
0
               static_cast<GByte *>(pBuffer) + nReadInFile - nBufferSize,
314
0
               nBufferSize);
315
316
0
        nCurOffset += nReadInFile;
317
#ifdef DEBUG_VERBOSE
318
        CPLAssert(m_poBaseHandle->Tell() == nBufferOffset + nBufferSize);
319
        CPLAssert(m_poBaseHandle->Tell() == nCurOffset);
320
#endif
321
322
0
        return nReadInFile / nSize;
323
0
    }
324
0
}
325
326
/************************************************************************/
327
/*                              Write()                                 */
328
/************************************************************************/
329
330
size_t VSIBufferedReaderHandle::Write(const void * /* pBuffer */,
331
                                      size_t /* nSize */, size_t /* nMemb */)
332
0
{
333
0
    CPLError(CE_Failure, CPLE_NotSupported,
334
0
             "VSIFWriteL is not supported on buffer reader streams");
335
0
    return 0;
336
0
}
337
338
/************************************************************************/
339
/*                             ClearErr()                               */
340
/************************************************************************/
341
342
void VSIBufferedReaderHandle::ClearErr()
343
344
0
{
345
0
    m_poBaseHandle->ClearErr();
346
0
    bEOF = false;
347
0
    bError = false;
348
0
}
349
350
/************************************************************************/
351
/*                               Eof()                                  */
352
/************************************************************************/
353
354
int VSIBufferedReaderHandle::Eof()
355
0
{
356
0
    return bEOF;
357
0
}
358
359
/************************************************************************/
360
/*                              Error()                                 */
361
/************************************************************************/
362
363
int VSIBufferedReaderHandle::Error()
364
0
{
365
0
    return bError;
366
0
}
367
368
/************************************************************************/
369
/*                              Flush()                                 */
370
/************************************************************************/
371
372
int VSIBufferedReaderHandle::Flush()
373
0
{
374
0
    return 0;
375
0
}
376
377
/************************************************************************/
378
/*                              Close()                                 */
379
/************************************************************************/
380
381
int VSIBufferedReaderHandle::Close()
382
0
{
383
0
    if (m_poBaseHandle)
384
0
    {
385
0
        m_poBaseHandle->Close();
386
0
        delete m_poBaseHandle;
387
0
        m_poBaseHandle = nullptr;
388
0
    }
389
0
    return 0;
390
0
}
391
392
//! @endcond