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