Coverage Report

Created: 2026-02-14 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/port/cpl_vsil_buffered_reader.cpp
Line
Count
Source
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
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 nTotalToRead)
218
0
{
219
#ifdef DEBUG_VERBOSE
220
    CPLDebug("BUFFERED", "Read(%d)", static_cast<int>(nTotalToRead));
221
#endif
222
223
0
    if (nTotalToRead == 0)
224
0
        return 0;
225
226
0
    if (nBufferSize != 0 && nCurOffset >= nBufferOffset &&
227
0
        nCurOffset <= nBufferOffset + nBufferSize)
228
0
    {
229
        // We try to read from an offset located within the buffer.
230
0
        const size_t nReadInBuffer = static_cast<size_t>(std::min(
231
0
            nTotalToRead,
232
0
            static_cast<size_t>(nBufferOffset + nBufferSize - nCurOffset)));
233
0
        memcpy(pBuffer, pabyBuffer + nCurOffset - nBufferOffset, nReadInBuffer);
234
0
        const size_t nToReadInFile = nTotalToRead - nReadInBuffer;
235
0
        if (nToReadInFile > 0)
236
0
        {
237
            // The beginning of the data to read is located in the buffer
238
            // but the end must be read from the file.
239
0
            if (bNeedBaseHandleSeek)
240
0
            {
241
0
                if (!SeekBaseTo(nBufferOffset + nBufferSize))
242
0
                {
243
0
                    nCurOffset += nReadInBuffer;
244
0
                    return nReadInBuffer;
245
0
                }
246
0
            }
247
0
            bNeedBaseHandleSeek = false;
248
#ifdef DEBUG_VERBOSE
249
            CPLAssert(m_poBaseHandle->Tell() == nBufferOffset + nBufferSize);
250
#endif
251
252
0
            const size_t nReadInFile = m_poBaseHandle->Read(
253
0
                static_cast<GByte *>(pBuffer) + nReadInBuffer, nToReadInFile);
254
0
            if (nReadInFile < nToReadInFile)
255
0
            {
256
0
                if (m_poBaseHandle->Eof())
257
0
                    bEOF = true;
258
0
                else
259
0
                {
260
0
                    CPLAssert(m_poBaseHandle->Error());
261
0
                    bError = true;
262
0
                }
263
0
            }
264
0
            const size_t nRead = nReadInBuffer + nReadInFile;
265
266
0
            nBufferSize = static_cast<int>(
267
0
                std::min(nRead, static_cast<size_t>(MAX_BUFFER_SIZE)));
268
0
            nBufferOffset = nCurOffset + nRead - nBufferSize;
269
0
            memcpy(pabyBuffer,
270
0
                   static_cast<GByte *>(pBuffer) + nRead - nBufferSize,
271
0
                   nBufferSize);
272
273
0
            nCurOffset += nRead;
274
#ifdef DEBUG_VERBOSE
275
            CPLAssert(m_poBaseHandle->Tell() == nBufferOffset + nBufferSize);
276
            CPLAssert(m_poBaseHandle->Tell() == nCurOffset);
277
#endif
278
279
0
            return nRead;
280
0
        }
281
0
        else
282
0
        {
283
            // The data to read is completely located within the buffer.
284
0
            nCurOffset += nTotalToRead;
285
0
            return nTotalToRead;
286
0
        }
287
0
    }
288
0
    else
289
0
    {
290
        // We try either to read before or after the buffer, so a seek is
291
        // necessary.
292
0
        if (!SeekBaseTo(nCurOffset))
293
0
            return 0;
294
0
        bNeedBaseHandleSeek = false;
295
0
        const size_t nReadInFile = m_poBaseHandle->Read(pBuffer, nTotalToRead);
296
0
        if (nReadInFile < nTotalToRead)
297
0
        {
298
0
            if (m_poBaseHandle->Eof())
299
0
                bEOF = true;
300
0
            else
301
0
            {
302
0
                CPLAssert(m_poBaseHandle->Error());
303
0
                bError = true;
304
0
            }
305
0
        }
306
0
        nBufferSize = static_cast<int>(
307
0
            std::min(nReadInFile, static_cast<size_t>(MAX_BUFFER_SIZE)));
308
0
        nBufferOffset = nCurOffset + nReadInFile - nBufferSize;
309
0
        memcpy(pabyBuffer,
310
0
               static_cast<GByte *>(pBuffer) + nReadInFile - nBufferSize,
311
0
               nBufferSize);
312
313
0
        nCurOffset += nReadInFile;
314
#ifdef DEBUG_VERBOSE
315
        CPLAssert(m_poBaseHandle->Tell() == nBufferOffset + nBufferSize);
316
        CPLAssert(m_poBaseHandle->Tell() == nCurOffset);
317
#endif
318
319
0
        return nReadInFile;
320
0
    }
321
0
}
322
323
/************************************************************************/
324
/*                               Write()                                */
325
/************************************************************************/
326
327
size_t VSIBufferedReaderHandle::Write(const void * /* pBuffer */,
328
                                      size_t /* nBytes */)
329
0
{
330
0
    CPLError(CE_Failure, CPLE_NotSupported,
331
0
             "VSIFWriteL is not supported on buffer reader streams");
332
0
    return 0;
333
0
}
334
335
/************************************************************************/
336
/*                              ClearErr()                              */
337
/************************************************************************/
338
339
void VSIBufferedReaderHandle::ClearErr()
340
341
0
{
342
0
    m_poBaseHandle->ClearErr();
343
0
    bEOF = false;
344
0
    bError = false;
345
0
}
346
347
/************************************************************************/
348
/*                                Eof()                                 */
349
/************************************************************************/
350
351
int VSIBufferedReaderHandle::Eof()
352
0
{
353
0
    return bEOF;
354
0
}
355
356
/************************************************************************/
357
/*                               Error()                                */
358
/************************************************************************/
359
360
int VSIBufferedReaderHandle::Error()
361
0
{
362
0
    return bError;
363
0
}
364
365
/************************************************************************/
366
/*                               Flush()                                */
367
/************************************************************************/
368
369
int VSIBufferedReaderHandle::Flush()
370
0
{
371
0
    return 0;
372
0
}
373
374
/************************************************************************/
375
/*                               Close()                                */
376
/************************************************************************/
377
378
int VSIBufferedReaderHandle::Close()
379
0
{
380
0
    if (m_poBaseHandle)
381
0
    {
382
0
        m_poBaseHandle->Close();
383
0
        delete m_poBaseHandle;
384
0
        m_poBaseHandle = nullptr;
385
0
    }
386
0
    return 0;
387
0
}
388
389
//! @endcond