Coverage Report

Created: 2025-06-13 06:29

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