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