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