1 : /******************************************************************************
2 : * $Id: cpl_vsi_mem.cpp 24698 2012-07-23 09:10:33Z rouault $
3 : *
4 : * Project: VSI Virtual File System
5 : * Purpose: Implementation of Memory Buffer virtual IO functions.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.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 : /* Remove annoying warnings in eVC++ and VC++ 6.0 */
31 : #if defined(WIN32CE)
32 : # pragma warning(disable:4786)
33 : #endif
34 :
35 : #include "cpl_vsi_virtual.h"
36 : #include "cpl_string.h"
37 : #include "cpl_multiproc.h"
38 : #include <map>
39 :
40 : #if defined(WIN32CE)
41 : # include <wce_errno.h>
42 : #endif
43 :
44 :
45 : CPL_CVSID("$Id: cpl_vsi_mem.cpp 24698 2012-07-23 09:10:33Z rouault $");
46 :
47 : /*
48 : ** Notes on Multithreading:
49 : **
50 : ** VSIMemFilesystemHandler: This class maintains a mutex to protect
51 : ** access and update of the oFileList array which has all the "files" in
52 : ** the memory filesystem area. It is expected that multiple threads would
53 : ** want to create and read different files at the same time and so might
54 : ** collide access oFileList without the mutex.
55 : **
56 : ** VSIMemFile: In theory we could allow different threads to update the
57 : ** the same memory file, but for simplicity we restrict to single writer,
58 : ** multiple reader as an expectation on the application code (not enforced
59 : ** here), which means we don't need to do any protection of this class.
60 : **
61 : ** VSIMemHandle: This is essentially a "current location" representing
62 : ** on accessor to a file, and is inherently intended only to be used in
63 : ** a single thread.
64 : **
65 : ** In General:
66 : **
67 : ** Multiple threads accessing the memory filesystem are ok as long as
68 : ** 1) A given VSIMemHandle (ie. FILE * at app level) isn't used by multiple
69 : ** threads at once.
70 : ** 2) A given memory file isn't accessed by more than one thread unless
71 : ** all threads are just reading.
72 : */
73 :
74 : /************************************************************************/
75 : /* ==================================================================== */
76 : /* VSIMemFile */
77 : /* ==================================================================== */
78 : /************************************************************************/
79 :
80 : class VSIMemFile
81 : {
82 : public:
83 : CPLString osFilename;
84 : int nRefCount;
85 :
86 : int bIsDirectory;
87 :
88 : int bOwnData;
89 : GByte *pabyData;
90 : vsi_l_offset nLength;
91 : vsi_l_offset nAllocLength;
92 :
93 : int bEOF;
94 :
95 : VSIMemFile();
96 : virtual ~VSIMemFile();
97 :
98 : bool SetLength( vsi_l_offset nNewSize );
99 : };
100 :
101 : /************************************************************************/
102 : /* ==================================================================== */
103 : /* VSIMemHandle */
104 : /* ==================================================================== */
105 : /************************************************************************/
106 :
107 : class VSIMemHandle : public VSIVirtualHandle
108 32744 : {
109 : public:
110 : VSIMemFile *poFile;
111 : vsi_l_offset nOffset;
112 : int bUpdate;
113 : int bEOF;
114 :
115 : virtual int Seek( vsi_l_offset nOffset, int nWhence );
116 : virtual vsi_l_offset Tell();
117 : virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb );
118 : virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb );
119 : virtual int Eof();
120 : virtual int Close();
121 : virtual int Truncate( vsi_l_offset nNewSize );
122 : };
123 :
124 : /************************************************************************/
125 : /* ==================================================================== */
126 : /* VSIMemFilesystemHandler */
127 : /* ==================================================================== */
128 : /************************************************************************/
129 :
130 : class VSIMemFilesystemHandler : public VSIFilesystemHandler
131 : {
132 : public:
133 : std::map<CPLString,VSIMemFile*> oFileList;
134 : void *hMutex;
135 :
136 : VSIMemFilesystemHandler();
137 : virtual ~VSIMemFilesystemHandler();
138 :
139 : virtual VSIVirtualHandle *Open( const char *pszFilename,
140 : const char *pszAccess);
141 : virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags );
142 : virtual int Unlink( const char *pszFilename );
143 : virtual int Mkdir( const char *pszDirname, long nMode );
144 : virtual int Rmdir( const char *pszDirname );
145 : virtual char **ReadDir( const char *pszDirname );
146 : virtual int Rename( const char *oldpath, const char *newpath );
147 :
148 : static void NormalizePath( CPLString & );
149 : };
150 :
151 : /************************************************************************/
152 : /* ==================================================================== */
153 : /* VSIMemFile */
154 : /* ==================================================================== */
155 : /************************************************************************/
156 :
157 : /************************************************************************/
158 : /* VSIMemFile() */
159 : /************************************************************************/
160 :
161 5705 : VSIMemFile::VSIMemFile()
162 :
163 : {
164 5705 : nRefCount = 0;
165 5705 : bIsDirectory = FALSE;
166 5705 : bOwnData = TRUE;
167 5705 : pabyData = NULL;
168 5705 : nLength = 0;
169 5705 : nAllocLength = 0;
170 5705 : bEOF = FALSE;
171 5705 : }
172 :
173 : /************************************************************************/
174 : /* ~VSIMemFile() */
175 : /************************************************************************/
176 :
177 5705 : VSIMemFile::~VSIMemFile()
178 :
179 : {
180 5705 : if( nRefCount != 0 )
181 : CPLDebug( "VSIMemFile", "Memory file %s deleted with %d references.",
182 0 : osFilename.c_str(), nRefCount );
183 :
184 5705 : if( bOwnData && pabyData )
185 5448 : CPLFree( pabyData );
186 5705 : }
187 :
188 : /************************************************************************/
189 : /* SetLength() */
190 : /************************************************************************/
191 :
192 149605 : bool VSIMemFile::SetLength( vsi_l_offset nNewLength )
193 :
194 : {
195 : /* -------------------------------------------------------------------- */
196 : /* Grow underlying array if needed. */
197 : /* -------------------------------------------------------------------- */
198 149605 : if( nNewLength > nAllocLength )
199 : {
200 : /* If we don't own the buffer, we cannot reallocate it because */
201 : /* the return address might be different from the one passed by */
202 : /* the caller. Hence, the caller would not be able to free */
203 : /* the buffer... */
204 7791 : if( !bOwnData )
205 : {
206 : CPLError(CE_Failure, CPLE_NotSupported,
207 0 : "Cannot extended in-memory file whose ownership was not transfered");
208 0 : return false;
209 : }
210 :
211 : GByte *pabyNewData;
212 7791 : vsi_l_offset nNewAlloc = (nNewLength + nNewLength / 10) + 5000;
213 :
214 7791 : pabyNewData = (GByte *) VSIRealloc(pabyData, (size_t)nNewAlloc);
215 7791 : if( pabyNewData == NULL )
216 : {
217 : CPLError(CE_Failure, CPLE_OutOfMemory,
218 : "Cannot extend in-memory file to " CPL_FRMT_GUIB " bytes due to out-of-memory situation",
219 0 : nNewAlloc);
220 0 : return false;
221 : }
222 :
223 : /* Clear the new allocated part of the buffer */
224 : memset(pabyNewData + nAllocLength, 0,
225 7791 : (size_t) (nNewAlloc - nAllocLength));
226 :
227 7791 : pabyData = pabyNewData;
228 7791 : nAllocLength = nNewAlloc;
229 : }
230 :
231 149605 : nLength = nNewLength;
232 :
233 149605 : return true;
234 : }
235 :
236 : /************************************************************************/
237 : /* ==================================================================== */
238 : /* VSIMemHandle */
239 : /* ==================================================================== */
240 : /************************************************************************/
241 :
242 :
243 : /************************************************************************/
244 : /* Close() */
245 : /************************************************************************/
246 :
247 16372 : int VSIMemHandle::Close()
248 :
249 : {
250 16372 : if( --(poFile->nRefCount) == 0 )
251 25 : delete poFile;
252 :
253 16372 : poFile = NULL;
254 :
255 16372 : return 0;
256 : }
257 :
258 : /************************************************************************/
259 : /* Seek() */
260 : /************************************************************************/
261 :
262 691310 : int VSIMemHandle::Seek( vsi_l_offset nOffset, int nWhence )
263 :
264 : {
265 691310 : if( nWhence == SEEK_CUR )
266 354120 : this->nOffset += nOffset;
267 337190 : else if( nWhence == SEEK_SET )
268 311613 : this->nOffset = nOffset;
269 25577 : else if( nWhence == SEEK_END )
270 25577 : this->nOffset = poFile->nLength + nOffset;
271 : else
272 : {
273 0 : errno = EINVAL;
274 0 : return -1;
275 : }
276 :
277 691310 : bEOF = FALSE;
278 :
279 691310 : if( this->nOffset > poFile->nLength )
280 : {
281 661 : if( !bUpdate ) // Read-only files cannot be extended by seek.
282 : {
283 : CPLDebug( "VSIMemHandle",
284 : "Attempt to extend read-only file '%s' to length %d from %d, .",
285 : poFile->osFilename.c_str(),
286 3 : (int) this->nOffset, (int) poFile->nLength );
287 :
288 3 : this->nOffset = poFile->nLength;
289 3 : errno = EACCES;
290 3 : return -1;
291 : }
292 : else // Writeable files are zero-extended by seek past end.
293 : {
294 658 : if( !poFile->SetLength( this->nOffset ) )
295 0 : return -1;
296 : }
297 : }
298 :
299 691307 : return 0;
300 : }
301 :
302 : /************************************************************************/
303 : /* Tell() */
304 : /************************************************************************/
305 :
306 42916 : vsi_l_offset VSIMemHandle::Tell()
307 :
308 : {
309 42916 : return nOffset;
310 : }
311 :
312 : /************************************************************************/
313 : /* Read() */
314 : /************************************************************************/
315 :
316 2783825 : size_t VSIMemHandle::Read( void * pBuffer, size_t nSize, size_t nCount )
317 :
318 : {
319 : // FIXME: Integer overflow check should be placed here:
320 2783825 : size_t nBytesToRead = nSize * nCount;
321 :
322 2783825 : if( nBytesToRead + nOffset > poFile->nLength )
323 : {
324 1517 : if (poFile->nLength < nOffset)
325 : {
326 0 : bEOF = TRUE;
327 0 : return 0;
328 : }
329 :
330 1517 : nBytesToRead = (size_t)(poFile->nLength - nOffset);
331 1517 : nCount = nBytesToRead / nSize;
332 1517 : bEOF = TRUE;
333 : }
334 :
335 2783825 : memcpy( pBuffer, poFile->pabyData + nOffset, (size_t)nBytesToRead );
336 2783825 : nOffset += nBytesToRead;
337 :
338 2783825 : return nCount;
339 : }
340 :
341 : /************************************************************************/
342 : /* Write() */
343 : /************************************************************************/
344 :
345 162471 : size_t VSIMemHandle::Write( const void * pBuffer, size_t nSize, size_t nCount )
346 :
347 : {
348 162471 : if( !bUpdate )
349 : {
350 555 : errno = EACCES;
351 555 : return 0;
352 : }
353 :
354 : // FIXME: Integer overflow check should be placed here:
355 161916 : size_t nBytesToWrite = nSize * nCount;
356 :
357 161916 : if( nBytesToWrite + nOffset > poFile->nLength )
358 : {
359 148776 : if( !poFile->SetLength( nBytesToWrite + nOffset ) )
360 0 : return 0;
361 : }
362 :
363 161916 : memcpy( poFile->pabyData + nOffset, pBuffer, nBytesToWrite );
364 161916 : nOffset += nBytesToWrite;
365 :
366 161916 : return nCount;
367 : }
368 :
369 : /************************************************************************/
370 : /* Eof() */
371 : /************************************************************************/
372 :
373 18550 : int VSIMemHandle::Eof()
374 :
375 : {
376 18550 : return bEOF;
377 : }
378 :
379 : /************************************************************************/
380 : /* Truncate() */
381 : /************************************************************************/
382 :
383 109 : int VSIMemHandle::Truncate( vsi_l_offset nNewSize )
384 : {
385 109 : if( !bUpdate )
386 : {
387 0 : errno = EACCES;
388 0 : return -1;
389 : }
390 :
391 109 : if (poFile->SetLength( nNewSize ))
392 109 : return 0;
393 : else
394 0 : return -1;
395 : }
396 :
397 : /************************************************************************/
398 : /* ==================================================================== */
399 : /* VSIMemFilesystemHandler */
400 : /* ==================================================================== */
401 : /************************************************************************/
402 :
403 : /************************************************************************/
404 : /* VSIMemFilesystemHandler() */
405 : /************************************************************************/
406 :
407 757 : VSIMemFilesystemHandler::VSIMemFilesystemHandler()
408 :
409 : {
410 757 : hMutex = NULL;
411 757 : }
412 :
413 : /************************************************************************/
414 : /* ~VSIMemFilesystemHandler() */
415 : /************************************************************************/
416 :
417 704 : VSIMemFilesystemHandler::~VSIMemFilesystemHandler()
418 :
419 : {
420 704 : std::map<CPLString,VSIMemFile*>::const_iterator iter;
421 :
422 1185 : for( iter = oFileList.begin(); iter != oFileList.end(); ++iter )
423 : {
424 481 : iter->second->nRefCount--;
425 481 : delete iter->second;
426 : }
427 :
428 704 : if( hMutex != NULL )
429 20 : CPLDestroyMutex( hMutex );
430 704 : hMutex = NULL;
431 704 : }
432 :
433 : /************************************************************************/
434 : /* Open() */
435 : /************************************************************************/
436 :
437 : VSIVirtualHandle *
438 24564 : VSIMemFilesystemHandler::Open( const char *pszFilename,
439 : const char *pszAccess )
440 :
441 : {
442 24564 : CPLMutexHolder oHolder( &hMutex );
443 : VSIMemFile *poFile;
444 24564 : CPLString osFilename = pszFilename;
445 24564 : NormalizePath( osFilename );
446 :
447 : /* -------------------------------------------------------------------- */
448 : /* Get the filename we are opening, create if needed. */
449 : /* -------------------------------------------------------------------- */
450 24564 : if( oFileList.find(osFilename) == oFileList.end() )
451 13248 : poFile = NULL;
452 : else
453 11316 : poFile = oFileList[osFilename];
454 :
455 24564 : if( strstr(pszAccess,"w") == NULL && poFile == NULL )
456 : {
457 8189 : errno = ENOENT;
458 8189 : return NULL;
459 : }
460 :
461 16375 : if( strstr(pszAccess,"w") )
462 : {
463 5121 : if( poFile )
464 62 : poFile->SetLength( 0 );
465 : else
466 : {
467 5059 : poFile = new VSIMemFile;
468 5059 : poFile->osFilename = osFilename;
469 5059 : oFileList[poFile->osFilename] = poFile;
470 5059 : poFile->nRefCount++; // for file list
471 : }
472 : }
473 :
474 16375 : if( poFile->bIsDirectory )
475 : {
476 3 : errno = EISDIR;
477 3 : return NULL;
478 : }
479 :
480 : /* -------------------------------------------------------------------- */
481 : /* Setup the file handle on this file. */
482 : /* -------------------------------------------------------------------- */
483 16372 : VSIMemHandle *poHandle = new VSIMemHandle;
484 :
485 16372 : poHandle->poFile = poFile;
486 16372 : poHandle->nOffset = 0;
487 16372 : poHandle->bEOF = FALSE;
488 40092 : if( strstr(pszAccess,"w") || strstr(pszAccess,"+")
489 : || strstr(pszAccess,"a") )
490 12469 : poHandle->bUpdate = TRUE;
491 : else
492 3903 : poHandle->bUpdate = FALSE;
493 :
494 16372 : poFile->nRefCount++;
495 :
496 16372 : if( strstr(pszAccess,"a") )
497 0 : poHandle->nOffset = poFile->nLength;
498 :
499 16372 : return poHandle;
500 : }
501 :
502 : /************************************************************************/
503 : /* Stat() */
504 : /************************************************************************/
505 :
506 11230 : int VSIMemFilesystemHandler::Stat( const char * pszFilename,
507 : VSIStatBufL * pStatBuf,
508 : int nFlags )
509 :
510 : {
511 : (void) nFlags;
512 :
513 11230 : CPLMutexHolder oHolder( &hMutex );
514 :
515 11230 : CPLString osFilename = pszFilename;
516 11230 : NormalizePath( osFilename );
517 :
518 11230 : memset( pStatBuf, 0, sizeof(VSIStatBufL) );
519 :
520 11230 : if ( osFilename == "/vsimem/" )
521 : {
522 5 : pStatBuf->st_size = 0;
523 5 : pStatBuf->st_mode = S_IFDIR;
524 5 : return 0;
525 : }
526 :
527 11225 : if( oFileList.find(osFilename) == oFileList.end() )
528 : {
529 8419 : errno = ENOENT;
530 8419 : return -1;
531 : }
532 :
533 2806 : VSIMemFile *poFile = oFileList[osFilename];
534 :
535 2806 : memset( pStatBuf, 0, sizeof(VSIStatBufL) );
536 :
537 2806 : if( poFile->bIsDirectory )
538 : {
539 43 : pStatBuf->st_size = 0;
540 43 : pStatBuf->st_mode = S_IFDIR;
541 : }
542 : else
543 : {
544 2763 : pStatBuf->st_size = (long)poFile->nLength;
545 2763 : pStatBuf->st_mode = S_IFREG;
546 : }
547 :
548 2806 : return 0;
549 : }
550 :
551 : /************************************************************************/
552 : /* Unlink() */
553 : /************************************************************************/
554 :
555 8052 : int VSIMemFilesystemHandler::Unlink( const char * pszFilename )
556 :
557 : {
558 8052 : CPLMutexHolder oHolder( &hMutex );
559 :
560 8052 : CPLString osFilename = pszFilename;
561 8052 : NormalizePath( osFilename );
562 :
563 : VSIMemFile *poFile;
564 :
565 8052 : if( oFileList.find(osFilename) == oFileList.end() )
566 : {
567 2873 : errno = ENOENT;
568 2873 : return -1;
569 : }
570 : else
571 : {
572 5179 : poFile = oFileList[osFilename];
573 :
574 5179 : if( --(poFile->nRefCount) == 0 )
575 5154 : delete poFile;
576 :
577 5179 : oFileList.erase( oFileList.find(osFilename) );
578 :
579 5179 : return 0;
580 0 : }
581 : }
582 :
583 : /************************************************************************/
584 : /* Mkdir() */
585 : /************************************************************************/
586 :
587 33 : int VSIMemFilesystemHandler::Mkdir( const char * pszPathname,
588 : long nMode )
589 :
590 : {
591 : (void) nMode;
592 :
593 33 : CPLMutexHolder oHolder( &hMutex );
594 :
595 33 : CPLString osPathname = pszPathname;
596 :
597 33 : NormalizePath( osPathname );
598 :
599 33 : if( oFileList.find(osPathname) != oFileList.end() )
600 : {
601 7 : errno = EEXIST;
602 7 : return -1;
603 : }
604 :
605 26 : VSIMemFile *poFile = new VSIMemFile;
606 :
607 26 : poFile->osFilename = osPathname;
608 26 : poFile->bIsDirectory = TRUE;
609 26 : oFileList[osPathname] = poFile;
610 26 : poFile->nRefCount++; /* referenced by file list */
611 :
612 26 : return 0;
613 : }
614 :
615 : /************************************************************************/
616 : /* Rmdir() */
617 : /************************************************************************/
618 :
619 226 : int VSIMemFilesystemHandler::Rmdir( const char * pszPathname )
620 :
621 : {
622 226 : CPLMutexHolder oHolder( &hMutex );
623 :
624 226 : return Unlink( pszPathname );
625 : }
626 :
627 : /************************************************************************/
628 : /* ReadDir() */
629 : /************************************************************************/
630 :
631 893 : char **VSIMemFilesystemHandler::ReadDir( const char *pszPath )
632 :
633 : {
634 893 : CPLMutexHolder oHolder( &hMutex );
635 :
636 893 : CPLString osPath = pszPath;
637 :
638 893 : NormalizePath( osPath );
639 :
640 893 : std::map<CPLString,VSIMemFile*>::const_iterator iter;
641 893 : char **papszDir = NULL;
642 893 : int nPathLen = strlen(osPath);
643 :
644 893 : if( osPath[nPathLen-1] == '/' )
645 3 : nPathLen--;
646 :
647 : /* In case of really big number of files in the directory, CSLAddString */
648 : /* can be slow (see #2158). We then directly build the list. */
649 893 : int nItems=0;
650 893 : int nAllocatedItems=0;
651 :
652 36291 : for( iter = oFileList.begin(); iter != oFileList.end(); ++iter )
653 : {
654 35398 : const char *pszFilePath = iter->second->osFilename.c_str();
655 51608 : if( EQUALN(osPath,pszFilePath,nPathLen)
656 16210 : && pszFilePath[nPathLen] == '/'
657 : && strstr(pszFilePath+nPathLen+1,"/") == NULL )
658 : {
659 14579 : if (nItems == 0)
660 : {
661 739 : papszDir = (char**) CPLCalloc(2,sizeof(char*));
662 739 : nAllocatedItems = 1;
663 : }
664 13840 : else if (nItems >= nAllocatedItems)
665 : {
666 2007 : nAllocatedItems = nAllocatedItems * 2;
667 : papszDir = (char**)CPLRealloc(papszDir,
668 2007 : (nAllocatedItems+2)*sizeof(char*));
669 : }
670 :
671 14579 : papszDir[nItems] = CPLStrdup(pszFilePath+nPathLen+1);
672 14579 : papszDir[nItems+1] = NULL;
673 :
674 14579 : nItems++;
675 : }
676 : }
677 :
678 893 : return papszDir;
679 : }
680 :
681 : /************************************************************************/
682 : /* Rename() */
683 : /************************************************************************/
684 :
685 0 : int VSIMemFilesystemHandler::Rename( const char *pszOldPath,
686 : const char *pszNewPath )
687 :
688 : {
689 0 : CPLMutexHolder oHolder( &hMutex );
690 :
691 0 : CPLString osOldPath = pszOldPath;
692 0 : CPLString osNewPath = pszNewPath;
693 :
694 0 : NormalizePath( osOldPath );
695 0 : NormalizePath( osNewPath );
696 :
697 0 : if ( osOldPath.compare(osNewPath) == 0 )
698 0 : return 0;
699 :
700 0 : if( oFileList.find(osOldPath) == oFileList.end() )
701 : {
702 0 : errno = ENOENT;
703 0 : return -1;
704 : }
705 : else
706 : {
707 0 : VSIMemFile* poFile = oFileList[osOldPath];
708 :
709 0 : oFileList.erase( oFileList.find(osOldPath) );
710 :
711 0 : Unlink(osNewPath);
712 :
713 0 : oFileList[osNewPath] = poFile;
714 0 : poFile->osFilename = osNewPath;
715 :
716 0 : return 0;
717 0 : }
718 : }
719 :
720 : /************************************************************************/
721 : /* NormalizePath() */
722 : /************************************************************************/
723 :
724 45506 : void VSIMemFilesystemHandler::NormalizePath( CPLString &oPath )
725 :
726 : {
727 45506 : int i, nSize = oPath.size();
728 :
729 1488915 : for( i = 0; i < nSize; i++ )
730 : {
731 1443409 : if( oPath[i] == '\\' )
732 4 : oPath[i] = '/';
733 : }
734 :
735 45506 : }
736 :
737 : /************************************************************************/
738 : /* VSIInstallLargeFileHandler() */
739 : /************************************************************************/
740 :
741 : /**
742 : * \brief Install "memory" file system handler.
743 : *
744 : * A special file handler is installed that allows block of memory to be
745 : * treated as files. All portions of the file system underneath the base
746 : * path "/vsimem/" will be handled by this driver.
747 : *
748 : * Normal VSI*L functions can be used freely to create and destroy memory
749 : * arrays treating them as if they were real file system objects. Some
750 : * additional methods exist to efficient create memory file system objects
751 : * without duplicating original copies of the data or to "steal" the block
752 : * of memory associated with a memory file.
753 : *
754 : * At this time the memory handler does not properly handle directory
755 : * semantics for the memory portion of the filesystem. The VSIReadDir()
756 : * function is not supported though this will be corrected in the future.
757 : *
758 : * Calling this function repeatedly should do no harm, though it is not
759 : * necessary. It is already called the first time a virtualizable
760 : * file access function (ie. VSIFOpenL(), VSIMkDir(), etc) is called.
761 : *
762 : * This code example demonstrates using GDAL to translate from one memory
763 : * buffer to another.
764 : *
765 : * \code
766 : * GByte *ConvertBufferFormat( GByte *pabyInData, vsi_l_offset nInDataLength,
767 : * vsi_l_offset *pnOutDataLength )
768 : * {
769 : * // create memory file system object from buffer.
770 : * VSIFCloseL( VSIFileFromMemBuffer( "/vsimem/work.dat", pabyInData,
771 : * nInDataLength, FALSE ) );
772 : *
773 : * // Open memory buffer for read.
774 : * GDALDatasetH hDS = GDALOpen( "/vsimem/work.dat", GA_ReadOnly );
775 : *
776 : * // Get output format driver.
777 : * GDALDriverH hDriver = GDALGetDriverByName( "GTiff" );
778 : * GDALDatasetH hOutDS;
779 : *
780 : * hOutDS = GDALCreateCopy( hDriver, "/vsimem/out.tif", hDS, TRUE, NULL,
781 : * NULL, NULL );
782 : *
783 : * // close source file, and "unlink" it.
784 : * GDALClose( hDS );
785 : * VSIUnlink( "/vsimem/work.dat" );
786 : *
787 : * // seize the buffer associated with the output file.
788 : *
789 : * return VSIGetMemFileBuffer( "/vsimem/out.tif", pnOutDataLength, TRUE );
790 : * }
791 : * \endcode
792 : */
793 :
794 757 : void VSIInstallMemFileHandler()
795 : {
796 757 : VSIFileManager::InstallHandler( "/vsimem/", new VSIMemFilesystemHandler );
797 757 : }
798 :
799 : /************************************************************************/
800 : /* VSIFileFromMemBuffer() */
801 : /************************************************************************/
802 :
803 : /**
804 : * \brief Create memory "file" from a buffer.
805 : *
806 : * A virtual memory file is created from the passed buffer with the indicated
807 : * filename. Under normal conditions the filename would need to be absolute
808 : * and within the /vsimem/ portion of the filesystem.
809 : *
810 : * If bTakeOwnership is TRUE, then the memory file system handler will take
811 : * ownership of the buffer, freeing it when the file is deleted. Otherwise
812 : * it remains the responsibility of the caller, but should not be freed as
813 : * long as it might be accessed as a file. In no circumstances does this
814 : * function take a copy of the pabyData contents.
815 : *
816 : * @param pszFilename the filename to be created.
817 : * @param pabyData the data buffer for the file.
818 : * @param nDataLength the length of buffer in bytes.
819 : * @param bTakeOwnership TRUE to transfer "ownership" of buffer or FALSE.
820 : *
821 : * @return open file handle on created file (see VSIFOpenL()).
822 : */
823 :
824 620 : VSILFILE *VSIFileFromMemBuffer( const char *pszFilename,
825 : GByte *pabyData,
826 : vsi_l_offset nDataLength,
827 : int bTakeOwnership )
828 :
829 : {
830 620 : if( VSIFileManager::GetHandler("")
831 : == VSIFileManager::GetHandler("/vsimem/") )
832 0 : VSIInstallMemFileHandler();
833 :
834 : VSIMemFilesystemHandler *poHandler = (VSIMemFilesystemHandler *)
835 620 : VSIFileManager::GetHandler("/vsimem/");
836 :
837 620 : if (pszFilename == NULL)
838 0 : return NULL;
839 :
840 620 : CPLString osFilename = pszFilename;
841 620 : VSIMemFilesystemHandler::NormalizePath( osFilename );
842 :
843 620 : VSIMemFile *poFile = new VSIMemFile;
844 :
845 620 : poFile->osFilename = osFilename;
846 620 : poFile->bOwnData = bTakeOwnership;
847 620 : poFile->pabyData = pabyData;
848 620 : poFile->nLength = nDataLength;
849 620 : poFile->nAllocLength = nDataLength;
850 :
851 : {
852 620 : CPLMutexHolder oHolder( &poHandler->hMutex );
853 620 : poHandler->Unlink(osFilename);
854 620 : poHandler->oFileList[poFile->osFilename] = poFile;
855 620 : poFile->nRefCount++;
856 : }
857 :
858 620 : return (VSILFILE *) poHandler->Open( osFilename, "r+" );
859 : }
860 :
861 : /************************************************************************/
862 : /* VSIGetMemFileBuffer() */
863 : /************************************************************************/
864 :
865 : /**
866 : * \brief Fetch buffer underlying memory file.
867 : *
868 : * This function returns a pointer to the memory buffer underlying a
869 : * virtual "in memory" file. If bUnlinkAndSeize is TRUE the filesystem
870 : * object will be deleted, and ownership of the buffer will pass to the
871 : * caller otherwise the underlying file will remain in existance.
872 : *
873 : * @param pszFilename the name of the file to grab the buffer of.
874 : * @param pnDataLength (file) length returned in this variable.
875 : * @param bUnlinkAndSeize TRUE to remove the file, or FALSE to leave unaltered.
876 : *
877 : * @return pointer to memory buffer or NULL on failure.
878 : */
879 :
880 114 : GByte *VSIGetMemFileBuffer( const char *pszFilename,
881 : vsi_l_offset *pnDataLength,
882 : int bUnlinkAndSeize )
883 :
884 : {
885 : VSIMemFilesystemHandler *poHandler = (VSIMemFilesystemHandler *)
886 114 : VSIFileManager::GetHandler("/vsimem/");
887 :
888 114 : if (pszFilename == NULL)
889 0 : return NULL;
890 :
891 114 : CPLString osFilename = pszFilename;
892 114 : VSIMemFilesystemHandler::NormalizePath( osFilename );
893 :
894 114 : CPLMutexHolder oHolder( &poHandler->hMutex );
895 :
896 114 : if( poHandler->oFileList.find(osFilename) == poHandler->oFileList.end() )
897 0 : return NULL;
898 :
899 114 : VSIMemFile *poFile = poHandler->oFileList[osFilename];
900 : GByte *pabyData;
901 :
902 114 : pabyData = poFile->pabyData;
903 114 : if( pnDataLength != NULL )
904 114 : *pnDataLength = poFile->nLength;
905 :
906 114 : if( bUnlinkAndSeize )
907 : {
908 45 : if( !poFile->bOwnData )
909 : CPLDebug( "VSIMemFile",
910 0 : "File doesn't own data in VSIGetMemFileBuffer!" );
911 : else
912 45 : poFile->bOwnData = FALSE;
913 :
914 45 : poHandler->oFileList.erase( poHandler->oFileList.find(osFilename) );
915 45 : --(poFile->nRefCount);
916 45 : delete poFile;
917 : }
918 :
919 114 : return pabyData;
920 : }
921 :
|