more writable vfs stuff

This commit is contained in:
David Rose 2011-09-27 16:10:19 +00:00
parent 9a7af0f435
commit 5496c4a9ec
27 changed files with 2011 additions and 181 deletions

View File

@ -224,11 +224,15 @@ class file:
xreadlines = readlines
def seek(self, offset, whence = 0):
print "seek(%s)" % (offset)
if self.__stream:
print "clear"
self.__stream.clear() # clear eof flag
if self.__reader:
print "seekg"
self.__stream.seekg(offset, whence)
if self.__writer:
print "seekp"
self.__stream.seekp(offset, whence)
def tell(self):

View File

@ -216,6 +216,42 @@ seek_write(streamsize pos, const char *buffer, streamsize num_bytes,
release();
}
////////////////////////////////////////////////////////////////////
// Function: OStreamWrapper::seek_eof_write
// Access: Public
// Description: Atomically seeks to the end of the file, and writes a
// number of bytes to the stream. Returns whether a
// failure condition was detected by the operation.
////////////////////////////////////////////////////////////////////
void OStreamWrapper::
seek_eof_write(const char *buffer, streamsize num_bytes, bool &fail) {
acquire();
_ostream->clear();
_ostream->seekp(0, ios::end);
_ostream->write(buffer, num_bytes);
fail = _ostream->fail();
release();
}
////////////////////////////////////////////////////////////////////
// Function: OStreamWrapper::seek_ppos_eof
// Access: Public
// Description: Atomically seeks to EOF and returns the ppos there;
// that is, returns the file size. Note that the EOF
// might have been moved in another thread by the time
// this method returns.
////////////////////////////////////////////////////////////////////
streamsize OStreamWrapper::
seek_ppos_eof() {
streamsize pos;
acquire();
_ostream->seekp(0, ios::end);
pos = _ostream->tellp();
release();
return pos;
}
////////////////////////////////////////////////////////////////////
// Function: StreamWrapper::Destructor
// Access: Published

View File

@ -91,7 +91,9 @@ public:
void write(const char *buffer, streamsize num_bytes);
void write(const char *buffer, streamsize num_bytes, bool &fail);
void seek_write(streamsize pos, const char *buffer, streamsize num_bytes, bool &fail);
void seek_eof_write(const char *buffer, streamsize num_bytes, bool &fail);
INLINE bool put(char c);
streamsize seek_ppos_eof();
private:
ostream *_ostream;

View File

@ -234,11 +234,9 @@ collapse_equivalent_materials() {
////////////////////////////////////////////////////////////////////
bool EggData::
write_egg(Filename filename) {
filename.unlink();
filename.set_text();
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
filename.set_text();
vfs->delete_file(filename);
ostream *file = vfs->open_write_file(filename, true, true);
if (file == (ostream *)NULL) {
egg_cat.error() << "Unable to open " << filename << " for writing.\n";

View File

@ -63,8 +63,10 @@
virtualFile.I virtualFileList.I virtualFileList.h virtualFileMount.h \
virtualFileComposite.h virtualFileComposite.I virtualFile.h \
virtualFileMount.I virtualFileMountMultifile.h \
virtualFileMountMultifile.I virtualFileMountSystem.h \
virtualFileMountSystem.I virtualFileSimple.h virtualFileSimple.I \
virtualFileMountMultifile.I \
virtualFileMountRamdisk.h virtualFileMountRamdisk.I \
virtualFileMountSystem.h virtualFileMountSystem.I \
virtualFileSimple.h virtualFileSimple.I \
virtualFileSystem.h virtualFileSystem.I \
weakPointerCallback.I weakPointerCallback.h \
weakPointerTo.I weakPointerTo.h \
@ -116,7 +118,9 @@
vector_uchar.cxx vector_float.cxx \
virtualFileComposite.cxx virtualFile.cxx virtualFileList.cxx \
virtualFileMount.cxx \
virtualFileMountMultifile.cxx virtualFileMountSystem.cxx \
virtualFileMountMultifile.cxx \
virtualFileMountRamdisk.cxx \
virtualFileMountSystem.cxx \
virtualFileSimple.cxx virtualFileSystem.cxx \
weakPointerCallback.cxx \
weakPointerTo.cxx \
@ -180,8 +184,10 @@
virtualFile.I virtualFileList.I virtualFileList.h virtualFileMount.h \
virtualFileComposite.h virtualFileComposite.I virtualFile.h \
virtualFileMount.I virtualFileMountMultifile.h \
virtualFileMountMultifile.I virtualFileMountSystem.h \
virtualFileMountSystem.I virtualFileSimple.h virtualFileSimple.I \
virtualFileMountMultifile.I \
virtualFileMountRamdisk.h virtualFileMountRamdisk.I \
virtualFileMountSystem.h virtualFileMountSystem.I \
virtualFileSimple.h virtualFileSimple.I \
virtualFileSystem.h virtualFileSystem.I \
weakPointerCallback.I weakPointerCallback.h \
weakPointerTo.I weakPointerTo.h \

View File

@ -23,6 +23,7 @@
#include "virtualFileComposite.h"
#include "virtualFileMount.h"
#include "virtualFileMountMultifile.h"
#include "virtualFileMountRamdisk.h"
#include "virtualFileMountSystem.h"
#include "virtualFileSimple.h"
#include "fileReference.h"
@ -106,6 +107,7 @@ init_libexpress() {
VirtualFileComposite::init_type();
VirtualFileMount::init_type();
VirtualFileMountMultifile::init_type();
VirtualFileMountRamdisk::init_type();
VirtualFileMountSystem::init_type();
VirtualFileSimple::init_type();
FileReference::init_type();

View File

@ -18,6 +18,7 @@
#include "virtualFileList.cxx"
#include "virtualFileMount.cxx"
#include "virtualFileMountMultifile.cxx"
#include "virtualFileMountRamdisk.cxx"
#include "virtualFileMountSystem.cxx"
#include "virtualFileSimple.cxx"
#include "virtualFileSystem.cxx"

View File

@ -48,7 +48,7 @@ ISubStream(IStreamWrapper *source, streampos start, streampos end) : istream(&_b
INLINE ISubStream &ISubStream::
open(IStreamWrapper *source, streampos start, streampos end) {
clear((ios_iostate)0);
_buf.open(source, start, end);
_buf.open(source, NULL, start, end, false);
return *this;
}
@ -64,3 +64,103 @@ close() {
return *this;
}
////////////////////////////////////////////////////////////////////
// Function: OSubStream::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE OSubStream::
OSubStream() : ostream(&_buf) {
}
////////////////////////////////////////////////////////////////////
// Function: OSubStream::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE OSubStream::
OSubStream(OStreamWrapper *dest, streampos start, streampos end, bool append) : ostream(&_buf) {
open(dest, start, end, append);
}
////////////////////////////////////////////////////////////////////
// Function: OSubStream::open
// Access: Public
// Description: Starts the SubStream reading from the indicated
// dest, with the first character being the character
// at position "start" within the dest, for end -
// start total characters. The character at "end"
// within the dest will never be read; this will
// appear to be EOF.
//
// If end is zero, it indicates that the OSubStream will
// continue until the end of the dest stream.
////////////////////////////////////////////////////////////////////
INLINE OSubStream &OSubStream::
open(OStreamWrapper *dest, streampos start, streampos end, bool append) {
clear((ios_iostate)0);
_buf.open(NULL, dest, start, end, append);
return *this;
}
////////////////////////////////////////////////////////////////////
// Function: OSubStream::close
// Access: Public
// Description: Resets the SubStream to empty, but does not actually
// close the dest ostream.
////////////////////////////////////////////////////////////////////
INLINE OSubStream &OSubStream::
close() {
_buf.close();
return *this;
}
////////////////////////////////////////////////////////////////////
// Function: SubStream::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE SubStream::
SubStream() : iostream(&_buf) {
}
////////////////////////////////////////////////////////////////////
// Function: SubStream::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE SubStream::
SubStream(StreamWrapper *nested, streampos start, streampos end, bool append) : iostream(&_buf) {
open(nested, start, end, append);
}
////////////////////////////////////////////////////////////////////
// Function: SubStream::open
// Access: Public
// Description: Starts the SubStream reading and writing from the
// indicated nested stream, within the indicated range.
// "end" is the first character outside of the range.
//
// If end is zero, it indicates that the SubStream will
// continue until the end of the nested stream.
////////////////////////////////////////////////////////////////////
INLINE SubStream &SubStream::
open(StreamWrapper *nested, streampos start, streampos end, bool append) {
clear((ios_iostate)0);
_buf.open(nested, nested, start, end, append);
return *this;
}
////////////////////////////////////////////////////////////////////
// Function: SubStream::close
// Access: Public
// Description: Resets the SubStream to empty, but does not actually
// close the nested ostream.
////////////////////////////////////////////////////////////////////
INLINE SubStream &SubStream::
close() {
_buf.close();
return *this;
}

View File

@ -43,6 +43,47 @@ private:
SubStreamBuf _buf;
};
////////////////////////////////////////////////////////////////////
// Class : OSubStream
// Description : An ostream object that presents a subwindow into
// another ostream. The first character written to this
// stream will be the "start" character in the dest
// istream; no characters may be written to character
// "end" or later (unless end is zero).
//
// The dest stream must be one that we can randomly
// seek within. The resulting OSubStream will also
// support arbitrary seeks.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDAEXPRESS OSubStream : public ostream {
PUBLISHED:
INLINE OSubStream();
INLINE OSubStream(OStreamWrapper *dest, streampos start, streampos end, bool append = false);
INLINE OSubStream &open(OStreamWrapper *dest, streampos start, streampos end, bool append = false);
INLINE OSubStream &close();
private:
SubStreamBuf _buf;
};
////////////////////////////////////////////////////////////////////
// Class : SubStream
// Description : Combined ISubStream and OSubStream for bidirectional
// I/O.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDAEXPRESS SubStream : public iostream {
PUBLISHED:
INLINE SubStream();
INLINE SubStream(StreamWrapper *nested, streampos start, streampos end, bool append = false);
INLINE SubStream &open(StreamWrapper *nested, streampos start, streampos end, bool append = false);
INLINE SubStream &close();
private:
SubStreamBuf _buf;
};
#include "subStream.I"
#endif

View File

@ -20,6 +20,8 @@
typedef int streamsize;
#endif /* HAVE_STREAMSIZE */
static const size_t substream_buffer_size = 4096;
////////////////////////////////////////////////////////////////////
// Function: SubStreamBuf::Constructor
// Access: Public
@ -28,6 +30,7 @@ typedef int streamsize;
SubStreamBuf::
SubStreamBuf() {
_source = (IStreamWrapper *)NULL;
_dest = (OStreamWrapper *)NULL;
// _start is the streampos of the first byte of the SubStream within
// its parent stream.
@ -38,28 +41,29 @@ SubStreamBuf() {
// continues to the end of the parent stream, wherever that is.
_end = 0;
// _cur is the streampos of the end of the read buffer (that is,
// egptr()) within the parent stream. By comparing _cur to gpos(),
// we can determine the actual current file position.
_cur = 0;
// _unused counts the number of bytes at the beginning of the buffer
// that are unused. Usually this is 0 after a read, but when we
// reach the end of the file we might not need the whole buffer to
// read the last bit, so the first part of the buffer is unused.
// This is important to prevent us from inadvertently seeking into
// the unused part of the buffer.
_unused = 0;
// _gpos is the streampos of the end of the read buffer (that is,
// egptr()) within the parent stream. By comparing _gpos to gpos(),
// we can determine the actual current file position. _ppos is the
// similar pos, for the write pointer.
_gpos = 0;
_ppos = 0;
#ifdef PHAVE_IOSTREAM
// The new-style iostream library doesn't seem to support allocate().
_buffer = (char *)PANDA_MALLOC_ARRAY(4096);
char *ebuf = _buffer + 4096;
setg(_buffer, ebuf, ebuf);
_buffer = (char *)PANDA_MALLOC_ARRAY(substream_buffer_size * 2);
char *ebuf = _buffer + substream_buffer_size * 2;
char *mbuf = _buffer + substream_buffer_size;
setg(_buffer, mbuf, mbuf);
setp(mbuf, ebuf);
#else
allocate();
setg(base(), ebuf(), ebuf());
// Chop the buffer in half. The bottom half goes to the get buffer;
// the top half goes to the put buffer.
char *b = base();
char *t = ebuf();
char *m = b + (t - b) / 2;
setg(b, m, m);
setp(b, m);
#endif
}
@ -82,14 +86,14 @@ SubStreamBuf::
// Description:
////////////////////////////////////////////////////////////////////
void SubStreamBuf::
open(IStreamWrapper *source, streampos start, streampos end) {
open(IStreamWrapper *source, OStreamWrapper *dest, streampos start, streampos end, bool append) {
_source = source;
_dest = dest;
_start = start;
_end = end;
_cur = _start;
// Initially, the entire buffer is unused. Duh.
_unused = egptr() - eback();
_append = append;
_gpos = _start;
_ppos = _start;
}
////////////////////////////////////////////////////////////////////
@ -99,11 +103,19 @@ open(IStreamWrapper *source, streampos start, streampos end) {
////////////////////////////////////////////////////////////////////
void SubStreamBuf::
close() {
// Make sure the write buffer is flushed.
sync();
_source = (IStreamWrapper *)NULL;
_dest = (OStreamWrapper *)NULL;
_start = 0;
_end = 0;
_cur = 0;
_unused = 0;
_gpos = 0;
_ppos = 0;
pbump(pbase() - pptr());
gbump(egptr() - gptr());
}
////////////////////////////////////////////////////////////////////
@ -112,55 +124,110 @@ close() {
// Description: Implements seeking within the stream.
////////////////////////////////////////////////////////////////////
streampos SubStreamBuf::
seekoff(streamoff off, ios_seekdir dir, ios_openmode mode) {
// Invariant: _cur points to the file location of the buffer at
// egptr().
seekoff(streamoff off, ios_seekdir dir, ios_openmode which) {
streampos result = -1;
// Use this to determine the actual file position right now.
size_t n = egptr() - gptr();
streampos cur_pos = _cur - (streampos)n;
streampos new_pos = cur_pos;
// Sync the iostream buffer first.
sync();
// Now adjust the data pointer appropriately.
// Casting this to int to prevent GCC 3.2 compiler warnings. Very
// suspicious, need to investigate further.
switch ((int)dir) {
case ios::beg:
new_pos = _start + off;
break;
case ios::cur:
new_pos = cur_pos + off;
break;
case ios::end:
if (_end == (streampos)0) {
// If the end of the file is unspecified, we have to seek to
// find it.
new_pos = _source->seek_gpos_eof() + off;
} else {
new_pos = _end + off;
}
break;
}
new_pos = max(_start, new_pos);
streamsize delta = new_pos - cur_pos;
if (gptr() + delta >= eback() + _unused && gptr() + delta <= egptr()) {
// If we can get away with just bumping the gptr within the
// buffer, do so.
gbump(delta);
} else {
// Otherwise, empty the buffer and force it to call underflow().
if (which & ios::in) {
// Determine the current file position.
size_t n = egptr() - gptr();
gbump(n);
_cur = new_pos;
_gpos -= n;
nassertr(_gpos >= 0, EOF);
streampos cur_pos = _gpos;
streampos new_pos = cur_pos;
// Now adjust the data pointer appropriately.
switch (dir) {
case ios::beg:
new_pos = (streampos)off + _start;
break;
case ios::cur:
new_pos = (streampos)((streamoff)cur_pos + off);
break;
case ios::end:
if (_end == (streampos)0) {
// If the end of the file is unspecified, we have to seek to
// find it.
new_pos = _source->seek_gpos_eof() + off;
} else {
new_pos = _end + off;
}
break;
default:
// Shouldn't get here.
break;
}
if (new_pos < _start) {
// Can't seek before beginning of file.
return EOF;
}
if (_end != 0 && new_pos > _end) {
// Can't seek past end of file.
return EOF;
}
_gpos = new_pos;
nassertr(_gpos >= 0, EOF);
result = new_pos - _start;
}
return new_pos - _start;
if (which & ios::out) {
// Determine the current file position.
size_t n = pptr() - pbase();
streampos cur_pos = _ppos + (streamoff)n;
streampos new_pos = cur_pos;
// Now adjust the data pointer appropriately.
switch (dir) {
case ios::beg:
new_pos = (streampos)off + _start;
break;
case ios::cur:
new_pos = (streampos)((streamoff)cur_pos + off);
break;
case ios::end:
if (_end == (streampos)0) {
// If the end of the file is unspecified, we have to seek to
// find it.
new_pos = _dest->seek_ppos_eof() + off;
} else {
new_pos = _end + off;
}
break;
default:
// Shouldn't get here.
break;
}
if (new_pos < _start) {
// Can't seek before beginning of file.
return EOF;
}
if (_end != 0 && new_pos > _end) {
// Can't seek past end of file.
return EOF;
}
_ppos = new_pos;
nassertr(_ppos >= 0, EOF);
result = new_pos - _start;
}
return result;
}
////////////////////////////////////////////////////////////////////
@ -178,8 +245,8 @@ seekoff(streamoff off, ios_seekdir dir, ios_openmode mode) {
// function as well.
////////////////////////////////////////////////////////////////////
streampos SubStreamBuf::
seekpos(streampos pos, ios_openmode mode) {
return seekoff(pos, ios::beg, mode);
seekpos(streampos pos, ios_openmode which) {
return seekoff(pos, ios::beg, which);
}
////////////////////////////////////////////////////////////////////
@ -189,8 +256,48 @@ seekpos(streampos pos, ios_openmode mode) {
// internal buffer is filled, plus one character.
////////////////////////////////////////////////////////////////////
int SubStreamBuf::
overflow(int c) {
// We don't support ostream.
overflow(int ch) {
bool okflag = true;
size_t n = pptr() - pbase();
if (n != 0) {
if (_end != 0 && _ppos + (streampos)n > _end) {
// Don't allow reading past the end of the file.
n = (size_t)(_end - _ppos);
if (n == 0) {
// No room.
return EOF;
}
}
nassertr(_dest != NULL, EOF);
bool fail = false;
if (_append) {
_dest->seek_eof_write(pbase(), n, fail);
} else {
_dest->seek_write(_ppos, pbase(), n, fail);
}
_ppos += n;
pbump(-(int)n);
if (fail) {
okflag = false;
}
}
if (okflag && ch != EOF) {
if (pptr() != epptr()) {
// Store the extra character back in the buffer.
*(pptr()) = ch;
pbump(1);
} else {
// No room to store ch.
okflag = false;
}
}
if (!okflag) {
return EOF;
}
return 0;
}
@ -202,8 +309,23 @@ overflow(int c) {
////////////////////////////////////////////////////////////////////
int SubStreamBuf::
sync() {
size_t n = egptr() - gptr();
gbump(n);
size_t n = pptr() - pbase();
if (n != 0) {
nassertr(_dest != NULL, EOF);
bool fail = false;
if (_append) {
_dest->seek_eof_write(pbase(), n, fail);
} else {
_dest->seek_write(_ppos, pbase(), n, fail);
}
_ppos += n;
pbump(-(int)n);
if (fail) {
return EOF;
}
}
return 0;
}
@ -218,54 +340,48 @@ int SubStreamBuf::
underflow() {
// Sometimes underflow() is called even if the buffer is not empty.
if (gptr() >= egptr()) {
if (_end != (streampos)0 && _cur >= _end) {
// We're done.
return EOF;
}
sync();
// Mark the buffer filled (with buffer_size bytes).
size_t buffer_size = egptr() - eback();
size_t num_bytes;
if (_end == (streampos)0 || _end - _cur > (streampos)buffer_size) {
// We have enough bytes in the input stream to fill our buffer.
num_bytes = buffer_size;
} else {
// We won't quite fill the buffer.
num_bytes = (size_t)(_end - _cur);
gbump(-(int)buffer_size);
streamsize num_bytes = buffer_size;
if (_end != 0 && _gpos + (streampos)num_bytes > _end) {
// Don't allow reading past the end of the file.
streamsize new_num_bytes = _end - _gpos;
if (new_num_bytes == 0) {
gbump(buffer_size);
return EOF;
}
// We won't be filling the entire buffer. Fill in only at the
// end of the buffer.
size_t delta = num_bytes - new_num_bytes;
gbump(delta);
num_bytes = new_num_bytes;
nassertr(egptr() - gptr() == num_bytes, EOF);
}
gbump(-(int)num_bytes);
nassertr(gptr() + num_bytes <= egptr(), EOF);
nassertr(_source != NULL, EOF);
streamsize read_count;
bool eof;
_source->seek_read(_cur, gptr(), num_bytes, read_count, eof);
_source->seek_read(_gpos, gptr(), num_bytes, read_count, eof);
_gpos += read_count;
if (read_count != (streamsize)num_bytes) {
if (read_count != num_bytes) {
// Oops, we didn't read what we thought we would.
if (read_count == 0) {
_unused = buffer_size;
if (_end != (streampos)0) {
_end = _cur;
}
gbump(num_bytes);
return EOF;
}
// Slide what we did read to the top of the buffer.
nassertr(read_count < (int)num_bytes, EOF);
nassertr(read_count < num_bytes, EOF);
size_t delta = num_bytes - read_count;
memmove(gptr() + delta, gptr(), read_count);
gbump(delta);
}
// Now record whatever bytes at the beginning of the buffer are
// unused, so we won't try to seek into that area.
_unused = buffer_size - read_count;
// Invariant: _cur points to the file location of the buffer at
// egptr().
_cur += read_count;
}
return (unsigned char)*gptr();

View File

@ -27,11 +27,11 @@ public:
SubStreamBuf();
virtual ~SubStreamBuf();
void open(IStreamWrapper *source, streampos start, streampos end);
void open(IStreamWrapper *source, OStreamWrapper *dest, streampos start, streampos end, bool append);
void close();
virtual streampos seekoff(streamoff off, ios_seekdir dir, ios_openmode mode);
virtual streampos seekpos(streampos pos, ios_openmode mode);
virtual streampos seekoff(streamoff off, ios_seekdir dir, ios_openmode which);
virtual streampos seekpos(streampos pos, ios_openmode which);
protected:
virtual int overflow(int c);
@ -40,10 +40,12 @@ protected:
private:
IStreamWrapper *_source;
OStreamWrapper *_dest;
streampos _start;
streampos _end;
streampos _cur;
size_t _unused;
bool _append;
streampos _gpos;
streampos _ppos;
char *_buffer;
};

View File

@ -64,6 +64,53 @@ is_writable() const {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFile::delete_file
// Access: Public
// Description: Attempts to delete this file or directory. This can
// remove a single file or an empty directory. It will
// not remove a nonempty directory. Returns true on
// success, false on failure.
////////////////////////////////////////////////////////////////////
bool VirtualFile::
delete_file() {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFile::rename_file
// Access: Public
// Description: Attempts to move or rename this file or directory.
// If the original file is an ordinary file, it will
// quietly replace any already-existing file in the new
// filename (but not a directory). If the original file
// is a directory, the new filename must not already
// exist.
//
// If the file is a directory, the new filename must be
// within the same mount point. If the file is an
// ordinary file, the new filename may be anywhere; but
// if it is not within the same mount point then the
// rename operation is automatically performed as a
// two-step copy-and-delete operation.
////////////////////////////////////////////////////////////////////
bool VirtualFile::
rename_file(VirtualFile *new_file) {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFile::copy_file
// Access: Public
// Description: Attempts to copy the contents of this file to the
// indicated file. Returns true on success, false on
// failure.
////////////////////////////////////////////////////////////////////
bool VirtualFile::
copy_file(VirtualFile *new_file) {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFile::scan_directory
// Access: Published
@ -349,6 +396,28 @@ get_system_info(SubfileInfo &info) {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFile::atomic_compare_and_exchange_contents
// Access: Public, Virtual
// Description: See Filename::atomic_compare_and_exchange_contents().
////////////////////////////////////////////////////////////////////
bool VirtualFile::
atomic_compare_and_exchange_contents(string &orig_contents,
const string &old_contents,
const string &new_contents) {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFile::atomic_read_contents
// Access: Public, Virtual
// Description: See Filename::atomic_read_contents().
////////////////////////////////////////////////////////////////////
bool VirtualFile::
atomic_read_contents(string &contents) const {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFile::read_file
// Access: Public

View File

@ -48,6 +48,10 @@ PUBLISHED:
virtual bool is_regular_file() const;
virtual bool is_writable() const;
BLOCKING virtual bool delete_file();
BLOCKING virtual bool rename_file(VirtualFile *new_file);
BLOCKING virtual bool copy_file(VirtualFile *new_file);
BLOCKING PT(VirtualFileList) scan_directory() const;
void output(ostream &out) const;
@ -75,6 +79,9 @@ PUBLISHED:
virtual bool get_system_info(SubfileInfo &info);
public:
virtual bool atomic_compare_and_exchange_contents(string &orig_contents, const string &old_contents, const string &new_contents);
virtual bool atomic_read_contents(string &contents) const;
INLINE void set_original_filename(const Filename &filename);
bool read_file(string &result, bool auto_unwrap) const;
virtual bool read_file(pvector<unsigned char> &result, bool auto_unwrap) const;

View File

@ -75,6 +75,48 @@ create_file(const Filename &file) {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMount::delete_file
// Access: Public, Virtual
// Description: Attempts to delete the indicated file or directory
// within the mount. This can remove a single file or
// an empty directory. It will not remove a nonempty
// directory. Returns true on success, false on
// failure.
////////////////////////////////////////////////////////////////////
bool VirtualFileMount::
delete_file(const Filename &file) {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMount::rename_file
// Access: Public
// Description: Attempts to rename the contents of the indicated file
// to the indicated file. Both filenames will be within
// the mount. Returns true on success, false on
// failure. If this returns false, this will be
// attempted again with a copy-and-delete operation.
////////////////////////////////////////////////////////////////////
bool VirtualFileMount::
rename_file(const Filename &orig_filename, const Filename &new_filename) {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMount::copy_file
// Access: Public
// Description: Attempts to copy the contents of the indicated file
// to the indicated file. Both filenames will be within
// the mount. Returns true on success, false on
// failure. If this returns false, the copy will be
// performed by explicit read-and-write operations.
////////////////////////////////////////////////////////////////////
bool VirtualFileMount::
copy_file(const Filename &orig_filename, const Filename &new_filename) {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMount::make_directory
// Access: Public, Virtual
@ -324,6 +366,28 @@ get_system_info(const Filename &file, SubfileInfo &info) {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMount::atomic_compare_and_exchange_contents
// Access: Public, Virtual
// Description: See Filename::atomic_compare_and_exchange_contents().
////////////////////////////////////////////////////////////////////
bool VirtualFileMount::
atomic_compare_and_exchange_contents(const Filename &file, string &orig_contents,
const string &old_contents,
const string &new_contents) {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMount::atomic_read_contents
// Access: Public, Virtual
// Description: See Filename::atomic_read_contents().
////////////////////////////////////////////////////////////////////
bool VirtualFileMount::
atomic_read_contents(const Filename &file, string &contents) const {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMount::output
// Access: Public, Virtual

View File

@ -48,6 +48,9 @@ public:
virtual bool has_file(const Filename &file) const=0;
virtual bool create_file(const Filename &file);
virtual bool make_directory(const Filename &file);
virtual bool delete_file(const Filename &file);
virtual bool rename_file(const Filename &orig_filename, const Filename &new_filename);
virtual bool copy_file(const Filename &orig_filename, const Filename &new_filename);
virtual bool is_directory(const Filename &file) const=0;
virtual bool is_regular_file(const Filename &file) const=0;
virtual bool is_writable(const Filename &file) const;
@ -78,6 +81,9 @@ public:
virtual bool scan_directory(vector_string &contents,
const Filename &dir) const=0;
virtual bool atomic_compare_and_exchange_contents(const Filename &file, string &orig_contents, const string &old_contents, const string &new_contents);
virtual bool atomic_read_contents(const Filename &file, string &contents) const;
PUBLISHED:
virtual void output(ostream &out) const;
virtual void write(ostream &out) const;

View File

@ -0,0 +1,54 @@
// Filename: virtualFileMountRamdisk.I
// Created by: drose (19Sep11)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::FileBase::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE VirtualFileMountRamdisk::FileBase::
FileBase(const string &basename) : _basename(basename), _timestamp(0) {
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::FileBase::operator <
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE bool VirtualFileMountRamdisk::FileBase::
operator < (const FileBase &other) const {
return _basename < other._basename;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::File::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE VirtualFileMountRamdisk::File::
File(const string &basename) :
FileBase(basename),
_wrapper(_data)
{
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::Directory::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE VirtualFileMountRamdisk::Directory::
Directory(const string &basename) : FileBase(basename) {
}

View File

@ -0,0 +1,707 @@
// Filename: virtualFileMountRamdisk.cxx
// Created by: drose (19Sep11)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "virtualFileMountRamdisk.h"
#include "subStream.h"
#include "dcast.h"
TypeHandle VirtualFileMountRamdisk::_type_handle;
TypeHandle VirtualFileMountRamdisk::FileBase::_type_handle;
TypeHandle VirtualFileMountRamdisk::File::_type_handle;
TypeHandle VirtualFileMountRamdisk::Directory::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
VirtualFileMountRamdisk::
VirtualFileMountRamdisk() : _root("") {
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::has_file
// Access: Public, Virtual
// Description: Returns true if the indicated file exists within the
// mount system.
////////////////////////////////////////////////////////////////////
bool VirtualFileMountRamdisk::
has_file(const Filename &file) const {
((VirtualFileMountRamdisk *)this)->_lock.acquire();
PT(FileBase) f = _root.do_find_file(file);
((VirtualFileMountRamdisk *)this)->_lock.release();
return (f != NULL);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::create_file
// Access: Public, Virtual
// Description: Attempts to create the indicated file within the
// mount, if it does not already exist. Returns true on
// success (or if the file already exists), or false if
// it cannot be created.
////////////////////////////////////////////////////////////////////
bool VirtualFileMountRamdisk::
create_file(const Filename &file) {
_lock.acquire();
PT(File) f = _root.do_create_file(file);
_lock.release();
return (f != NULL);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::delete_file
// Access: Public, Virtual
// Description: Attempts to delete the indicated file or directory
// within the mount. This can remove a single file or
// an empty directory. It will not remove a nonempty
// directory. Returns true on success, false on
// failure.
////////////////////////////////////////////////////////////////////
bool VirtualFileMountRamdisk::
delete_file(const Filename &file) {
_lock.acquire();
PT(FileBase) f = _root.do_delete_file(file);
_lock.release();
return (f != NULL);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::rename_file
// Access: Public
// Description: Attempts to rename the contents of the indicated file
// to the indicated file. Both filenames will be within
// the mount. Returns true on success, false on
// failure. If this returns false, this will be
// attempted again with a copy-and-delete operation.
////////////////////////////////////////////////////////////////////
bool VirtualFileMountRamdisk::
rename_file(const Filename &orig_filename, const Filename &new_filename) {
_lock.acquire();
PT(FileBase) orig_fb = _root.do_find_file(orig_filename);
if (orig_fb == NULL) {
_lock.release();
return false;
}
if (orig_fb->is_directory()) {
// Rename the directory.
Directory *orig_d = DCAST(Directory, orig_fb);
PT(Directory) new_d = _root.do_make_directory(new_filename);
if (new_d == NULL || !new_d->_files.empty()) {
_lock.release();
return false;
}
if (express_cat.is_debug()) {
express_cat.debug()
<< "Renaming ramdisk directory " << orig_filename << " to " << new_filename << "\n";
}
new_d->_files.swap(orig_d->_files);
_root.do_delete_file(orig_filename);
_lock.release();
return true;
}
// Rename the file.
File *orig_f = DCAST(File, orig_fb);
PT(File) new_f = _root.do_create_file(new_filename);
if (new_f == NULL) {
_lock.release();
return false;
}
if (express_cat.is_debug()) {
express_cat.debug()
<< "Renaming ramdisk file " << orig_filename << " to " << new_filename << "\n";
}
new_f->_data.str(orig_f->_data.str());
_root.do_delete_file(orig_filename);
_lock.release();
return true;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::copy_file
// Access: Public
// Description: Attempts to copy the contents of the indicated file
// to the indicated file. Both filenames will be within
// the mount. Returns true on success, false on
// failure. If this returns false, the copy will be
// performed by explicit read-and-write operations.
////////////////////////////////////////////////////////////////////
bool VirtualFileMountRamdisk::
copy_file(const Filename &orig_filename, const Filename &new_filename) {
_lock.acquire();
PT(FileBase) orig_fb = _root.do_find_file(orig_filename);
if (orig_fb == NULL || orig_fb->is_directory()) {
_lock.release();
return false;
}
// Copy the file.
File *orig_f = DCAST(File, orig_fb);
PT(File) new_f = _root.do_create_file(new_filename);
if (new_f == NULL) {
_lock.release();
return false;
}
if (express_cat.is_debug()) {
express_cat.debug()
<< "Copying ramdisk file " << orig_filename << " to " << new_filename << "\n";
}
new_f->_data.str(orig_f->_data.str());
_lock.release();
return true;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::make_directory
// Access: Public, Virtual
// Description: Attempts to create the indicated file within the
// mount, if it does not already exist. Returns true on
// success, or false if it cannot be created. If the
// directory already existed prior to this call, may
// return either true or false.
////////////////////////////////////////////////////////////////////
bool VirtualFileMountRamdisk::
make_directory(const Filename &file) {
_lock.acquire();
PT(Directory) f = _root.do_make_directory(file);
_lock.release();
return (f != NULL);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::is_directory
// Access: Public, Virtual
// Description: Returns true if the indicated file exists within the
// mount system and is a directory.
////////////////////////////////////////////////////////////////////
bool VirtualFileMountRamdisk::
is_directory(const Filename &file) const {
((VirtualFileMountRamdisk *)this)->_lock.acquire();
PT(FileBase) f = _root.do_find_file(file);
((VirtualFileMountRamdisk *)this)->_lock.release();
return (f != NULL && f->is_directory());
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::is_regular_file
// Access: Public, Virtual
// Description: Returns true if the indicated file exists within the
// mount system and is a regular file.
////////////////////////////////////////////////////////////////////
bool VirtualFileMountRamdisk::
is_regular_file(const Filename &file) const {
((VirtualFileMountRamdisk *)this)->_lock.acquire();
PT(FileBase) f = _root.do_find_file(file);
((VirtualFileMountRamdisk *)this)->_lock.release();
return (f != NULL && !f->is_directory());
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::is_writable
// Access: Public, Virtual
// Description: Returns true if the named file or directory may be
// written to, false otherwise.
////////////////////////////////////////////////////////////////////
bool VirtualFileMountRamdisk::
is_writable(const Filename &file) const {
return has_file(file);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::open_read_file
// Access: Public, Virtual
// Description: Opens the file for reading, if it exists. Returns a
// newly allocated istream on success (which you should
// eventually delete when you are done reading).
// Returns NULL on failure.
////////////////////////////////////////////////////////////////////
istream *VirtualFileMountRamdisk::
open_read_file(const Filename &file) const {
((VirtualFileMountRamdisk *)this)->_lock.acquire();
PT(FileBase) f = _root.do_find_file(file);
((VirtualFileMountRamdisk *)this)->_lock.release();
if (f == (FileBase *)NULL || f->is_directory()) {
return false;
}
File *f2 = DCAST(File, f);
return new ISubStream(&f2->_wrapper, 0, 0);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::open_write_file
// Access: Published, Virtual
// Description: Opens the file for writing. Returns a newly
// allocated ostream on success (which you should
// eventually delete when you are done writing).
// Returns NULL on failure.
////////////////////////////////////////////////////////////////////
ostream *VirtualFileMountRamdisk::
open_write_file(const Filename &file, bool truncate) {
_lock.acquire();
PT(File) f = _root.do_create_file(file);
_lock.release();
if (f == (File *)NULL) {
return false;
}
if (truncate) {
// Reset to an empty string.
f->_data.str(string());
}
return new OSubStream(&f->_wrapper, 0, 0);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::open_append_file
// Access: Published
// Description: Works like open_write_file(), but the file is opened
// in append mode. Like open_write_file, the returned
// pointer should eventually be passed to
// close_write_file().
////////////////////////////////////////////////////////////////////
ostream *VirtualFileMountRamdisk::
open_append_file(const Filename &file) {
_lock.acquire();
PT(File) f = _root.do_create_file(file);
_lock.release();
if (f == (File *)NULL) {
return false;
}
return new OSubStream(&f->_wrapper, 0, 0, true);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::open_read_write_file
// Access: Published, Virtual
// Description: Opens the file for writing. Returns a newly
// allocated iostream on success (which you should
// eventually delete when you are done writing).
// Returns NULL on failure.
////////////////////////////////////////////////////////////////////
iostream *VirtualFileMountRamdisk::
open_read_write_file(const Filename &file, bool truncate) {
_lock.acquire();
PT(File) f = _root.do_create_file(file);
_lock.release();
if (f == (File *)NULL) {
return false;
}
return new SubStream(&f->_wrapper, 0, 0);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::open_read_append_file
// Access: Published, Virtual
// Description: Works like open_read_write_file(), but the file is opened
// in append mode. Like open_read_write_file, the returned
// pointer should eventually be passed to
// close_read_write_file().
////////////////////////////////////////////////////////////////////
iostream *VirtualFileMountRamdisk::
open_read_append_file(const Filename &file) {
_lock.acquire();
PT(FileBase) f = _root.do_find_file(file);
_lock.release();
if (f == (FileBase *)NULL || f->is_directory()) {
return false;
}
File *f2 = DCAST(File, f);
return new SubStream(&f2->_wrapper, 0, 0, true);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::get_file_size
// Access: Published, Virtual
// Description: Returns the current size on disk (or wherever it is)
// of the already-open file. Pass in the stream that
// was returned by open_read_file(); some
// implementations may require this stream to determine
// the size.
////////////////////////////////////////////////////////////////////
off_t VirtualFileMountRamdisk::
get_file_size(const Filename &file, istream *stream) const {
((VirtualFileMountRamdisk *)this)->_lock.acquire();
PT(FileBase) f = _root.do_find_file(file);
((VirtualFileMountRamdisk *)this)->_lock.release();
if (f == (FileBase *)NULL || f->is_directory()) {
return false;
}
File *f2 = DCAST(File, f);
return f2->_data.str().length();
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::get_file_size
// Access: Published, Virtual
// Description: Returns the current size on disk (or wherever it is)
// of the file before it has been opened.
////////////////////////////////////////////////////////////////////
off_t VirtualFileMountRamdisk::
get_file_size(const Filename &file) const {
((VirtualFileMountRamdisk *)this)->_lock.acquire();
PT(FileBase) f = _root.do_find_file(file);
((VirtualFileMountRamdisk *)this)->_lock.release();
if (f == (FileBase *)NULL || f->is_directory()) {
return false;
}
File *f2 = DCAST(File, f);
return f2->_data.str().length();
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::get_timestamp
// Access: Published, Virtual
// Description: Returns a time_t value that represents the time the
// file was last modified, to within whatever precision
// the operating system records this information (on a
// Windows95 system, for instance, this may only be
// accurate to within 2 seconds).
//
// If the timestamp cannot be determined, either because
// it is not supported by the operating system or
// because there is some error (such as file not found),
// returns 0.
////////////////////////////////////////////////////////////////////
time_t VirtualFileMountRamdisk::
get_timestamp(const Filename &file) const {
return 0;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::scan_directory
// Access: Public, Virtual
// Description: Fills the given vector up with the list of filenames
// that are local to this directory, if the filename is
// a directory. Returns true if successful, or false if
// the file is not a directory or cannot be read.
////////////////////////////////////////////////////////////////////
bool VirtualFileMountRamdisk::
scan_directory(vector_string &contents, const Filename &dir) const {
((VirtualFileMountRamdisk *)this)->_lock.acquire();
PT(FileBase) f = _root.do_find_file(dir);
if (f == (FileBase *)NULL || !f->is_directory()) {
((VirtualFileMountRamdisk *)this)->_lock.release();
return false;
}
Directory *f2 = DCAST(Directory, f);
bool result = f2->do_scan_directory(contents);
((VirtualFileMountRamdisk *)this)->_lock.release();
return result;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::atomic_compare_and_exchange_contents
// Access: Public, Virtual
// Description: See Filename::atomic_compare_and_exchange_contents().
////////////////////////////////////////////////////////////////////
bool VirtualFileMountRamdisk::
atomic_compare_and_exchange_contents(const Filename &file, string &orig_contents,
const string &old_contents,
const string &new_contents) {
_lock.acquire();
PT(FileBase) f = _root.do_find_file(file);
if (f == (FileBase *)NULL || f->is_directory()) {
_lock.release();
return false;
}
bool retval = false;
File *f2 = DCAST(File, f);
orig_contents = f2->_data.str();
if (orig_contents == old_contents) {
f2->_data.str(new_contents);
retval = true;
}
_lock.release();
return retval;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::atomic_read_contents
// Access: Public, Virtual
// Description: See Filename::atomic_read_contents().
////////////////////////////////////////////////////////////////////
bool VirtualFileMountRamdisk::
atomic_read_contents(const Filename &file, string &contents) const {
((VirtualFileMountRamdisk *)this)->_lock.acquire();
PT(FileBase) f = _root.do_find_file(file);
if (f == (FileBase *)NULL || f->is_directory()) {
((VirtualFileMountRamdisk *)this)->_lock.release();
return false;
}
File *f2 = DCAST(File, f);
contents = f2->_data.str();
((VirtualFileMountRamdisk *)this)->_lock.release();
return true;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::output
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void VirtualFileMountRamdisk::
output(ostream &out) const {
out << "VirtualFileMountRamdisk";
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::FileBase::Destructor
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
VirtualFileMountRamdisk::FileBase::
~FileBase() {
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::FileBase::is_directory
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
bool VirtualFileMountRamdisk::FileBase::
is_directory() const {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::Directory::is_directory
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
bool VirtualFileMountRamdisk::Directory::
is_directory() const {
return true;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::Directory::do_find_file
// Access: Public
// Description: Recursively search for the file with the indicated
// name in this directory hierarchy.
////////////////////////////////////////////////////////////////////
PT(VirtualFileMountRamdisk::FileBase) VirtualFileMountRamdisk::Directory::
do_find_file(const string &filename) const {
size_t slash = filename.find('/');
if (slash == string::npos) {
// Search for a file within the local directory.
FileBase tfile(filename);
tfile.local_object();
Files::iterator fi = _files.find(&tfile);
if (fi != _files.end()) {
return (*fi);
}
return NULL;
}
// A nested directory. Search for the directory name, then recurse.
string dirname = filename.substr(0, slash);
string remainder = filename.substr(slash + 1);
FileBase tfile(dirname);
tfile.local_object();
Files::iterator fi = _files.find(&tfile);
if (fi != _files.end()) {
PT(FileBase) file = (*fi);
if (file->is_directory()) {
return DCAST(Directory, file.p())->do_find_file(remainder);
}
}
return NULL;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::Directory::do_create_file
// Access: Public
// Description: Recursively search for the file with the indicated
// name in this directory hierarchy. If not found,
// creates a new file.
////////////////////////////////////////////////////////////////////
PT(VirtualFileMountRamdisk::File) VirtualFileMountRamdisk::Directory::
do_create_file(const string &filename) {
size_t slash = filename.find('/');
if (slash == string::npos) {
// Search for a file within the local directory.
FileBase tfile(filename);
tfile.local_object();
Files::iterator fi = _files.find(&tfile);
if (fi != _files.end()) {
PT(FileBase) file = (*fi);
if (!file->is_directory()) {
return DCAST(File, file.p());
}
// Cannot create: a directory by the same name already exists.
return NULL;
}
// Create a new file.
if (express_cat.is_debug()) {
express_cat.debug()
<< "Making ramdisk file " << filename << "\n";
}
PT(File) file = new File(filename);
_files.insert(file.p());
return file;
}
// A nested directory. Search for the directory name, then recurse.
string dirname = filename.substr(0, slash);
string remainder = filename.substr(slash + 1);
FileBase tfile(dirname);
tfile.local_object();
Files::iterator fi = _files.find(&tfile);
if (fi != _files.end()) {
PT(FileBase) file = (*fi);
if (file->is_directory()) {
return DCAST(Directory, file.p())->do_create_file(remainder);
}
}
return NULL;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::Directory::do_make_directory
// Access: Public
// Description: Recursively search for the file with the indicated
// name in this directory hierarchy. If not found,
// creates a new directory.
////////////////////////////////////////////////////////////////////
PT(VirtualFileMountRamdisk::Directory) VirtualFileMountRamdisk::Directory::
do_make_directory(const string &filename) {
size_t slash = filename.find('/');
if (slash == string::npos) {
// Search for a file within the local directory.
FileBase tfile(filename);
tfile.local_object();
Files::iterator fi = _files.find(&tfile);
if (fi != _files.end()) {
PT(FileBase) file = (*fi);
if (file->is_directory()) {
return DCAST(Directory, file.p());
}
// Cannot create: a file by the same name already exists.
return NULL;
}
// Create a new directory.
if (express_cat.is_debug()) {
express_cat.debug()
<< "Making ramdisk directory " << filename << "\n";
}
PT(Directory) file = new Directory(filename);
_files.insert(file.p());
return file;
}
// A nested directory. Search for the directory name, then recurse.
string dirname = filename.substr(0, slash);
string remainder = filename.substr(slash + 1);
FileBase tfile(dirname);
tfile.local_object();
Files::iterator fi = _files.find(&tfile);
if (fi != _files.end()) {
PT(FileBase) file = (*fi);
if (file->is_directory()) {
return DCAST(Directory, file.p())->do_make_directory(remainder);
}
}
return NULL;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::Directory::do_delete_file
// Access: Public
// Description: Recursively search for the file with the indicated
// name in this directory hierarchy, and removes it.
// Returns the removed FileBase object.
////////////////////////////////////////////////////////////////////
PT(VirtualFileMountRamdisk::FileBase) VirtualFileMountRamdisk::Directory::
do_delete_file(const string &filename) {
size_t slash = filename.find('/');
if (slash == string::npos) {
// Search for a file within the local directory.
FileBase tfile(filename);
tfile.local_object();
Files::iterator fi = _files.find(&tfile);
if (fi != _files.end()) {
PT(FileBase) file = (*fi);
if (file->is_directory()) {
Directory *dir = DCAST(Directory, file.p());
if (!dir->_files.empty()) {
// Can't delete a nonempty directory.
return NULL;
}
}
_files.erase(fi);
return file;
}
return NULL;
}
// A nested directory. Search for the directory name, then recurse.
string dirname = filename.substr(0, slash);
string remainder = filename.substr(slash + 1);
FileBase tfile(dirname);
tfile.local_object();
Files::iterator fi = _files.find(&tfile);
if (fi != _files.end()) {
PT(FileBase) file = (*fi);
if (file->is_directory()) {
return DCAST(Directory, file.p())->do_delete_file(remainder);
}
}
return NULL;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountRamdisk::Directory::do_scan_directory
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
bool VirtualFileMountRamdisk::Directory::
do_scan_directory(vector_string &contents) const {
Files::const_iterator fi;
for (fi = _files.begin(); fi != _files.end(); ++fi) {
FileBase *file = (*fi);
contents.push_back(file->_basename);
}
return true;
}

View File

@ -0,0 +1,186 @@
// Filename: virtualFileMountRamdisk.h
// Created by: drose (19Sep11)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifndef VIRTUALFILEMOUNTRAMDISK_H
#define VIRTUALFILEMOUNTRAMDISK_H
#include "pandabase.h"
#include "virtualFileMount.h"
#include "mutexImpl.h"
#include "streamWrapper.h"
////////////////////////////////////////////////////////////////////
// Class : VirtualFileMountRamdisk
// Description : Simulates an actual directory on disk with in-memory
// storage. This is useful mainly for performing high
// level functions that expect disk I/O without actually
// writing files to disk. Naturally, there are
// significant limits to the size of the files that may
// be written with this system; and "files" written here
// are not automatically persistent between sessions.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDAEXPRESS VirtualFileMountRamdisk : public VirtualFileMount {
PUBLISHED:
VirtualFileMountRamdisk();
public:
virtual bool has_file(const Filename &file) const;
virtual bool create_file(const Filename &file);
virtual bool make_directory(const Filename &file);
virtual bool delete_file(const Filename &file);
virtual bool rename_file(const Filename &orig_filename, const Filename &new_filename);
virtual bool copy_file(const Filename &orig_filename, const Filename &new_filename);
virtual bool is_directory(const Filename &file) const;
virtual bool is_regular_file(const Filename &file) const;
virtual bool is_writable(const Filename &file) const;
virtual istream *open_read_file(const Filename &file) const;
virtual ostream *open_write_file(const Filename &file, bool truncate);
virtual ostream *open_append_file(const Filename &file);
virtual iostream *open_read_write_file(const Filename &file, bool truncate);
virtual iostream *open_read_append_file(const Filename &file);
virtual off_t get_file_size(const Filename &file, istream *stream) const;
virtual off_t get_file_size(const Filename &file) const;
virtual time_t get_timestamp(const Filename &file) const;
virtual bool scan_directory(vector_string &contents,
const Filename &dir) const;
virtual bool atomic_compare_and_exchange_contents(const Filename &file, string &orig_contents, const string &old_contents, const string &new_contents);
virtual bool atomic_read_contents(const Filename &file, string &contents) const;
virtual void output(ostream &out) const;
private:
class FileBase;
class File;
class Directory;
class FileBase : public TypedReferenceCount {
public:
INLINE FileBase(const string &basename);
virtual ~FileBase();
INLINE bool operator < (const FileBase &other) const;
virtual bool is_directory() const;
string _basename;
time_t _timestamp;
public:
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
TypedReferenceCount::init_type();
register_type(_type_handle, "VirtualFileMountRamdisk::FileBase",
TypedReferenceCount::get_class_type());
}
private:
static TypeHandle _type_handle;
};
class File : public FileBase {
public:
INLINE File(const string &basename);
stringstream _data;
StreamWrapper _wrapper;
public:
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
FileBase::init_type();
register_type(_type_handle, "VirtualFileMountRamdisk::File",
FileBase::get_class_type());
}
private:
static TypeHandle _type_handle;
};
typedef pset<PT(FileBase), indirect_less<FileBase *> > Files;
class Directory : public FileBase {
public:
INLINE Directory(const string &basename);
virtual bool is_directory() const;
PT(FileBase) do_find_file(const string &filename) const;
PT(File) do_create_file(const string &filename);
PT(Directory) do_make_directory(const string &filename);
PT(FileBase) do_delete_file(const string &filename);
bool do_scan_directory(vector_string &contents) const;
Files _files;
public:
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
FileBase::init_type();
register_type(_type_handle, "VirtualFileMountRamdisk::Directory",
FileBase::get_class_type());
}
private:
static TypeHandle _type_handle;
};
Directory _root;
MutexImpl _lock;
public:
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
VirtualFileMount::init_type();
register_type(_type_handle, "VirtualFileMountRamdisk",
VirtualFileMount::get_class_type());
FileBase::init_type();
File::init_type();
Directory::init_type();
}
private:
static TypeHandle _type_handle;
};
#include "virtualFileMountRamdisk.I"
#endif

View File

@ -59,6 +59,53 @@ create_file(const Filename &file) {
return pathname.open_write(stream, false);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountSystem::delete_file
// Access: Public, Virtual
// Description: Attempts to delete the indicated file or directory
// within the mount. This can remove a single file or
// an empty directory. It will not remove a nonempty
// directory. Returns true on success, false on
// failure.
////////////////////////////////////////////////////////////////////
bool VirtualFileMountSystem::
delete_file(const Filename &file) {
Filename pathname(_physical_filename, file);
return pathname.unlink() || pathname.rmdir();
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountSystem::rename_file
// Access: Public
// Description: Attempts to rename the contents of the indicated file
// to the indicated file. Both filenames will be within
// the mount. Returns true on success, false on
// failure. If this returns false, this will be
// attempted again with a copy-and-delete operation.
////////////////////////////////////////////////////////////////////
bool VirtualFileMountSystem::
rename_file(const Filename &orig_filename, const Filename &new_filename) {
Filename orig_pathname(_physical_filename, orig_filename);
Filename new_pathname(_physical_filename, new_filename);
return orig_pathname.rename_to(new_pathname);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountSystem::copy_file
// Access: Public
// Description: Attempts to copy the contents of the indicated file
// to the indicated file. Both filenames will be within
// the mount. Returns true on success, false on
// failure. If this returns false, the copy will be
// performed by explicit read-and-write operations.
////////////////////////////////////////////////////////////////////
bool VirtualFileMountSystem::
copy_file(const Filename &orig_filename, const Filename &new_filename) {
Filename orig_pathname(_physical_filename, orig_filename);
Filename new_pathname(_physical_filename, new_filename);
return orig_pathname.copy_to(new_pathname);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountSystem::make_directory
// Access: Public, Virtual
@ -383,6 +430,46 @@ scan_directory(vector_string &contents, const Filename &dir) const {
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountSystem::atomic_compare_and_exchange_contents
// Access: Public, Virtual
// Description: See Filename::atomic_compare_and_exchange_contents().
////////////////////////////////////////////////////////////////////
bool VirtualFileMountSystem::
atomic_compare_and_exchange_contents(const Filename &file, string &orig_contents,
const string &old_contents,
const string &new_contents) {
#ifdef WIN32
// First ensure that the file exists to validate its case.
if (VirtualFileSystem::get_global_ptr()->vfs_case_sensitive) {
if (!has_file(file)) {
return NULL;
}
}
#endif // WIN32
Filename pathname(_physical_filename, file);
return pathname.atomic_compare_and_exchange_contents(orig_contents, old_contents, new_contents);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountSystem::atomic_read_contents
// Access: Public, Virtual
// Description: See Filename::atomic_read_contents().
////////////////////////////////////////////////////////////////////
bool VirtualFileMountSystem::
atomic_read_contents(const Filename &file, string &contents) const {
#ifdef WIN32
// First ensure that the file exists to validate its case.
if (VirtualFileSystem::get_global_ptr()->vfs_case_sensitive) {
if (!has_file(file)) {
return NULL;
}
}
#endif // WIN32
Filename pathname(_physical_filename, file);
return pathname.atomic_read_contents(contents);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountSystem::output
// Access: Public, Virtual

View File

@ -34,6 +34,9 @@ public:
virtual bool has_file(const Filename &file) const;
virtual bool create_file(const Filename &file);
virtual bool make_directory(const Filename &file);
virtual bool delete_file(const Filename &file);
virtual bool rename_file(const Filename &orig_filename, const Filename &new_filename);
virtual bool copy_file(const Filename &orig_filename, const Filename &new_filename);
virtual bool is_directory(const Filename &file) const;
virtual bool is_regular_file(const Filename &file) const;
virtual bool is_writable(const Filename &file) const;
@ -52,6 +55,9 @@ public:
virtual bool scan_directory(vector_string &contents,
const Filename &dir) const;
virtual bool atomic_compare_and_exchange_contents(const Filename &file, string &orig_contents, const string &old_contents, const string &new_contents);
virtual bool atomic_read_contents(const Filename &file, string &contents) const;
virtual void output(ostream &out) const;
private:

View File

@ -99,6 +99,115 @@ is_writable() const {
return _mount->is_writable(_local_filename);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileSimple::delete_file
// Access: Public
// Description: Attempts to delete this file or directory. This can
// remove a single file or an empty directory. It will
// not remove a nonempty directory. Returns true on
// success, false on failure.
////////////////////////////////////////////////////////////////////
bool VirtualFileSimple::
delete_file() {
return _mount->delete_file(_local_filename);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileSimple::rename_file
// Access: Public
// Description: Attempts to move or rename this file or directory.
// If the original file is an ordinary file, it will
// quietly replace any already-existing file in the new
// filename (but not a directory). If the original file
// is a directory, the new filename must not already
// exist.
//
// If the file is a directory, the new filename must be
// within the same mount point. If the file is an
// ordinary file, the new filename may be anywhere; but
// if it is not within the same mount point then the
// rename operation is automatically performed as a
// two-step copy-and-delete operation.
////////////////////////////////////////////////////////////////////
bool VirtualFileSimple::
rename_file(VirtualFile *new_file) {
if (new_file->is_of_type(VirtualFileSimple::get_class_type())) {
VirtualFileSimple *new_file_simple = DCAST(VirtualFileSimple, new_file);
if (new_file_simple->_mount == _mount) {
// Same mount pount.
if (_mount->rename_file(_local_filename, new_file_simple->_local_filename)) {
return true;
}
}
}
// Different mount point, or the mount doesn't support renaming. Do
// it by hand.
if (is_regular_file() && !new_file->is_directory()) {
// copy-and-delete.
new_file->delete_file();
if (copy_file(new_file)) {
delete_file();
return true;
}
}
return false;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileSimple::copy_file
// Access: Public
// Description: Attempts to copy the contents of this file to the
// indicated file. Returns true on success, false on
// failure.
////////////////////////////////////////////////////////////////////
bool VirtualFileSimple::
copy_file(VirtualFile *new_file) {
if (new_file->is_of_type(VirtualFileSimple::get_class_type())) {
VirtualFileSimple *new_file_simple = DCAST(VirtualFileSimple, new_file);
if (new_file_simple->_mount == _mount) {
// Same mount pount.
if (_mount->copy_file(_local_filename, new_file_simple->_local_filename)) {
return true;
}
}
}
// Different mount point, or the mount doesn't support copying. Do
// it by hand.
ostream *out = new_file->open_write_file(false, true);
istream *in = open_read_file(false);
static const size_t buffer_size = 4096;
char buffer[buffer_size];
in->read(buffer, buffer_size);
size_t count = in->gcount();
while (count != 0) {
out->write(buffer, count);
if (out->fail()) {
new_file->close_write_file(out);
close_read_file(in);
new_file->delete_file();
return false;
}
in->read(buffer, buffer_size);
count = in->gcount();
}
if (!in->eof()) {
new_file->close_write_file(out);
close_read_file(in);
new_file->delete_file();
return false;
}
new_file->close_write_file(out);
close_read_file(in);
return true;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileSimple::open_read_file
// Access: Published, Virtual
@ -192,7 +301,7 @@ open_append_file() {
// work around compiler issues.
////////////////////////////////////////////////////////////////////
void VirtualFileSimple::
close_write_file(ostream *stream) const {
close_write_file(ostream *stream) {
_mount->close_write_file(stream);
}
@ -295,6 +404,28 @@ get_system_info(SubfileInfo &info) {
return _mount->get_system_info(_local_filename, info);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileSimple::atomic_compare_and_exchange_contents
// Access: Public, Virtual
// Description: See Filename::atomic_compare_and_exchange_contents().
////////////////////////////////////////////////////////////////////
bool VirtualFileSimple::
atomic_compare_and_exchange_contents(string &orig_contents,
const string &old_contents,
const string &new_contents) {
return _mount->atomic_compare_and_exchange_contents(_local_filename, orig_contents, old_contents, new_contents);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileSimple::atomic_read_contents
// Access: Public, Virtual
// Description: See Filename::atomic_read_contents().
////////////////////////////////////////////////////////////////////
bool VirtualFileSimple::
atomic_read_contents(string &contents) const {
return _mount->atomic_read_contents(_local_filename, contents);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileSimple::read_file
// Access: Public, Virtual

View File

@ -44,11 +44,15 @@ PUBLISHED:
virtual bool is_writable() const;
INLINE bool is_implicit_pz_file() const;
virtual bool delete_file();
virtual bool rename_file(VirtualFile *new_file);
virtual bool copy_file(VirtualFile *new_file);
virtual istream *open_read_file(bool auto_unwrap) const;
virtual void close_read_file(istream *stream) const;
virtual ostream *open_write_file(bool auto_wrap, bool truncate);
virtual ostream *open_append_file();
virtual void close_write_file(ostream *stream) const;
virtual void close_write_file(ostream *stream);
virtual iostream *open_read_write_file(bool truncate);
virtual iostream *open_read_append_file();
virtual void close_read_write_file(iostream *stream);
@ -59,6 +63,9 @@ PUBLISHED:
virtual bool get_system_info(SubfileInfo &info);
public:
virtual bool atomic_compare_and_exchange_contents(string &orig_contents, const string &old_contents, const string &new_contents);
virtual bool atomic_read_contents(string &contents) const;
virtual bool read_file(pvector<unsigned char> &result, bool auto_unwrap) const;
virtual bool write_file(const unsigned char *data, size_t data_size, bool auto_wrap);

View File

@ -17,6 +17,7 @@
#include "virtualFileComposite.h"
#include "virtualFileMount.h"
#include "virtualFileMountMultifile.h"
#include "virtualFileMountRamdisk.h"
#include "virtualFileMountSystem.h"
#include "streamWrapper.h"
#include "dSearchPath.h"
@ -510,9 +511,37 @@ get_cwd() const {
////////////////////////////////////////////////////////////////////
bool VirtualFileSystem::
make_directory(const Filename &filename) {
((VirtualFileSystem *)this)->_lock.acquire();
_lock.acquire();
PT(VirtualFile) result = do_get_file(filename, OF_make_directory);
((VirtualFileSystem *)this)->_lock.release();
_lock.release();
return result->is_directory();
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileSystem::make_directory_full
// Access: Published
// Description: Attempts to create a directory within the file
// system. Will also create any intervening directories
// needed. Returns true on success, false on failure.
////////////////////////////////////////////////////////////////////
bool VirtualFileSystem::
make_directory_full(const Filename &filename) {
_lock.acquire();
// First, make sure everything up to the last path is known. We
// don't care too much if any of these fail; maybe they failed
// because the directory was already there.
string dirname = filename;
size_t slash = dirname.find('/', 1);
while (slash != string::npos) {
Filename component(dirname.substr(0, slash));
do_get_file(component, OF_make_directory);
slash = dirname.find('/', slash + 1);
}
// Now make the last one, and check the return value.
PT(VirtualFile) result = do_get_file(filename, OF_make_directory);
_lock.release();
return result->is_directory();
}
@ -594,6 +623,82 @@ find_file(const Filename &filename, const DSearchPath &searchpath,
return NULL;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileSystem::delete_file
// Access: Public
// Description: Attempts to delete the indicated file or directory.
// This can remove a single file or an empty directory.
// It will not remove a nonempty directory. Returns
// true on success, false on failure.
////////////////////////////////////////////////////////////////////
bool VirtualFileSystem::
delete_file(const Filename &filename) {
PT(VirtualFile) file = get_file(filename, true);
if (file == (VirtualFile *)NULL) {
return false;
}
return file->delete_file();
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileSystem::rename_file
// Access: Public
// Description: Attempts to move or rename the indicated file or
// directory. If the original file is an ordinary file,
// it will quietly replace any already-existing file in
// the new filename (but not a directory). If the
// original file is a directory, the new filename must
// not already exist.
//
// If the file is a directory, the new filename must be
// within the same mount point. If the file is an
// ordinary file, the new filename may be anywhere; but
// if it is not within the same mount point then the
// rename operation is automatically performed as a
// two-step copy-and-delete operation.
////////////////////////////////////////////////////////////////////
bool VirtualFileSystem::
rename_file(const Filename &orig_filename, const Filename &new_filename) {
_lock.acquire();
PT(VirtualFile) orig_file = do_get_file(orig_filename, OF_status_only);
if (orig_file == (VirtualFile *)NULL) {
_lock.release();
return false;
}
PT(VirtualFile) new_file = do_get_file(new_filename, OF_status_only | OF_allow_nonexist);
if (new_file == (VirtualFile *)NULL) {
_lock.release();
return false;
}
_lock.release();
return orig_file->rename_file(new_file);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileSystem::copy_file
// Access: Public
// Description: Attempts to copy the contents of the indicated file
// to the indicated file. Returns true on success,
// false on failure.
////////////////////////////////////////////////////////////////////
bool VirtualFileSystem::
copy_file(const Filename &orig_filename, const Filename &new_filename) {
PT(VirtualFile) orig_file = get_file(orig_filename, true);
if (orig_file == (VirtualFile *)NULL) {
return false;
}
PT(VirtualFile) new_file = get_file(new_filename, true);
if (new_file == (VirtualFile *)NULL) {
return false;
}
return orig_file->copy_file(new_file);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileSystem::resolve_filename
@ -779,23 +884,37 @@ get_global_ptr() {
mount_desc = ExecutionEnvironment::expand_string(mount_desc);
Filename physical_filename = Filename::from_os_specific(mount_desc);
int flags = 0;
int flags;
string password;
// Split the options up by commas.
size_t p = 0;
size_t q = options.find(',', p);
while (q != string::npos) {
parse_option(options.substr(p, q - p),
flags, password);
p = q + 1;
q = options.find(',', p);
}
parse_option(options.substr(p), flags, password);
parse_options(options, flags, password);
_global_ptr->mount(physical_filename, mount_point, flags, password);
}
}
ConfigVariableString vfs_mount_ramdisk
("vfs-mount-ramdisk", "",
PRC_DESC("vfs-mount-ramdisk mount-point [options]"));
if (!vfs_mount_ramdisk.empty()) {
string mount_point = vfs_mount_ramdisk;
string options;
size_t space = mount_point.rfind(' ');
if (space != string::npos) {
// If there's a space, we have the optional options field.
options = mount_point.substr(space + 1);
while (space > 0 && isspace(mount_point[space - 1])) {
--space;
}
mount_point = mount_point.substr(0, space);
}
int flags;
string password;
parse_options(options, flags, password);
PT(VirtualFileMount) ramdisk = new VirtualFileMountRamdisk;
_global_ptr->mount(ramdisk, mount_point, flags);
}
}
return _global_ptr;
@ -1037,6 +1156,38 @@ close_read_write_file(iostream *stream) {
}
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileSystem::atomic_compare_and_exchange_contents
// Access: Public
// Description: See Filename::atomic_compare_and_exchange_contents().
////////////////////////////////////////////////////////////////////
bool VirtualFileSystem::
atomic_compare_and_exchange_contents(const Filename &filename, string &orig_contents,
const string &old_contents,
const string &new_contents) {
PT(VirtualFile) file = create_file(filename);
if (file == NULL) {
return false;
}
return file->atomic_compare_and_exchange_contents(orig_contents, old_contents, new_contents);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileSystem::atomic_read_contents
// Access: Public
// Description: See Filename::atomic_read_contents().
////////////////////////////////////////////////////////////////////
bool VirtualFileSystem::
atomic_read_contents(const Filename &filename, string &contents) const {
PT(VirtualFile) file = get_file(filename, false);
if (file == NULL) {
return false;
}
return file->atomic_read_contents(contents);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileSystem::scan_mount_points
// Access: Public
@ -1081,6 +1232,30 @@ scan_mount_points(vector_string &names, const Filename &path) const {
}
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileSystem::parse_options
// Access: Public, Static
// Description: Parses all of the option flags in the options list on
// the vfs-mount Config.prc line.
////////////////////////////////////////////////////////////////////
void VirtualFileSystem::
parse_options(const string &options, int &flags, string &password) {
flags = 0;
password = string();
// Split the options up by commas.
size_t p = 0;
size_t q = options.find(',', p);
while (q != string::npos) {
parse_option(options.substr(p, q - p),
flags, password);
p = q + 1;
q = options.find(',', p);
}
parse_option(options.substr(p), flags, password);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileSystem::parse_option
// Access: Public, Static
@ -1148,7 +1323,9 @@ do_mount(VirtualFileMount *mount, const Filename &mount_point, int flags) {
////////////////////////////////////////////////////////////////////
PT(VirtualFile) VirtualFileSystem::
do_get_file(const Filename &filename, int open_flags) const {
nassertr(!filename.empty(), NULL);
if (filename.empty()) {
return NULL;
}
Filename pathname(filename);
if (pathname.is_local()) {
pathname = Filename(_cwd, filename);
@ -1262,7 +1439,7 @@ consider_match(PT(VirtualFile) &found_file, VirtualFileComposite *&composite_fil
int open_flags) const {
PT(VirtualFile) vfile =
mount->make_virtual_file(local_filename, original_filename, false, open_flags);
if (!vfile->has_file()) {
if (!vfile->has_file() && ((open_flags & OF_allow_nonexist) == 0)) {
// Keep looking.
return false;
}

View File

@ -69,12 +69,16 @@ PUBLISHED:
BLOCKING bool chdir(const Filename &new_directory);
BLOCKING Filename get_cwd() const;
BLOCKING bool make_directory(const Filename &filename);
BLOCKING bool make_directory_full(const Filename &filename);
BLOCKING PT(VirtualFile) get_file(const Filename &filename, bool status_only = false) const;
BLOCKING PT(VirtualFile) create_file(const Filename &filename);
BLOCKING PT(VirtualFile) find_file(const Filename &filename,
const DSearchPath &searchpath,
bool status_only = false) const;
BLOCKING bool delete_file(const Filename &filename);
BLOCKING bool rename_file(const Filename &orig_filename, const Filename &new_filename);
BLOCKING bool copy_file(const Filename &orig_filename, const Filename &new_filename);
BLOCKING bool resolve_filename(Filename &filename, const DSearchPath &searchpath,
const string &default_extension = string()) const;
@ -115,14 +119,19 @@ PUBLISHED:
BLOCKING static void close_read_write_file(iostream *stream);
public:
bool atomic_compare_and_exchange_contents(const Filename &filename, string &orig_contents, const string &old_contents, const string &new_contents);
bool atomic_read_contents(const Filename &filename, string &contents) const;
INLINE bool read_file(const Filename &filename, string &result, bool auto_unwrap) const;
INLINE bool read_file(const Filename &filename, pvector<unsigned char> &result, bool auto_unwrap) const;
INLINE bool write_file(const Filename &filename, const unsigned char *data, size_t data_size, bool auto_wrap);
void scan_mount_points(vector_string &names, const Filename &path) const;
static void parse_options(const string &options,
int &flags, string &password);
static void parse_option(const string &option,
int &flags, string &password);
int &flags, string &password);
public:
// These flags are passed to do_get_file() and
@ -132,6 +141,7 @@ public:
OF_status_only = 0x0001,
OF_create_file = 0x0002,
OF_make_directory = 0x0004,
OF_allow_nonexist = 0x0008,
};
// These are declared as class instances, instead of as globals, to

View File

@ -222,7 +222,8 @@ open_write(const Filename &bam_filename, bool report_errors) {
loader_cat.info() << "Writing " << bam_filename << "\n";
bam_filename.unlink();
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
vfs->delete_file(bam_filename);
if (!_dout.open(bam_filename)) {
if (report_errors) {
loader_cat.error() << "Unable to open " << bam_filename << "\n";

View File

@ -118,22 +118,19 @@ set_root(const Filename &root) {
flush_index();
_root = root;
// For now, the filename must be a directory. Maybe eventually we
// will support writing caches to a Panda multifile (though maybe it
// would be better to implement this kind of thing at a lower level,
// via a writable VFS, in which case the specified root filename
// will still be a "directory").
if (!root.is_directory()) {
Filename dirname(_root, Filename("."));
dirname.make_dir();
// The root filename must be a directory.
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
if (!vfs->is_directory(_root)) {
vfs->make_directory_full(_root);
}
nassertv(root.is_directory());
delete _index;
_index = new BamCacheIndex;
_index_stale_since = 0;
read_index();
check_cache_size();
nassertv(vfs->is_directory(_root));
}
////////////////////////////////////////////////////////////////////
@ -193,6 +190,7 @@ lookup(const Filename &source_filename, const string &cache_extension) {
////////////////////////////////////////////////////////////////////
bool BamCache::
store(BamCacheRecord *record) {
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
ReMutexHolder holder(_lock);
nassertr(!record->_cache_pathname.empty(), false);
nassertr(record->has_data(), false);
@ -227,7 +225,7 @@ store(BamCacheRecord *record) {
if (!dout.open(temp_pathname)) {
util_cat.error()
<< "Could not write cache file: " << temp_pathname << "\n";
temp_pathname.unlink();
vfs->delete_file(temp_pathname);
emergency_read_only();
return false;
}
@ -235,14 +233,14 @@ store(BamCacheRecord *record) {
if (!dout.write_header(_bam_header)) {
util_cat.error()
<< "Unable to write to " << temp_pathname << "\n";
temp_pathname.unlink();
vfs->delete_file(temp_pathname);
return false;
}
{
BamWriter writer(&dout);
if (!writer.init()) {
temp_pathname.unlink();
vfs->delete_file(temp_pathname);
return false;
}
@ -257,12 +255,12 @@ store(BamCacheRecord *record) {
}
if (!writer.write_object(record)) {
temp_pathname.unlink();
vfs->delete_file(temp_pathname);
return false;
}
if (!writer.write_object(record->get_data())) {
temp_pathname.unlink();
vfs->delete_file(temp_pathname);
return false;
}
@ -275,13 +273,13 @@ store(BamCacheRecord *record) {
dout.close();
// Now move the file into place.
if (!temp_pathname.rename_to(cache_pathname) && temp_pathname.exists()) {
cache_pathname.unlink();
if (!temp_pathname.rename_to(cache_pathname)) {
if (!vfs->rename_file(temp_pathname, cache_pathname) && vfs->exists(temp_pathname)) {
vfs->delete_file(cache_pathname);
if (!vfs->rename_file(temp_pathname, cache_pathname)) {
util_cat.error()
<< "Unable to rename " << temp_pathname << " to "
<< cache_pathname << "\n";
temp_pathname.unlink();
vfs->delete_file(temp_pathname);
return false;
}
}
@ -351,15 +349,17 @@ flush_index() {
// Now atomically write the name of this index file to the index
// reference file.
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
Filename index_ref_pathname(_root, Filename("index_name.txt"));
string old_index = _index_ref_contents;
string new_index = temp_pathname.get_basename() + "\n";
string orig_index;
if (index_ref_pathname.atomic_compare_and_exchange_contents(orig_index, old_index, new_index)) {
if (vfs->atomic_compare_and_exchange_contents(index_ref_pathname, orig_index, old_index, new_index)) {
// We successfully wrote our version of the index, and no other
// process beat us to it. Our index is now the official one.
// Remove the old index.
_index_pathname.unlink();
vfs->delete_file(_index_pathname);
_index_pathname = temp_pathname;
_index_ref_contents = new_index;
_index_stale_since = 0;
@ -369,7 +369,7 @@ flush_index() {
// Shoot, some other process updated the index while we were
// trying to update it, and they beat us to it. We have to merge,
// and try again.
temp_pathname.unlink();
vfs->delete_file(temp_pathname);
_index_pathname = Filename(_root, Filename(trim(orig_index)));
_index_ref_contents = orig_index;
read_index();
@ -411,7 +411,8 @@ read_index() {
if (old_index_pathname == _index_pathname) {
// Nope, we just couldn't read it. Delete it and build a new
// one.
_index_pathname.unlink();
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
vfs->delete_file(_index_pathname);
rebuild_index();
flush_index();
return;
@ -428,9 +429,10 @@ read_index() {
////////////////////////////////////////////////////////////////////
bool BamCache::
read_index_pathname(Filename &index_pathname, string &index_ref_contents) const {
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
index_ref_contents.clear();
Filename index_ref_pathname(_root, Filename("index_name.txt"));
if (!index_ref_pathname.atomic_read_contents(index_ref_contents)) {
if (!vfs->atomic_read_contents(index_ref_pathname, index_ref_contents)) {
return false;
}
@ -555,8 +557,10 @@ merge_index(BamCacheIndex *new_index) {
////////////////////////////////////////////////////////////////////
void BamCache::
rebuild_index() {
vector_string contents;
if (!_root.scan_directory(contents)) {
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
PT(VirtualFileList) contents = vfs->scan_directory(_root);
if (contents == NULL) {
util_cat.error()
<< "Unable to read directory " << _root << ", caching disabled.\n";
set_active(false);
@ -566,9 +570,10 @@ rebuild_index() {
delete _index;
_index = new BamCacheIndex;
vector_string::const_iterator ci;
for (ci = contents.begin(); ci != contents.end(); ++ci) {
Filename filename(*ci);
int num_files = contents->get_num_files();
for (int ci = 0; ci < num_files; ++ci) {
VirtualFile *file = contents->get_file(ci);
Filename filename = file->get_filename();
if (filename.get_extension() == "bam" ||
filename.get_extension() == "txo") {
Filename pathname(_root, filename);
@ -576,7 +581,7 @@ rebuild_index() {
PT(BamCacheRecord) record = do_read_record(pathname, false);
if (record == (BamCacheRecord *)NULL) {
// Well, it was invalid, so blow it away.
pathname.unlink();
file->delete_file();
} else {
record->_record_access_time = record->_recorded_time;
@ -585,7 +590,7 @@ rebuild_index() {
if (!inserted) {
util_cat.info()
<< "Multiple cache files defining " << record->get_source_pathname() << "\n";
pathname.unlink();
file->delete_file();
}
}
}
@ -646,8 +651,9 @@ check_cache_size() {
// Never mind; the cache is empty.
break;
}
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
Filename cache_pathname(_root, record->get_cache_filename());
cache_pathname.unlink();
vfs->delete_file(cache_pathname);
}
mark_index_stale();
}
@ -722,30 +728,31 @@ do_read_index(const Filename &index_pathname) {
////////////////////////////////////////////////////////////////////
bool BamCache::
do_write_index(const Filename &index_pathname, const BamCacheIndex *index) {
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
DatagramOutputFile dout;
if (!dout.open(index_pathname)) {
util_cat.error()
<< "Could not write index file: " << index_pathname << "\n";
index_pathname.unlink();
vfs->delete_file(index_pathname);
return false;
}
if (!dout.write_header(_bam_header)) {
util_cat.error()
<< "Unable to write to " << index_pathname << "\n";
index_pathname.unlink();
vfs->delete_file(index_pathname);
return false;
}
{
BamWriter writer(&dout);
if (!writer.init()) {
index_pathname.unlink();
vfs->delete_file(index_pathname);
return false;
}
if (!writer.write_object(index)) {
index_pathname.unlink();
vfs->delete_file(index_pathname);
return false;
}
}
@ -788,6 +795,7 @@ PT(BamCacheRecord) BamCache::
read_record(const Filename &source_pathname,
const Filename &cache_filename,
int pass) {
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
Filename cache_pathname(_root, cache_filename);
if (pass != 0) {
ostringstream strm;
@ -806,7 +814,7 @@ read_record(const Filename &source_pathname,
PT(BamCacheRecord) record = do_read_record(cache_pathname, true);
if (record == (BamCacheRecord *)NULL) {
// Well, it was invalid, so blow it away, and make a new one.
cache_pathname.unlink();
vfs->delete_file(cache_pathname);
remove_from_index(source_pathname);
PT(BamCacheRecord) record =

View File

@ -22,6 +22,7 @@
#include "partGroup.h"
#include "cardMaker.h"
#include "bamCache.h"
#include "virtualFileSystem.h"
#include "panda_getopt.h"
// By including checkPandaVersion.h, we guarantee that runtime
@ -324,11 +325,12 @@ main(int argc, char *argv[]) {
window->load_models(framework.get_models(), argc, argv);
if (delete_models) {
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
for (int i = 1; i < argc && argv[i] != (char *)NULL; i++) {
Filename model = Filename::from_os_specific(argv[i]);
if (model.exists()) {
if (vfs->exists(model)) {
nout << "Deleting " << model << "\n";
model.unlink();
vfs->delete_file(model);
}
}
}