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