Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: VSI Virtual File System
4 : * Purpose: Implementation of caching IO layer.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2011, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2011-2014, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "cpl_port.h"
31 : #include "cpl_vsi_virtual.h"
32 :
33 : #include <cstddef>
34 : #include <cstring>
35 : #if HAVE_FCNTL_H
36 : #include <fcntl.h>
37 : #endif
38 :
39 : #include <algorithm>
40 : #include <limits>
41 : #include <map>
42 : #include <memory>
43 : #include <utility>
44 :
45 : #include "cpl_conv.h"
46 : #include "cpl_error.h"
47 : #include "cpl_vsi.h"
48 : #include "cpl_vsi_virtual.h"
49 : #include "cpl_mem_cache.h"
50 : #include "cpl_noncopyablevector.h"
51 :
52 : //! @cond Doxygen_Suppress
53 :
54 : /************************************************************************/
55 : /* ==================================================================== */
56 : /* VSICachedFile */
57 : /* ==================================================================== */
58 : /************************************************************************/
59 :
60 : class VSICachedFile final : public VSIVirtualHandle
61 : {
62 : CPL_DISALLOW_COPY_ASSIGN(VSICachedFile)
63 :
64 : public:
65 : VSICachedFile(VSIVirtualHandle *poBaseHandle, size_t nChunkSize,
66 : size_t nCacheSize);
67 :
68 50 : ~VSICachedFile() override
69 25 : {
70 25 : VSICachedFile::Close();
71 50 : }
72 :
73 : bool LoadBlocks(vsi_l_offset nStartBlock, size_t nBlockCount, void *pBuffer,
74 : size_t nBufferSize);
75 :
76 : VSIVirtualHandleUniquePtr m_poBase{};
77 :
78 : vsi_l_offset m_nOffset = 0;
79 : vsi_l_offset m_nFileSize = 0;
80 :
81 : size_t m_nChunkSize = 0;
82 : lru11::Cache<vsi_l_offset, cpl::NonCopyableVector<GByte>>
83 : m_oCache; // can only been initialized in constructor
84 :
85 : bool m_bEOF = false;
86 :
87 : int Seek(vsi_l_offset nOffset, int nWhence) override;
88 : vsi_l_offset Tell() override;
89 : size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
90 : int ReadMultiRange(int nRanges, void **ppData,
91 : const vsi_l_offset *panOffsets,
92 : const size_t *panSizes) override;
93 :
94 0 : void AdviseRead(int nRanges, const vsi_l_offset *panOffsets,
95 : const size_t *panSizes) override
96 : {
97 0 : m_poBase->AdviseRead(nRanges, panOffsets, panSizes);
98 0 : }
99 :
100 : size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
101 : int Eof() override;
102 : int Flush() override;
103 : int Close() override;
104 :
105 0 : void *GetNativeFileDescriptor() override
106 : {
107 0 : return m_poBase->GetNativeFileDescriptor();
108 : }
109 :
110 0 : bool HasPRead() const override
111 : {
112 0 : return m_poBase->HasPRead();
113 : }
114 :
115 0 : size_t PRead(void *pBuffer, size_t nSize,
116 : vsi_l_offset nOffset) const override
117 : {
118 0 : return m_poBase->PRead(pBuffer, nSize, nOffset);
119 : }
120 : };
121 :
122 : /************************************************************************/
123 : /* GetCacheMax() */
124 : /************************************************************************/
125 :
126 25 : static size_t GetCacheMax(size_t nCacheSize)
127 : {
128 50 : return nCacheSize ? nCacheSize
129 : : static_cast<size_t>(std::min(
130 0 : static_cast<GUIntBig>(
131 50 : std::numeric_limits<size_t>::max() / 2),
132 25 : CPLScanUIntBig(CPLGetConfigOption("VSI_CACHE_SIZE",
133 : "25000000"),
134 50 : 40)));
135 : }
136 :
137 : /************************************************************************/
138 : /* DIV_ROUND_UP() */
139 : /************************************************************************/
140 :
141 25 : template <class T> inline T DIV_ROUND_UP(T a, T b)
142 : {
143 25 : return a / b + (((a % b) == 0) ? 0 : 1);
144 : }
145 :
146 : /************************************************************************/
147 : /* VSICachedFile() */
148 : /************************************************************************/
149 :
150 25 : VSICachedFile::VSICachedFile(VSIVirtualHandle *poBaseHandle, size_t nChunkSize,
151 25 : size_t nCacheSize)
152 : : m_poBase(poBaseHandle),
153 25 : m_nChunkSize(nChunkSize ? nChunkSize : VSI_CACHED_DEFAULT_CHUNK_SIZE),
154 50 : m_oCache{DIV_ROUND_UP(GetCacheMax(nCacheSize), m_nChunkSize), 0}
155 : {
156 25 : m_poBase->Seek(0, SEEK_END);
157 25 : m_nFileSize = m_poBase->Tell();
158 25 : }
159 :
160 : /************************************************************************/
161 : /* Close() */
162 : /************************************************************************/
163 :
164 49 : int VSICachedFile::Close()
165 :
166 : {
167 49 : m_oCache.clear();
168 49 : m_poBase.reset();
169 :
170 49 : return 0;
171 : }
172 :
173 : /************************************************************************/
174 : /* Seek() */
175 : /************************************************************************/
176 :
177 8334 : int VSICachedFile::Seek(vsi_l_offset nReqOffset, int nWhence)
178 :
179 : {
180 8334 : m_bEOF = false;
181 :
182 8334 : if (nWhence == SEEK_SET)
183 : {
184 : // Use offset directly.
185 : }
186 7967 : else if (nWhence == SEEK_CUR)
187 : {
188 7937 : nReqOffset += m_nOffset;
189 : }
190 30 : else if (nWhence == SEEK_END)
191 : {
192 30 : nReqOffset += m_nFileSize;
193 : }
194 :
195 8334 : m_nOffset = nReqOffset;
196 :
197 8334 : return 0;
198 : }
199 :
200 : /************************************************************************/
201 : /* Tell() */
202 : /************************************************************************/
203 :
204 7998 : vsi_l_offset VSICachedFile::Tell()
205 :
206 : {
207 7998 : return m_nOffset;
208 : }
209 :
210 : /************************************************************************/
211 : /* LoadBlocks() */
212 : /* */
213 : /* Load the desired set of blocks. Use pBuffer as a temporary */
214 : /* buffer if it would be helpful. */
215 : /************************************************************************/
216 :
217 278 : bool VSICachedFile::LoadBlocks(vsi_l_offset nStartBlock, size_t nBlockCount,
218 : void *pBuffer, size_t nBufferSize)
219 :
220 : {
221 278 : if (nBlockCount == 0)
222 0 : return true;
223 :
224 : /* -------------------------------------------------------------------- */
225 : /* When we want to load only one block, we can directly load it */
226 : /* into the target buffer with no concern about intermediaries. */
227 : /* -------------------------------------------------------------------- */
228 278 : if (nBlockCount == 1)
229 : {
230 253 : if (m_poBase->Seek(static_cast<vsi_l_offset>(nStartBlock) *
231 253 : m_nChunkSize,
232 253 : SEEK_SET) != 0)
233 : {
234 0 : return false;
235 : }
236 :
237 : try
238 : {
239 253 : cpl::NonCopyableVector<GByte> oData(m_nChunkSize);
240 : const auto nDataRead =
241 253 : m_poBase->Read(oData.data(), 1, m_nChunkSize);
242 253 : if (nDataRead == 0)
243 6 : return false;
244 247 : oData.resize(nDataRead);
245 :
246 247 : m_oCache.insert(nStartBlock, std::move(oData));
247 : }
248 0 : catch (const std::exception &)
249 : {
250 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
251 : "Out of memory situation in VSICachedFile::LoadBlocks()");
252 0 : return false;
253 : }
254 :
255 247 : return true;
256 : }
257 :
258 : /* -------------------------------------------------------------------- */
259 : /* If the buffer is quite large but not quite large enough to */
260 : /* hold all the blocks we will take the pain of splitting the */
261 : /* io request in two in order to avoid allocating a large */
262 : /* temporary buffer. */
263 : /* -------------------------------------------------------------------- */
264 25 : if (nBufferSize > m_nChunkSize * 20 &&
265 14 : nBufferSize < nBlockCount * m_nChunkSize)
266 : {
267 0 : if (!LoadBlocks(nStartBlock, 2, pBuffer, nBufferSize))
268 0 : return false;
269 :
270 0 : return LoadBlocks(nStartBlock + 2, nBlockCount - 2, pBuffer,
271 0 : nBufferSize);
272 : }
273 :
274 25 : if (m_poBase->Seek(static_cast<vsi_l_offset>(nStartBlock) * m_nChunkSize,
275 25 : SEEK_SET) != 0)
276 0 : return false;
277 :
278 : /* -------------------------------------------------------------------- */
279 : /* Do we need to allocate our own buffer? */
280 : /* -------------------------------------------------------------------- */
281 25 : GByte *pabyWorkBuffer = static_cast<GByte *>(pBuffer);
282 :
283 25 : if (nBufferSize < m_nChunkSize * nBlockCount)
284 : {
285 : pabyWorkBuffer = static_cast<GByte *>(
286 3 : VSI_MALLOC_VERBOSE(m_nChunkSize * nBlockCount));
287 3 : if (pabyWorkBuffer == nullptr)
288 0 : return false;
289 : }
290 :
291 : /* -------------------------------------------------------------------- */
292 : /* Read the whole request into the working buffer. */
293 : /* -------------------------------------------------------------------- */
294 :
295 : const size_t nDataRead =
296 25 : m_poBase->Read(pabyWorkBuffer, 1, nBlockCount * m_nChunkSize);
297 :
298 25 : bool ret = true;
299 25 : if (nBlockCount * m_nChunkSize > nDataRead + m_nChunkSize - 1)
300 : {
301 5 : size_t nNewBlockCount = (nDataRead + m_nChunkSize - 1) / m_nChunkSize;
302 5 : if (nNewBlockCount < nBlockCount)
303 : {
304 5 : nBlockCount = nNewBlockCount;
305 5 : ret = false;
306 : }
307 : }
308 :
309 511 : for (size_t i = 0; i < nBlockCount; i++)
310 : {
311 486 : const vsi_l_offset iBlock = nStartBlock + i;
312 :
313 972 : const auto nDataFilled = (nDataRead >= (i + 1) * m_nChunkSize)
314 486 : ? m_nChunkSize
315 3 : : nDataRead - i * m_nChunkSize;
316 : try
317 : {
318 972 : cpl::NonCopyableVector<GByte> oData(nDataFilled);
319 :
320 486 : memcpy(oData.data(), pabyWorkBuffer + i * m_nChunkSize,
321 : nDataFilled);
322 :
323 486 : m_oCache.insert(iBlock, std::move(oData));
324 : }
325 0 : catch (const std::exception &)
326 : {
327 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
328 : "Out of memory situation in VSICachedFile::LoadBlocks()");
329 0 : ret = false;
330 0 : break;
331 : }
332 : }
333 :
334 25 : if (pabyWorkBuffer != pBuffer)
335 3 : CPLFree(pabyWorkBuffer);
336 :
337 25 : return ret;
338 : }
339 :
340 : /************************************************************************/
341 : /* Read() */
342 : /************************************************************************/
343 :
344 9171 : size_t VSICachedFile::Read(void *pBuffer, size_t nSize, size_t nCount)
345 :
346 : {
347 9171 : if (nSize == 0 || nCount == 0)
348 0 : return 0;
349 9171 : const size_t nRequestedBytes = nSize * nCount;
350 :
351 : // nFileSize might be set wrongly to 0 by underlying layers, such as
352 : // /vsicurl_streaming/https://query.data.world/s/jgsghstpphjhicstradhy5kpjwrnfy
353 9171 : if (m_nFileSize > 0 && m_nOffset >= m_nFileSize)
354 : {
355 4 : m_bEOF = true;
356 4 : return 0;
357 : }
358 :
359 : /* ==================================================================== */
360 : /* Make sure the cache is loaded for the whole request region. */
361 : /* ==================================================================== */
362 9167 : const vsi_l_offset nStartBlock = m_nOffset / m_nChunkSize;
363 : // Calculate last block
364 9167 : const vsi_l_offset nLastBlock = m_nFileSize / m_nChunkSize;
365 9167 : vsi_l_offset nEndBlock = (m_nOffset + nRequestedBytes - 1) / m_nChunkSize;
366 :
367 : // if nLastBlock is not 0 consider the min value to avoid out-of-range reads
368 9167 : if (nLastBlock != 0 && nEndBlock > nLastBlock)
369 : {
370 6 : nEndBlock = nLastBlock;
371 : }
372 :
373 18717 : for (vsi_l_offset iBlock = nStartBlock; iBlock <= nEndBlock; iBlock++)
374 : {
375 9556 : if (!m_oCache.contains(iBlock))
376 : {
377 229 : size_t nBlocksToLoad = 1;
378 1199 : while (iBlock + nBlocksToLoad <= nEndBlock &&
379 1199 : !m_oCache.contains(iBlock + nBlocksToLoad))
380 : {
381 481 : nBlocksToLoad++;
382 : }
383 :
384 229 : if (!LoadBlocks(iBlock, nBlocksToLoad, pBuffer, nRequestedBytes))
385 6 : break;
386 : }
387 : }
388 :
389 : /* ==================================================================== */
390 : /* Copy data into the target buffer to the extent possible. */
391 : /* ==================================================================== */
392 9167 : size_t nAmountCopied = 0;
393 :
394 18819 : while (nAmountCopied < nRequestedBytes)
395 : {
396 9664 : const vsi_l_offset iBlock = (m_nOffset + nAmountCopied) / m_nChunkSize;
397 9664 : const cpl::NonCopyableVector<GByte> *poData = m_oCache.getPtr(iBlock);
398 9664 : if (poData == nullptr)
399 : {
400 : // We can reach that point when the amount to read exceeds
401 : // the cache size.
402 49 : LoadBlocks(iBlock, 1, static_cast<GByte *>(pBuffer) + nAmountCopied,
403 49 : std::min(nRequestedBytes - nAmountCopied, m_nChunkSize));
404 49 : poData = m_oCache.getPtr(iBlock);
405 49 : if (poData == nullptr)
406 : {
407 12 : break;
408 : }
409 : }
410 :
411 9659 : const vsi_l_offset nStartOffset =
412 9659 : static_cast<vsi_l_offset>(iBlock) * m_nChunkSize;
413 9659 : if (nStartOffset + poData->size() < nAmountCopied + m_nOffset)
414 1 : break;
415 : const size_t nThisCopy =
416 19316 : std::min(nRequestedBytes - nAmountCopied,
417 9658 : static_cast<size_t>(((nStartOffset + poData->size()) -
418 9658 : nAmountCopied - m_nOffset)));
419 9658 : if (nThisCopy == 0)
420 6 : break;
421 :
422 9652 : memcpy(static_cast<GByte *>(pBuffer) + nAmountCopied,
423 9652 : poData->data() + (m_nOffset + nAmountCopied) - nStartOffset,
424 : nThisCopy);
425 :
426 9652 : nAmountCopied += nThisCopy;
427 : }
428 :
429 9167 : m_nOffset += nAmountCopied;
430 :
431 9167 : const size_t nRet = nAmountCopied / nSize;
432 9167 : if (nRet != nCount)
433 12 : m_bEOF = true;
434 9167 : return nRet;
435 : }
436 :
437 : /************************************************************************/
438 : /* ReadMultiRange() */
439 : /************************************************************************/
440 :
441 0 : int VSICachedFile::ReadMultiRange(int const nRanges, void **const ppData,
442 : const vsi_l_offset *const panOffsets,
443 : const size_t *const panSizes)
444 : {
445 : // If the base is /vsicurl/
446 0 : return m_poBase->ReadMultiRange(nRanges, ppData, panOffsets, panSizes);
447 : }
448 :
449 : /************************************************************************/
450 : /* Write() */
451 : /************************************************************************/
452 :
453 0 : size_t VSICachedFile::Write(const void * /* pBuffer */, size_t /*nSize */,
454 : size_t /* nCount */)
455 : {
456 0 : return 0;
457 : }
458 :
459 : /************************************************************************/
460 : /* Eof() */
461 : /************************************************************************/
462 :
463 0 : int VSICachedFile::Eof()
464 :
465 : {
466 0 : return m_bEOF;
467 : }
468 :
469 : /************************************************************************/
470 : /* Flush() */
471 : /************************************************************************/
472 :
473 0 : int VSICachedFile::Flush()
474 :
475 : {
476 0 : return 0;
477 : }
478 :
479 : /************************************************************************/
480 : /* VSICachedFilesystemHandler */
481 : /************************************************************************/
482 :
483 : class VSICachedFilesystemHandler final : public VSIFilesystemHandler
484 : {
485 : static bool AnalyzeFilename(const char *pszFilename,
486 : std::string &osUnderlyingFilename,
487 : size_t &nChunkSize, size_t &nCacheSize);
488 :
489 : public:
490 : VSIVirtualHandle *Open(const char *pszFilename, const char *pszAccess,
491 : bool bSetError, CSLConstList papszOptions) override;
492 : int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
493 : int nFlags) override;
494 : char **ReadDirEx(const char *pszDirname, int nMaxFiles) override;
495 : };
496 :
497 : /************************************************************************/
498 : /* ParseSize() */
499 : /************************************************************************/
500 :
501 17 : static bool ParseSize(const char *pszKey, const char *pszValue, size_t nMaxVal,
502 : size_t &nOutVal)
503 : {
504 17 : char *end = nullptr;
505 17 : auto nVal = std::strtoull(pszValue, &end, 10);
506 17 : if (!end || end == pszValue || nVal >= nMaxVal)
507 : {
508 6 : CPLError(
509 : CE_Failure, CPLE_IllegalArg,
510 : "Invalid value for %s: %s. Max supported value = " CPL_FRMT_GUIB,
511 : pszKey, pszValue, static_cast<GUIntBig>(nMaxVal));
512 6 : return false;
513 : }
514 11 : if (*end != '\0')
515 : {
516 10 : if (strcmp(end, "KB") == 0)
517 : {
518 4 : if (nVal > nMaxVal / 1024)
519 : {
520 2 : CPLError(CE_Failure, CPLE_IllegalArg,
521 : "Invalid value for %s: %s. Max supported value "
522 : "= " CPL_FRMT_GUIB,
523 : pszKey, pszValue, static_cast<GUIntBig>(nMaxVal));
524 2 : return false;
525 : }
526 2 : nVal *= 1024;
527 : }
528 6 : else if (strcmp(end, "MB") == 0)
529 : {
530 4 : if (nVal > nMaxVal / (1024 * 1024))
531 : {
532 2 : CPLError(CE_Failure, CPLE_IllegalArg,
533 : "Invalid value for %s: %s. Max supported value "
534 : "= " CPL_FRMT_GUIB,
535 : pszKey, pszValue, static_cast<GUIntBig>(nMaxVal));
536 2 : return false;
537 : }
538 2 : nVal *= (1024 * 1024);
539 : }
540 : else
541 : {
542 2 : CPLError(CE_Failure, CPLE_IllegalArg, "Invalid value for %s: %s",
543 : pszKey, pszValue);
544 2 : return false;
545 : }
546 : }
547 5 : nOutVal = static_cast<size_t>(nVal);
548 5 : return true;
549 : }
550 :
551 : /************************************************************************/
552 : /* AnalyzeFilename() */
553 : /************************************************************************/
554 :
555 26 : bool VSICachedFilesystemHandler::AnalyzeFilename(
556 : const char *pszFilename, std::string &osUnderlyingFilename,
557 : size_t &nChunkSize, size_t &nCacheSize)
558 : {
559 :
560 26 : if (!STARTS_WITH(pszFilename, "/vsicached?"))
561 0 : return false;
562 :
563 : const CPLStringList aosTokens(
564 52 : CSLTokenizeString2(pszFilename + strlen("/vsicached?"), "&", 0));
565 :
566 26 : osUnderlyingFilename.clear();
567 26 : nChunkSize = 0;
568 26 : nCacheSize = 0;
569 :
570 42 : for (int i = 0; i < aosTokens.size(); ++i)
571 : {
572 : char *pszUnescaped =
573 28 : CPLUnescapeString(aosTokens[i], nullptr, CPLES_URL);
574 28 : std::string osUnescaped(pszUnescaped);
575 28 : CPLFree(pszUnescaped);
576 28 : char *pszKey = nullptr;
577 28 : const char *pszValue = CPLParseNameValue(osUnescaped.c_str(), &pszKey);
578 28 : if (pszKey && pszValue)
579 : {
580 28 : if (strcmp(pszKey, "file") == 0)
581 : {
582 10 : osUnderlyingFilename = pszValue;
583 : }
584 18 : else if (strcmp(pszKey, "chunk_size") == 0)
585 : {
586 11 : if (!ParseSize(pszKey, pszValue, 1024 * 1024 * 1024,
587 : nChunkSize))
588 : {
589 6 : CPLFree(pszKey);
590 6 : return false;
591 : }
592 : }
593 7 : else if (strcmp(pszKey, "cache_size") == 0)
594 : {
595 6 : if (!ParseSize(pszKey, pszValue,
596 : std::numeric_limits<size_t>::max(), nCacheSize))
597 : {
598 6 : CPLFree(pszKey);
599 6 : return false;
600 : }
601 : }
602 : else
603 : {
604 1 : CPLError(CE_Warning, CPLE_NotSupported,
605 : "Unsupported option: %s", pszKey);
606 : }
607 : }
608 16 : CPLFree(pszKey);
609 : }
610 :
611 14 : if (osUnderlyingFilename.empty())
612 : {
613 4 : CPLError(CE_Warning, CPLE_NotSupported, "Missing 'file' option");
614 : }
615 :
616 14 : return !osUnderlyingFilename.empty();
617 : }
618 :
619 : /************************************************************************/
620 : /* Open() */
621 : /************************************************************************/
622 :
623 22 : VSIVirtualHandle *VSICachedFilesystemHandler::Open(const char *pszFilename,
624 : const char *pszAccess,
625 : bool bSetError,
626 : CSLConstList papszOptions)
627 : {
628 44 : std::string osUnderlyingFilename;
629 22 : size_t nChunkSize = 0;
630 22 : size_t nCacheSize = 0;
631 22 : if (!AnalyzeFilename(pszFilename, osUnderlyingFilename, nChunkSize,
632 : nCacheSize))
633 14 : return nullptr;
634 8 : if (strcmp(pszAccess, "r") != 0 && strcmp(pszAccess, "rb") != 0)
635 : {
636 1 : if (bSetError)
637 : {
638 0 : VSIError(VSIE_FileError,
639 : "/vsicached? supports only 'r' and 'rb' access modes");
640 : }
641 1 : return nullptr;
642 : }
643 :
644 7 : auto fp = VSIFOpenEx2L(osUnderlyingFilename.c_str(), pszAccess, bSetError,
645 : papszOptions);
646 7 : if (!fp)
647 1 : return nullptr;
648 6 : return VSICreateCachedFile(fp, nChunkSize, nCacheSize);
649 : }
650 :
651 : /************************************************************************/
652 : /* Stat() */
653 : /************************************************************************/
654 :
655 2 : int VSICachedFilesystemHandler::Stat(const char *pszFilename,
656 : VSIStatBufL *pStatBuf, int nFlags)
657 : {
658 4 : std::string osUnderlyingFilename;
659 2 : size_t nChunkSize = 0;
660 2 : size_t nCacheSize = 0;
661 2 : if (!AnalyzeFilename(pszFilename, osUnderlyingFilename, nChunkSize,
662 : nCacheSize))
663 1 : return -1;
664 1 : return VSIStatExL(osUnderlyingFilename.c_str(), pStatBuf, nFlags);
665 : }
666 :
667 : /************************************************************************/
668 : /* ReadDirEx() */
669 : /************************************************************************/
670 :
671 2 : char **VSICachedFilesystemHandler::ReadDirEx(const char *pszDirname,
672 : int nMaxFiles)
673 : {
674 4 : std::string osUnderlyingFilename;
675 2 : size_t nChunkSize = 0;
676 2 : size_t nCacheSize = 0;
677 2 : if (!AnalyzeFilename(pszDirname, osUnderlyingFilename, nChunkSize,
678 : nCacheSize))
679 1 : return nullptr;
680 1 : return VSIReadDirEx(osUnderlyingFilename.c_str(), nMaxFiles);
681 : }
682 :
683 : //! @endcond
684 :
685 : /************************************************************************/
686 : /* VSICreateCachedFile() */
687 : /************************************************************************/
688 :
689 : /** Wraps a file handle in another one, which has caching for read-operations.
690 : *
691 : * This takes a virtual file handle and returns a new handle that caches
692 : * read-operations on the input file handle. The cache is RAM based and
693 : * the content of the cache is discarded when the file handle is closed.
694 : * The cache is a least-recently used lists of blocks of 32KB each.
695 : *
696 : * @param poBaseHandle base handle
697 : * @param nChunkSize chunk size, in bytes. If 0, defaults to 32 KB
698 : * @param nCacheSize total size of the cache for the file, in bytes.
699 : * If 0, defaults to the value of the VSI_CACHE_SIZE
700 : * configuration option, which defaults to 25 MB.
701 : * @return a new handle
702 : */
703 25 : VSIVirtualHandle *VSICreateCachedFile(VSIVirtualHandle *poBaseHandle,
704 : size_t nChunkSize, size_t nCacheSize)
705 :
706 : {
707 25 : return new VSICachedFile(poBaseHandle, nChunkSize, nCacheSize);
708 : }
709 :
710 : /************************************************************************/
711 : /* VSIInstallCachedFileHandler() */
712 : /************************************************************************/
713 :
714 : /*!
715 : \brief Install /vsicached? file system handler
716 :
717 : \verbatim embed:rst
718 : See :ref:`/vsicached? documentation <vsicached>`
719 : \endverbatim
720 :
721 : @since GDAL 3.8.0
722 : */
723 1228 : void VSIInstallCachedFileHandler(void)
724 : {
725 1228 : VSIFilesystemHandler *poHandler = new VSICachedFilesystemHandler;
726 1228 : VSIFileManager::InstallHandler("/vsicached?", poHandler);
727 1228 : }
|