mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-01 01:07:51 -04:00
optimizations to vfs.readFile(), particular for mounted multifiles
This commit is contained in:
parent
d51e49516a
commit
9d3724a40b
@ -21,6 +21,7 @@
|
||||
#include "zStream.h"
|
||||
#include "encryptStream.h"
|
||||
#include "virtualFileSystem.h"
|
||||
#include "virtualFile.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <time.h>
|
||||
@ -1996,14 +1997,72 @@ read_subfile(int index, pvector<unsigned char> &result) {
|
||||
nassertr(index >= 0 && index < (int)_subfiles.size(), false);
|
||||
result.clear();
|
||||
|
||||
istream *in = open_read_subfile(index);
|
||||
if (in == (istream *)NULL) {
|
||||
// Now look up the particular Subfile we are reading.
|
||||
nassertr(is_read_valid(), NULL);
|
||||
nassertr(index >= 0 && index < (int)_subfiles.size(), NULL);
|
||||
Subfile *subfile = _subfiles[index];
|
||||
|
||||
if (subfile->_source != (istream *)NULL ||
|
||||
!subfile->_source_filename.empty()) {
|
||||
// The subfile has not yet been copied into the physical
|
||||
// Multifile. Force a flush operation to incorporate it.
|
||||
flush();
|
||||
|
||||
// That shouldn't change the subfile index or delete the subfile
|
||||
// pointer.
|
||||
nassertr(subfile == _subfiles[index], NULL);
|
||||
}
|
||||
|
||||
result.reserve(subfile->_uncompressed_length);
|
||||
|
||||
bool success = true;
|
||||
if (subfile->_flags & (SF_encrypted | SF_compressed)) {
|
||||
// If the subfile is encrypted or compressed, we can't read it
|
||||
// directly. Fall back to the generic implementation.
|
||||
istream *in = open_read_subfile(index);
|
||||
if (in == (istream *)NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
success = VirtualFile::simple_read_file(in, result);
|
||||
close_read_subfile(in);
|
||||
|
||||
} else {
|
||||
// But if the subfile is just a plain file, we can just read the
|
||||
// data directly from the Multifile, without paying the cost of an
|
||||
// ISubStream.
|
||||
static const size_t buffer_size = 4096;
|
||||
char buffer[buffer_size];
|
||||
|
||||
streamsize pos = _offset + subfile->_data_start;
|
||||
size_t max_bytes = subfile->_data_length;
|
||||
streamsize count = 0;
|
||||
bool eof = true;
|
||||
|
||||
streamsize num_bytes = (streamsize)min(buffer_size, max_bytes);
|
||||
_read->seek_read(pos, buffer, num_bytes, count, eof);
|
||||
while (count != 0) {
|
||||
thread_consider_yield();
|
||||
nassertr(count <= max_bytes, false);
|
||||
result.insert(result.end(), buffer, buffer + (size_t)count);
|
||||
max_bytes -= (size_t)count;
|
||||
pos += count;
|
||||
|
||||
num_bytes = (streamsize)min(buffer_size, max_bytes);
|
||||
_read->seek_read(pos, buffer, num_bytes, count, eof);
|
||||
}
|
||||
|
||||
success = !eof;
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
ostringstream message;
|
||||
message << "I/O error reading from " << get_multifile_name() << " at "
|
||||
<< get_subfile_name(index);
|
||||
nassert_raise(message.str());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = read_to_pvector(result, *in);
|
||||
close_read_subfile(in);
|
||||
nassertr(success, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2176,31 +2235,6 @@ standardize_subfile_name(const string &subfile_name) const {
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Multifile::read_to_pvector
|
||||
// Access: Private, Static
|
||||
// Description: Helper function to read the entire contents of the
|
||||
// indicated stream from the current position, and
|
||||
// append it onto the indicated pvector. Returns true
|
||||
// on success, false on failure.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool Multifile::
|
||||
read_to_pvector(pvector<unsigned char> &result, istream &stream) {
|
||||
static const size_t buffer_size = 4096;
|
||||
char buffer[buffer_size];
|
||||
|
||||
stream.read(buffer, buffer_size);
|
||||
size_t count = stream.gcount();
|
||||
while (count != 0) {
|
||||
result.insert(result.end(), buffer, buffer + count);
|
||||
stream.read(buffer, buffer_size);
|
||||
count = stream.gcount();
|
||||
}
|
||||
|
||||
bool failed = stream.fail() && !stream.eof();
|
||||
return !failed;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Multifile::clear_subfiles
|
||||
// Access: Private
|
||||
@ -2494,7 +2528,7 @@ check_signatures() {
|
||||
|
||||
// Read the remaining buffer of certificate data.
|
||||
pvector<unsigned char> buffer;
|
||||
bool success = read_to_pvector(buffer, *stream);
|
||||
bool success = VirtualFile::simple_read_file(stream, buffer);
|
||||
nassertv(success);
|
||||
close_read_subfile(stream);
|
||||
|
||||
|
@ -205,7 +205,6 @@ private:
|
||||
void add_new_subfile(Subfile *subfile, int compression_level);
|
||||
istream *open_read_subfile(Subfile *subfile);
|
||||
string standardize_subfile_name(const string &subfile_name) const;
|
||||
static bool read_to_pvector(pvector<unsigned char> &result, istream &stream);
|
||||
|
||||
void clear_subfiles();
|
||||
bool read_index();
|
||||
|
@ -278,35 +278,18 @@ read_file(string &result, bool auto_unwrap) const {
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFile::read_file
|
||||
// Access: Public
|
||||
// Access: Public, Virtual
|
||||
// Description: Fills up the indicated pvector with the contents of
|
||||
// the file, if it is a regular file. Returns true on
|
||||
// success, false otherwise.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFile::
|
||||
read_file(pvector<unsigned char> &result, bool auto_unwrap) const {
|
||||
result.clear();
|
||||
|
||||
istream *in = open_read_file(auto_unwrap);
|
||||
if (in == (istream *)NULL) {
|
||||
express_cat.info()
|
||||
<< "Unable to read " << get_filename() << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool okflag = read_file(in, result);
|
||||
|
||||
close_read_file(in);
|
||||
|
||||
if (!okflag) {
|
||||
express_cat.info()
|
||||
<< "Error while reading " << get_filename() << "\n";
|
||||
}
|
||||
return okflag;
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFile::read_file
|
||||
// Function: VirtualFile::simple_read_file
|
||||
// Access: Public, Static
|
||||
// Description: Fills up the indicated pvector with the contents of
|
||||
// the just-opened file. Returns true on success, false
|
||||
@ -314,10 +297,8 @@ read_file(pvector<unsigned char> &result, bool auto_unwrap) const {
|
||||
// data read from the file will be appended onto it.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFile::
|
||||
read_file(istream *in, pvector<unsigned char> &result) {
|
||||
pvector<char> result_vec;
|
||||
|
||||
static const size_t buffer_size = 1024;
|
||||
simple_read_file(istream *in, pvector<unsigned char> &result) {
|
||||
static const size_t buffer_size = 4096;
|
||||
char buffer[buffer_size];
|
||||
|
||||
in->read(buffer, buffer_size);
|
||||
@ -333,14 +314,14 @@ read_file(istream *in, pvector<unsigned char> &result) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFile::read_file
|
||||
// Access: Public, Static
|
||||
// Description: As in read_file() with two parameters, above, but
|
||||
// only reads up to max_bytes bytes from the file.
|
||||
// Function: VirtualFile::simple_read_file
|
||||
// Access: Public
|
||||
// Description: As in simple_read_file() with two parameters, above,
|
||||
// but only reads up to max_bytes bytes from the file.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFile::
|
||||
read_file(istream *in, pvector<unsigned char> &result, size_t max_bytes) {
|
||||
static const size_t buffer_size = 1024;
|
||||
simple_read_file(istream *in, pvector<unsigned char> &result, size_t max_bytes) {
|
||||
static const size_t buffer_size = 4096;
|
||||
char buffer[buffer_size];
|
||||
|
||||
in->read(buffer, min(buffer_size, max_bytes));
|
||||
|
@ -63,9 +63,10 @@ PUBLISHED:
|
||||
public:
|
||||
INLINE void set_original_filename(const Filename &filename);
|
||||
bool read_file(string &result, bool auto_unwrap) const;
|
||||
bool read_file(pvector<unsigned char> &result, bool auto_unwrap) const;
|
||||
static bool read_file(istream *stream, pvector<unsigned char> &result);
|
||||
static bool read_file(istream *stream, pvector<unsigned char> &result, size_t max_bytes);
|
||||
virtual bool read_file(pvector<unsigned char> &result, bool auto_unwrap) const;
|
||||
|
||||
static bool simple_read_file(istream *stream, pvector<unsigned char> &result);
|
||||
static bool simple_read_file(istream *stream, pvector<unsigned char> &result, size_t max_bytes);
|
||||
|
||||
|
||||
protected:
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#include "virtualFileMount.h"
|
||||
#include "virtualFileSimple.h"
|
||||
#include "zStream.h"
|
||||
|
||||
TypeHandle VirtualFileMount::_type_handle;
|
||||
|
||||
@ -53,6 +54,67 @@ make_virtual_file(const Filename &local_filename,
|
||||
return file.p();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMount::read_file
|
||||
// Access: Public, Virtual
|
||||
// Description: Fills up the indicated pvector with the contents of
|
||||
// the file, if it is a regular file. Returns true on
|
||||
// success, false otherwise.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileMount::
|
||||
read_file(const Filename &file, bool do_uncompress,
|
||||
pvector<unsigned char> &result) const {
|
||||
result.clear();
|
||||
|
||||
istream *in = open_read_file(file, do_uncompress);
|
||||
if (in == (istream *)NULL) {
|
||||
express_cat.info()
|
||||
<< "Unable to read " << file << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
off_t file_size = get_file_size(file, in);
|
||||
if (file_size != 0) {
|
||||
result.reserve((size_t)file_size);
|
||||
}
|
||||
|
||||
bool okflag = VirtualFile::simple_read_file(in, result);
|
||||
|
||||
close_read_file(in);
|
||||
|
||||
if (!okflag) {
|
||||
express_cat.info()
|
||||
<< "Error while reading " << file << "\n";
|
||||
}
|
||||
return okflag;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMount::open_read_file
|
||||
// Access: Published, Virtual
|
||||
// Description: Opens the file for reading. Returns a newly
|
||||
// allocated istream on success (which you should
|
||||
// eventually delete when you are done reading).
|
||||
// Returns NULL on failure.
|
||||
//
|
||||
// If do_uncompress is true, the file is also
|
||||
// decompressed on-the-fly using zlib.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
istream *VirtualFileMount::
|
||||
open_read_file(const Filename &file, bool do_uncompress) const {
|
||||
istream *result = open_read_file(file);
|
||||
|
||||
#ifdef HAVE_ZLIB
|
||||
if (result != (istream *)NULL && do_uncompress) {
|
||||
// We have to slip in a layer to decompress the file on the fly.
|
||||
IDecompressStream *wrapper = new IDecompressStream(result, true);
|
||||
result = wrapper;
|
||||
}
|
||||
#endif // HAVE_ZLIB
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMount::close_read_file
|
||||
// Access: Public
|
||||
|
@ -49,7 +49,11 @@ public:
|
||||
virtual bool is_directory(const Filename &file) const=0;
|
||||
virtual bool is_regular_file(const Filename &file) const=0;
|
||||
|
||||
virtual bool read_file(const Filename &file, bool do_uncompress,
|
||||
pvector<unsigned char> &result) const;
|
||||
|
||||
virtual istream *open_read_file(const Filename &file) const=0;
|
||||
istream *open_read_file(const Filename &file, bool do_uncompress) const;
|
||||
void close_read_file(istream *stream) const;
|
||||
virtual off_t get_file_size(const Filename &file, istream *stream) const=0;
|
||||
virtual off_t get_file_size(const Filename &file) const=0;
|
||||
|
@ -63,6 +63,36 @@ is_regular_file(const Filename &file) const {
|
||||
return (_multifile->find_subfile(file) >= 0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountMultifile::read_file
|
||||
// Access: Public, Virtual
|
||||
// Description: Fills up the indicated pvector with the contents of
|
||||
// the file, if it is a regular file. Returns true on
|
||||
// success, false otherwise.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileMountMultifile::
|
||||
read_file(const Filename &file, bool do_uncompress,
|
||||
pvector<unsigned char> &result) const {
|
||||
if (do_uncompress) {
|
||||
// If the file is to be decompressed, we'd better just use the
|
||||
// higher-level implementation, which includes support for
|
||||
// on-the-fly decompression.
|
||||
return VirtualFileMount::read_file(file, do_uncompress, result);
|
||||
}
|
||||
|
||||
// But if we're just reading a straight file, let the Multifile do
|
||||
// the reading, which avoids a few levels of buffer copies.
|
||||
|
||||
int subfile_index = _multifile->find_subfile(file);
|
||||
if (subfile_index < 0) {
|
||||
express_cat.info()
|
||||
<< "Unable to read " << file << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return _multifile->read_subfile(subfile_index, result);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountMultifile::open_read_file
|
||||
// Access: Public, Virtual
|
||||
|
@ -38,6 +38,9 @@ public:
|
||||
virtual bool is_directory(const Filename &file) const;
|
||||
virtual bool is_regular_file(const Filename &file) const;
|
||||
|
||||
virtual bool read_file(const Filename &file, bool do_uncompress,
|
||||
pvector<unsigned char> &result) const;
|
||||
|
||||
virtual istream *open_read_file(const Filename &file) const;
|
||||
virtual off_t get_file_size(const Filename &file, istream *stream) const;
|
||||
virtual off_t get_file_size(const Filename &file) const;
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include "virtualFileSimple.h"
|
||||
#include "virtualFileMount.h"
|
||||
#include "virtualFileList.h"
|
||||
#include "zStream.h"
|
||||
|
||||
TypeHandle VirtualFileSimple::_type_handle;
|
||||
|
||||
@ -106,24 +105,15 @@ istream *VirtualFileSimple::
|
||||
open_read_file(bool auto_unwrap) const {
|
||||
|
||||
// Will we be automatically unwrapping a .pz file?
|
||||
bool do_unwrap = (_implicit_pz_file || (auto_unwrap && _local_filename.get_extension() == "pz"));
|
||||
bool do_uncompress = (_implicit_pz_file || (auto_unwrap && _local_filename.get_extension() == "pz"));
|
||||
|
||||
Filename local_filename(_local_filename);
|
||||
if (do_unwrap) {
|
||||
if (do_uncompress) {
|
||||
// .pz files are always binary, of course.
|
||||
local_filename.set_binary();
|
||||
}
|
||||
|
||||
istream *result = _mount->open_read_file(local_filename);
|
||||
#ifdef HAVE_ZLIB
|
||||
if (result != (istream *)NULL && do_unwrap) {
|
||||
// We have to slip in a layer to decompress the file on the fly.
|
||||
IDecompressStream *wrapper = new IDecompressStream(result, true);
|
||||
result = wrapper;
|
||||
}
|
||||
#endif // HAVE_ZLIB
|
||||
|
||||
return result;
|
||||
return _mount->open_read_file(local_filename, do_uncompress);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
@ -170,6 +160,28 @@ get_timestamp() const {
|
||||
return _mount->get_timestamp(_local_filename);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileSimple::read_file
|
||||
// Access: Public, Virtual
|
||||
// Description: Fills up the indicated pvector with the contents of
|
||||
// the file, if it is a regular file. Returns true on
|
||||
// success, false otherwise.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileSimple::
|
||||
read_file(pvector<unsigned char> &result, bool auto_unwrap) const {
|
||||
|
||||
// Will we be automatically unwrapping a .pz file?
|
||||
bool do_uncompress = (_implicit_pz_file || (auto_unwrap && _local_filename.get_extension() == "pz"));
|
||||
|
||||
Filename local_filename(_local_filename);
|
||||
if (do_uncompress) {
|
||||
// .pz files are always binary, of course.
|
||||
local_filename.set_binary();
|
||||
}
|
||||
|
||||
return _mount->read_file(local_filename, do_uncompress, result);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileSimple::scan_local_directory
|
||||
// Access: Protected, Virtual
|
||||
|
@ -47,6 +47,9 @@ PUBLISHED:
|
||||
virtual off_t get_file_size() const;
|
||||
virtual time_t get_timestamp() const;
|
||||
|
||||
public:
|
||||
virtual bool read_file(pvector<unsigned char> &result, bool auto_unwrap) const;
|
||||
|
||||
protected:
|
||||
virtual bool scan_local_directory(VirtualFileList *file_list,
|
||||
const ov_set<string> &mount_points) const;
|
||||
|
@ -765,6 +765,32 @@ get_global_ptr() {
|
||||
return _global_ptr;
|
||||
}
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileSystem::__py__read_file
|
||||
// Access: Published
|
||||
// Description: Convenience function; returns the entire contents of
|
||||
// the indicated file as a string.
|
||||
//
|
||||
// This variant on read_file() is implemented directly
|
||||
// for Python, as a small optimization, to avoid the
|
||||
// double-construction of a string object that would be
|
||||
// otherwise required for the return value.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
PyObject *VirtualFileSystem::
|
||||
__py__read_file(const Filename &filename, bool auto_unwrap) const {
|
||||
pvector<unsigned char> pv;
|
||||
bool okflag = read_file(filename, pv, auto_unwrap);
|
||||
nassertr(okflag, NULL);
|
||||
|
||||
if (pv.empty()) {
|
||||
return PyString_FromStringAndSize("", 0);
|
||||
} else {
|
||||
return PyString_FromStringAndSize((const char *)&pv[0], pv.size());
|
||||
}
|
||||
}
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileSystem::open_read_file
|
||||
// Access: Published
|
||||
|
@ -91,7 +91,11 @@ PUBLISHED:
|
||||
|
||||
static VirtualFileSystem *get_global_ptr();
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
BLOCKING PyObject *__py__read_file(const Filename &filename, bool auto_unwrap) const;
|
||||
#endif // HAVE_PYTHON
|
||||
BLOCKING INLINE string read_file(const Filename &filename, bool auto_unwrap) const;
|
||||
|
||||
BLOCKING istream *open_read_file(const Filename &filename, bool auto_unwrap) const;
|
||||
BLOCKING static void close_read_file(istream *stream);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user