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
|