Panda3D
Loading...
Searching...
No Matches
virtualFileMountRamdisk.cxx
Go to the documentation of this file.
1/**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file virtualFileMountRamdisk.cxx
10 * @author drose
11 * @date 2011-09-19
12 */
13
15#include "subStream.h"
16#include "dcast.h"
17
18using std::iostream;
19using std::istream;
20using std::ostream;
21using std::string;
22
23TypeHandle VirtualFileMountRamdisk::_type_handle;
24TypeHandle VirtualFileMountRamdisk::FileBase::_type_handle;
25TypeHandle VirtualFileMountRamdisk::File::_type_handle;
26TypeHandle VirtualFileMountRamdisk::Directory::_type_handle;
27
28/**
29 *
30 */
31VirtualFileMountRamdisk::
32VirtualFileMountRamdisk() : _root("") {
33 _root.local_object();
34}
35
36/**
37 * Returns true if the indicated file exists within the mount system.
38 */
40has_file(const Filename &file) const {
41 _lock.lock();
42 PT(FileBase) f = _root.do_find_file(file);
43 _lock.unlock();
44 return (f != nullptr);
45}
46
47/**
48 * Attempts to create the indicated file within the mount, if it does not
49 * already exist. Returns true on success (or if the file already exists), or
50 * false if it cannot be created.
51 */
53create_file(const Filename &file) {
54 _lock.lock();
55 PT(File) f = _root.do_create_file(file);
56 _lock.unlock();
57 return (f != nullptr);
58}
59
60/**
61 * Attempts to delete the indicated file or directory within the mount. This
62 * can remove a single file or an empty directory. It will not remove a
63 * nonempty directory. Returns true on success, false on failure.
64 */
66delete_file(const Filename &file) {
67 _lock.lock();
68 PT(FileBase) f = _root.do_delete_file(file);
69 _lock.unlock();
70 return (f != nullptr);
71}
72
73/**
74 * Attempts to rename the contents of the indicated file to the indicated
75 * file. Both filenames will be within the mount. Returns true on success,
76 * false on failure. If this returns false, this will be attempted again with
77 * a copy-and-delete operation.
78 */
80rename_file(const Filename &orig_filename, const Filename &new_filename) {
81 _lock.lock();
82 PT(FileBase) orig_fb = _root.do_find_file(orig_filename);
83 if (orig_fb == nullptr) {
84 _lock.unlock();
85 return false;
86 }
87
88 if (orig_fb->is_directory()) {
89 // Rename the directory.
90 Directory *orig_d = DCAST(Directory, orig_fb);
91 PT(Directory) new_d = _root.do_make_directory(new_filename);
92 if (new_d == nullptr || !new_d->_files.empty()) {
93 _lock.unlock();
94 return false;
95 }
96
97 if (express_cat.is_debug()) {
98 express_cat.debug()
99 << "Renaming ramdisk directory " << orig_filename << " to " << new_filename << "\n";
100 }
101
102 new_d->_files.swap(orig_d->_files);
103 _root.do_delete_file(orig_filename);
104 _lock.unlock();
105 return true;
106 }
107
108 // Rename the file.
109 File *orig_f = DCAST(File, orig_fb);
110 PT(File) new_f = _root.do_create_file(new_filename);
111 if (new_f == nullptr) {
112 _lock.unlock();
113 return false;
114 }
115
116 if (express_cat.is_debug()) {
117 express_cat.debug()
118 << "Renaming ramdisk file " << orig_filename << " to " << new_filename << "\n";
119 }
120
121 new_f->_data.str(orig_f->_data.str());
122 _root.do_delete_file(orig_filename);
123
124 _lock.unlock();
125 return true;
126}
127
128/**
129 * Attempts to copy the contents of the indicated file to the indicated file.
130 * Both filenames will be within the mount. Returns true on success, false on
131 * failure. If this returns false, the copy will be performed by explicit
132 * read-and-write operations.
133 */
135copy_file(const Filename &orig_filename, const Filename &new_filename) {
136 _lock.lock();
137 PT(FileBase) orig_fb = _root.do_find_file(orig_filename);
138 if (orig_fb == nullptr || orig_fb->is_directory()) {
139 _lock.unlock();
140 return false;
141 }
142
143 // Copy the file.
144 File *orig_f = DCAST(File, orig_fb);
145 PT(File) new_f = _root.do_create_file(new_filename);
146 if (new_f == nullptr) {
147 _lock.unlock();
148 return false;
149 }
150
151 if (express_cat.is_debug()) {
152 express_cat.debug()
153 << "Copying ramdisk file " << orig_filename << " to " << new_filename << "\n";
154 }
155
156 new_f->_data.str(orig_f->_data.str());
157
158 _lock.unlock();
159 return true;
160}
161
162/**
163 * Attempts to create the indicated file within the mount, if it does not
164 * already exist. Returns true on success, or false if it cannot be created.
165 * If the directory already existed prior to this call, may return either true
166 * or false.
167 */
169make_directory(const Filename &file) {
170 _lock.lock();
171 PT(Directory) f = _root.do_make_directory(file);
172 _lock.unlock();
173 return (f != nullptr);
174}
175
176/**
177 * Returns true if the indicated file exists within the mount system and is a
178 * directory.
179 */
181is_directory(const Filename &file) const {
182 _lock.lock();
183 PT(FileBase) f = _root.do_find_file(file);
184 _lock.unlock();
185 return (f != nullptr && f->is_directory());
186}
187
188/**
189 * Returns true if the indicated file exists within the mount system and is a
190 * regular file.
191 */
193is_regular_file(const Filename &file) const {
194 _lock.lock();
195 PT(FileBase) f = _root.do_find_file(file);
196 _lock.unlock();
197 return (f != nullptr && !f->is_directory());
198}
199
200/**
201 * Returns true if the named file or directory may be written to, false
202 * otherwise.
203 */
205is_writable(const Filename &file) const {
206 return has_file(file);
207}
208
209/**
210 * Opens the file for reading, if it exists. Returns a newly allocated
211 * istream on success (which you should eventually delete when you are done
212 * reading). Returns NULL on failure.
213 */
215open_read_file(const Filename &file) const {
216 _lock.lock();
217 PT(FileBase) f = _root.do_find_file(file);
218 _lock.unlock();
219 if (f == nullptr || f->is_directory()) {
220 return nullptr;
221 }
222
223 File *f2 = DCAST(File, f);
224 return new ISubStream(&f2->_wrapper, 0, 0);
225}
226
227/**
228 * Opens the file for writing. Returns a newly allocated ostream on success
229 * (which you should eventually delete when you are done writing). Returns
230 * NULL on failure.
231 */
233open_write_file(const Filename &file, bool truncate) {
234 _lock.lock();
235 PT(File) f = _root.do_create_file(file);
236 _lock.unlock();
237 if (f == nullptr) {
238 return nullptr;
239 }
240
241 if (truncate) {
242 // Reset to an empty string.
243 f->_data.str(string());
244
245 // Instead of setting the time, we ensure that we always store a newer time.
246 // This is a workarround for the case that a file is written twice per
247 // second, since the timer only has a one second precision. The proper
248 // solution to fix this would be to switch to a higher precision
249 // timer everywhere.
250 f->_timestamp = std::max(f->_timestamp + 1, time(nullptr));
251 }
252
253 return new OSubStream(&f->_wrapper, 0, 0);
254}
255
256/**
257 * Works like open_write_file(), but the file is opened in append mode. Like
258 * open_write_file, the returned pointer should eventually be passed to
259 * close_write_file().
260 */
262open_append_file(const Filename &file) {
263 _lock.lock();
264 PT(File) f = _root.do_create_file(file);
265 _lock.unlock();
266 if (f == nullptr) {
267 return nullptr;
268 }
269
270 return new OSubStream(&f->_wrapper, 0, 0, true);
271}
272
273/**
274 * Opens the file for writing. Returns a newly allocated iostream on success
275 * (which you should eventually delete when you are done writing). Returns
276 * NULL on failure.
277 */
279open_read_write_file(const Filename &file, bool truncate) {
280 _lock.lock();
281 PT(File) f = _root.do_create_file(file);
282 _lock.unlock();
283 if (f == nullptr) {
284 return nullptr;
285 }
286
287 if (truncate) {
288 // Reset to an empty string.
289 f->_data.str(string());
290
291 // See open_write_file
292 f->_timestamp = std::max(f->_timestamp + 1, time(nullptr));
293 }
294
295 return new SubStream(&f->_wrapper, 0, 0);
296}
297
298/**
299 * Works like open_read_write_file(), but the file is opened in append mode.
300 * Like open_read_write_file, the returned pointer should eventually be passed
301 * to close_read_write_file().
302 */
304open_read_append_file(const Filename &file) {
305 _lock.lock();
306 PT(FileBase) f = _root.do_find_file(file);
307 _lock.unlock();
308 if (f == nullptr || f->is_directory()) {
309 return nullptr;
310 }
311
312 File *f2 = DCAST(File, f);
313 return new SubStream(&f2->_wrapper, 0, 0, true);
314}
315
316/**
317 * Returns the current size on disk (or wherever it is) of the already-open
318 * file. Pass in the stream that was returned by open_read_file(); some
319 * implementations may require this stream to determine the size.
320 */
322get_file_size(const Filename &file, istream *stream) const {
323 _lock.lock();
324 PT(FileBase) f = _root.do_find_file(file);
325 _lock.unlock();
326 if (f == nullptr || f->is_directory()) {
327 return 0;
328 }
329
330 File *f2 = DCAST(File, f);
331 return f2->_data.str().length();
332}
333
334/**
335 * Returns the current size on disk (or wherever it is) of the file before it
336 * has been opened.
337 */
339get_file_size(const Filename &file) const {
340 _lock.lock();
341 PT(FileBase) f = _root.do_find_file(file);
342 _lock.unlock();
343 if (f == nullptr || f->is_directory()) {
344 return 0;
345 }
346
347 File *f2 = DCAST(File, f);
348 return f2->_data.str().length();
349}
350
351/**
352 * Returns a time_t value that represents the time the file was last modified,
353 * to within whatever precision the operating system records this information
354 * (on a Windows95 system, for instance, this may only be accurate to within 2
355 * seconds).
356 *
357 * If the timestamp cannot be determined, either because it is not supported
358 * by the operating system or because there is some error (such as file not
359 * found), returns 0.
360 */
362get_timestamp(const Filename &file) const {
363 _lock.lock();
364 PT(FileBase) f = _root.do_find_file(file);
365 if (f.is_null()) {
366 _lock.unlock();
367 return 0;
368 }
369 time_t timestamp = f->_timestamp;
370 _lock.unlock();
371 return timestamp;
372}
373
374/**
375 * Fills the given vector up with the list of filenames that are local to this
376 * directory, if the filename is a directory. Returns true if successful, or
377 * false if the file is not a directory or cannot be read.
378 */
380scan_directory(vector_string &contents, const Filename &dir) const {
381 _lock.lock();
382 PT(FileBase) f = _root.do_find_file(dir);
383 if (f == nullptr || !f->is_directory()) {
384 _lock.unlock();
385 return false;
386 }
387
388 Directory *f2 = DCAST(Directory, f);
389 bool result = f2->do_scan_directory(contents);
390
391 _lock.unlock();
392 return result;
393}
394
395/**
396 * See Filename::atomic_compare_and_exchange_contents().
397 */
399atomic_compare_and_exchange_contents(const Filename &file, string &orig_contents,
400 const string &old_contents,
401 const string &new_contents) {
402 _lock.lock();
403 PT(FileBase) f = _root.do_find_file(file);
404 if (f == nullptr || f->is_directory()) {
405 _lock.unlock();
406 return false;
407 }
408
409 bool retval = false;
410 File *f2 = DCAST(File, f);
411 orig_contents = f2->_data.str();
412 if (orig_contents == old_contents) {
413 f2->_data.str(new_contents);
414 f2->_timestamp = time(nullptr);
415 retval = true;
416 }
417
418 _lock.unlock();
419 return retval;
420}
421
422/**
423 * See Filename::atomic_read_contents().
424 */
426atomic_read_contents(const Filename &file, string &contents) const {
427 _lock.lock();
428 PT(FileBase) f = _root.do_find_file(file);
429 if (f == nullptr || f->is_directory()) {
430 _lock.unlock();
431 return false;
432 }
433
434 File *f2 = DCAST(File, f);
435 contents = f2->_data.str();
436
437 _lock.unlock();
438 return true;
439}
440
441
442/**
443 *
444 */
445void VirtualFileMountRamdisk::
446output(ostream &out) const {
447 out << "VirtualFileMountRamdisk";
448}
449
450/**
451 *
452 */
453VirtualFileMountRamdisk::FileBase::
454~FileBase() {
455}
456
457/**
458 *
459 */
460bool VirtualFileMountRamdisk::FileBase::
461is_directory() const {
462 return false;
463}
464
465/**
466 *
467 */
468bool VirtualFileMountRamdisk::Directory::
469is_directory() const {
470 return true;
471}
472
473/**
474 * Recursively search for the file with the indicated name in this directory
475 * hierarchy.
476 */
477PT(VirtualFileMountRamdisk::FileBase) VirtualFileMountRamdisk::Directory::
478do_find_file(const string &filename) const {
479 size_t slash = filename.find('/');
480 if (slash == string::npos) {
481 if (filename.empty()) {
482 return (FileBase *)this;
483 }
484
485 // Search for a file within the local directory.
486 FileBase tfile(filename);
487 tfile.local_object();
488 Files::const_iterator fi = _files.find(&tfile);
489 if (fi != _files.end()) {
490 return (*fi);
491 }
492 return nullptr;
493 }
494
495 // A nested directory. Search for the directory name, then recurse.
496 string dirname = filename.substr(0, slash);
497 string remainder = filename.substr(slash + 1);
498 FileBase tfile(dirname);
499 tfile.local_object();
500 Files::const_iterator fi = _files.find(&tfile);
501 if (fi != _files.end()) {
502 PT(FileBase) file = (*fi);
503 if (file->is_directory()) {
504 return DCAST(Directory, file.p())->do_find_file(remainder);
505 }
506 }
507
508 return nullptr;
509}
510
511/**
512 * Recursively search for the file with the indicated name in this directory
513 * hierarchy. If not found, creates a new file.
514 */
515PT(VirtualFileMountRamdisk::File) VirtualFileMountRamdisk::Directory::
516do_create_file(const string &filename) {
517 size_t slash = filename.find('/');
518 if (slash == string::npos) {
519 // Search for a file within the local directory.
520 FileBase tfile(filename);
521 tfile.local_object();
522 Files::iterator fi = _files.find(&tfile);
523 if (fi != _files.end()) {
524 PT(FileBase) file = (*fi);
525 if (!file->is_directory()) {
526 return DCAST(File, file.p());
527 }
528 // Cannot create: a directory by the same name already exists.
529 return nullptr;
530 }
531
532 // Create a new file.
533 if (express_cat.is_debug()) {
534 express_cat.debug()
535 << "Making ramdisk file " << filename << "\n";
536 }
537 PT(File) file = new File(filename);
538 _files.insert(file.p());
539 _timestamp = time(nullptr);
540 return file;
541 }
542
543 // A nested directory. Search for the directory name, then recurse.
544 string dirname = filename.substr(0, slash);
545 string remainder = filename.substr(slash + 1);
546 FileBase tfile(dirname);
547 tfile.local_object();
548 Files::iterator fi = _files.find(&tfile);
549 if (fi != _files.end()) {
550 PT(FileBase) file = (*fi);
551 if (file->is_directory()) {
552 return DCAST(Directory, file.p())->do_create_file(remainder);
553 }
554 }
555
556 return nullptr;
557}
558
559/**
560 * Recursively search for the file with the indicated name in this directory
561 * hierarchy. If not found, creates a new directory.
562 */
563PT(VirtualFileMountRamdisk::Directory) VirtualFileMountRamdisk::Directory::
564do_make_directory(const string &filename) {
565 size_t slash = filename.find('/');
566 if (slash == string::npos) {
567 // Search for a file within the local directory.
568 FileBase tfile(filename);
569 tfile.local_object();
570 Files::iterator fi = _files.find(&tfile);
571 if (fi != _files.end()) {
572 PT(FileBase) file = (*fi);
573 if (file->is_directory()) {
574 return DCAST(Directory, file.p());
575 }
576 // Cannot create: a file by the same name already exists.
577 return nullptr;
578 }
579
580 // Create a new directory.
581 if (express_cat.is_debug()) {
582 express_cat.debug()
583 << "Making ramdisk directory " << filename << "\n";
584 }
585 PT(Directory) file = new Directory(filename);
586 _files.insert(file.p());
587 _timestamp = time(nullptr);
588 return file;
589 }
590
591 // A nested directory. Search for the directory name, then recurse.
592 string dirname = filename.substr(0, slash);
593 string remainder = filename.substr(slash + 1);
594 FileBase tfile(dirname);
595 tfile.local_object();
596 Files::iterator fi = _files.find(&tfile);
597 if (fi != _files.end()) {
598 PT(FileBase) file = (*fi);
599 if (file->is_directory()) {
600 return DCAST(Directory, file.p())->do_make_directory(remainder);
601 }
602 }
603
604 return nullptr;
605}
606
607/**
608 * Recursively search for the file with the indicated name in this directory
609 * hierarchy, and removes it. Returns the removed FileBase object.
610 */
611PT(VirtualFileMountRamdisk::FileBase) VirtualFileMountRamdisk::Directory::
612do_delete_file(const string &filename) {
613 size_t slash = filename.find('/');
614 if (slash == string::npos) {
615 // Search for a file within the local directory.
616 FileBase tfile(filename);
617 tfile.local_object();
618 Files::iterator fi = _files.find(&tfile);
619 if (fi != _files.end()) {
620 PT(FileBase) file = (*fi);
621 if (file->is_directory()) {
622 Directory *dir = DCAST(Directory, file.p());
623 if (!dir->_files.empty()) {
624 // Can't delete a nonempty directory.
625 return nullptr;
626 }
627 }
628 _files.erase(fi);
629 _timestamp = time(nullptr);
630 return file;
631 }
632 return nullptr;
633 }
634
635 // A nested directory. Search for the directory name, then recurse.
636 string dirname = filename.substr(0, slash);
637 string remainder = filename.substr(slash + 1);
638 FileBase tfile(dirname);
639 tfile.local_object();
640 Files::iterator fi = _files.find(&tfile);
641 if (fi != _files.end()) {
642 PT(FileBase) file = (*fi);
643 if (file->is_directory()) {
644 return DCAST(Directory, file.p())->do_delete_file(remainder);
645 }
646 }
647
648 return nullptr;
649}
650
651/**
652 *
653 */
654bool VirtualFileMountRamdisk::Directory::
655do_scan_directory(vector_string &contents) const {
656 Files::const_iterator fi;
657 for (fi = _files.begin(); fi != _files.end(); ++fi) {
658 FileBase *file = (*fi);
659 contents.push_back(file->_basename);
660 }
661
662 return true;
663}
The name of a file, such as a texture file or an Egg file.
Definition filename.h:44
An istream object that presents a subwindow into another istream.
Definition subStream.h:30
An ostream object that presents a subwindow into another ostream.
Definition subStream.h:55
Combined ISubStream and OSubStream for bidirectional I/O.
Definition subStream.h:74
TypeHandle is the identifier used to differentiate C++ class types.
Definition typeHandle.h:81
virtual bool atomic_read_contents(const Filename &file, std::string &contents) const
See Filename::atomic_read_contents().
virtual std::iostream * open_read_append_file(const Filename &file)
Works like open_read_write_file(), but the file is opened in append mode.
virtual bool scan_directory(vector_string &contents, const Filename &dir) const
Fills the given vector up with the list of filenames that are local to this directory,...
virtual bool is_directory(const Filename &file) const
Returns true if the indicated file exists within the mount system and is a directory.
virtual bool make_directory(const Filename &file)
Attempts to create the indicated file within the mount, if it does not already exist.
virtual time_t get_timestamp(const Filename &file) const
Returns a time_t value that represents the time the file was last modified, to within whatever precis...
virtual bool create_file(const Filename &file)
Attempts to create the indicated file within the mount, if it does not already exist.
virtual bool is_writable(const Filename &file) const
Returns true if the named file or directory may be written to, false otherwise.
virtual std::iostream * open_read_write_file(const Filename &file, bool truncate)
Opens the file for writing.
virtual bool delete_file(const Filename &file)
Attempts to delete the indicated file or directory within the mount.
virtual bool copy_file(const Filename &orig_filename, const Filename &new_filename)
Attempts to copy the contents of the indicated file to the indicated file.
virtual bool has_file(const Filename &file) const
Returns true if the indicated file exists within the mount system.
virtual bool atomic_compare_and_exchange_contents(const Filename &file, std::string &orig_contents, const std::string &old_contents, const std::string &new_contents)
See Filename::atomic_compare_and_exchange_contents().
virtual std::istream * open_read_file(const Filename &file) const
Opens the file for reading, if it exists.
virtual std::ostream * open_append_file(const Filename &file)
Works like open_write_file(), but the file is opened in append mode.
virtual bool rename_file(const Filename &orig_filename, const Filename &new_filename)
Attempts to rename the contents of the indicated file to the indicated file.
virtual std::streamsize get_file_size(const Filename &file, std::istream *stream) const
Returns the current size on disk (or wherever it is) of the already-open file.
virtual std::ostream * open_write_file(const Filename &file, bool truncate)
Opens the file for writing.
virtual bool is_regular_file(const Filename &file) const
Returns true if the indicated file exists within the mount system and is a regular file.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.