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 5373 : VSIVirtualHandle *VSICreateBufferedReaderHandle(VSIVirtualHandle *poBaseHandle)
78 : {
79 5373 : 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 5373 : VSIBufferedReaderHandle::VSIBufferedReaderHandle(VSIVirtualHandle *poBaseHandle)
98 : : m_poBaseHandle(poBaseHandle),
99 5373 : pabyBuffer(static_cast<GByte *>(CPLMalloc(MAX_BUFFER_SIZE)))
100 : {
101 5373 : }
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 10758 : VSIBufferedReaderHandle::~VSIBufferedReaderHandle()
121 : {
122 5379 : delete m_poBaseHandle;
123 5379 : CPLFree(pabyBuffer);
124 10758 : }
125 :
126 : /************************************************************************/
127 : /* Seek() */
128 : /************************************************************************/
129 :
130 235764 : 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 235764 : bEOF = false;
137 235764 : int ret = 0;
138 235764 : if (nWhence == SEEK_CUR)
139 : {
140 1446 : nCurOffset += nOffset;
141 : }
142 234318 : else if (nWhence == SEEK_END)
143 : {
144 2261 : if (nCheatFileSize)
145 : {
146 2 : nCurOffset = nCheatFileSize;
147 : }
148 : else
149 : {
150 2259 : ret = m_poBaseHandle->Seek(nOffset, nWhence);
151 2259 : nCurOffset = m_poBaseHandle->Tell();
152 2259 : bNeedBaseHandleSeek = true;
153 : }
154 : }
155 : else
156 : {
157 232057 : nCurOffset = nOffset;
158 : }
159 :
160 235764 : return ret;
161 : }
162 :
163 : /************************************************************************/
164 : /* Tell() */
165 : /************************************************************************/
166 :
167 117955 : vsi_l_offset VSIBufferedReaderHandle::Tell()
168 : {
169 : #ifdef DEBUG_VERBOSE
170 : CPLDebug("BUFFERED", "Tell() = %d", static_cast<int>(nCurOffset));
171 : #endif
172 117955 : return nCurOffset;
173 : }
174 :
175 : /************************************************************************/
176 : /* SeekBaseTo() */
177 : /************************************************************************/
178 :
179 35134 : int VSIBufferedReaderHandle::SeekBaseTo(vsi_l_offset nTargetOffset)
180 : {
181 35134 : if (m_poBaseHandle->Seek(nTargetOffset, SEEK_SET) == 0)
182 35134 : 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 354984 : 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 354984 : if (nTotalToRead == 0)
224 12 : return 0;
225 :
226 354972 : if (nBufferSize != 0 && nCurOffset >= nBufferOffset &&
227 329204 : 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 319952 : static_cast<size_t>(nBufferOffset + nBufferSize - nCurOffset)));
233 319952 : memcpy(pBuffer, pabyBuffer + nCurOffset - nBufferOffset, nReadInBuffer);
234 319952 : const size_t nToReadInFile = nTotalToRead - nReadInBuffer;
235 319952 : 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 312565 : if (bNeedBaseHandleSeek)
240 : {
241 114 : if (!SeekBaseTo(nBufferOffset + nBufferSize))
242 : {
243 0 : nCurOffset += nReadInBuffer;
244 0 : return nReadInBuffer;
245 : }
246 : }
247 312565 : bNeedBaseHandleSeek = false;
248 : #ifdef DEBUG_VERBOSE
249 : CPLAssert(m_poBaseHandle->Tell() == nBufferOffset + nBufferSize);
250 : #endif
251 :
252 625130 : const size_t nReadInFile = m_poBaseHandle->Read(
253 312565 : static_cast<GByte *>(pBuffer) + nReadInBuffer, nToReadInFile);
254 312565 : if (nReadInFile < nToReadInFile)
255 : {
256 5461 : if (m_poBaseHandle->Eof())
257 5459 : bEOF = true;
258 : else
259 : {
260 2 : CPLAssert(m_poBaseHandle->Error());
261 2 : bError = true;
262 : }
263 : }
264 312565 : const size_t nRead = nReadInBuffer + nReadInFile;
265 :
266 312565 : nBufferSize = static_cast<int>(
267 312565 : std::min(nRead, static_cast<size_t>(MAX_BUFFER_SIZE)));
268 312565 : nBufferOffset = nCurOffset + nRead - nBufferSize;
269 312565 : memcpy(pabyBuffer,
270 312565 : static_cast<GByte *>(pBuffer) + nRead - nBufferSize,
271 312565 : nBufferSize);
272 :
273 312565 : nCurOffset += nRead;
274 : #ifdef DEBUG_VERBOSE
275 : CPLAssert(m_poBaseHandle->Tell() == nBufferOffset + nBufferSize);
276 : CPLAssert(m_poBaseHandle->Tell() == nCurOffset);
277 : #endif
278 :
279 312565 : return nRead;
280 : }
281 : else
282 : {
283 : // The data to read is completely located within the buffer.
284 7387 : nCurOffset += nTotalToRead;
285 7387 : 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 35020 : if (!SeekBaseTo(nCurOffset))
293 0 : return 0;
294 35020 : bNeedBaseHandleSeek = false;
295 35020 : const size_t nReadInFile = m_poBaseHandle->Read(pBuffer, nTotalToRead);
296 35020 : 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 35020 : nBufferSize = static_cast<int>(
307 35020 : std::min(nReadInFile, static_cast<size_t>(MAX_BUFFER_SIZE)));
308 35020 : nBufferOffset = nCurOffset + nReadInFile - nBufferSize;
309 35020 : memcpy(pabyBuffer,
310 35020 : static_cast<GByte *>(pBuffer) + nReadInFile - nBufferSize,
311 35020 : nBufferSize);
312 :
313 35020 : nCurOffset += nReadInFile;
314 : #ifdef DEBUG_VERBOSE
315 : CPLAssert(m_poBaseHandle->Tell() == nBufferOffset + nBufferSize);
316 : CPLAssert(m_poBaseHandle->Tell() == nCurOffset);
317 : #endif
318 :
319 35020 : 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 360 : int VSIBufferedReaderHandle::Eof()
352 : {
353 360 : 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 5455 : int VSIBufferedReaderHandle::Close()
379 : {
380 5455 : if (m_poBaseHandle)
381 : {
382 5379 : m_poBaseHandle->Close();
383 5379 : delete m_poBaseHandle;
384 5379 : m_poBaseHandle = nullptr;
385 : }
386 5455 : return 0;
387 : }
388 :
389 : //! @endcond
|