diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 57a60322d5..7121bde103 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -206,7 +206,7 @@ IF(NOT WIN32 AND NOT APPLE) ENDIF() add_component_dir (files linuxpath androidpath windowspath macospath fixedpath multidircollection collections configurationmanager - lowlevelfile constrainedfilestream memorystream hash configfileparser openfile + lowlevelfile constrainedfilestream memorystream hash configfileparser openfile constrainedfilestreambuf ) add_component_dir (compiler diff --git a/components/files/constrainedfilestream.cpp b/components/files/constrainedfilestream.cpp index 0beaf5facc..aafcacba74 100644 --- a/components/files/constrainedfilestream.cpp +++ b/components/files/constrainedfilestream.cpp @@ -1,109 +1,8 @@ #include "constrainedfilestream.hpp" -#include -#include - -#include "lowlevelfile.hpp" - -namespace -{ -// somewhat arbitrary though 64KB buffers didn't seem to improve performance any -const size_t sBufferSize = 8192; -} - namespace Files { - class ConstrainedFileStreamBuf : public std::streambuf - { - - size_t mOrigin; - size_t mSize; - - LowLevelFile mFile; - - char mBuffer[sBufferSize]{0}; - - public: - ConstrainedFileStreamBuf(const std::string &fname, size_t start, size_t length) - { - mFile.open (fname.c_str ()); - mSize = length != std::numeric_limits::max() ? length : mFile.size () - start; - - if (start != 0) - mFile.seek(start); - - setg(nullptr,nullptr,nullptr); - - mOrigin = start; - } - - int_type underflow() override - { - if(gptr() == egptr()) - { - size_t toRead = std::min((mOrigin+mSize)-(mFile.tell()), sBufferSize); - // Read in the next chunk of data, and set the read pointers on success - // Failure will throw exception in LowLevelFile - size_t got = mFile.read(mBuffer, toRead); - setg(&mBuffer[0], &mBuffer[0], &mBuffer[0]+got); - } - if(gptr() == egptr()) - return traits_type::eof(); - - return traits_type::to_int_type(*gptr()); - } - - pos_type seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode) override - { - if((mode&std::ios_base::out) || !(mode&std::ios_base::in)) - return traits_type::eof(); - - // new file position, relative to mOrigin - size_t newPos; - switch (whence) - { - case std::ios_base::beg: - newPos = offset; - break; - case std::ios_base::cur: - newPos = (mFile.tell() - mOrigin - (egptr() - gptr())) + offset; - break; - case std::ios_base::end: - newPos = mSize + offset; - break; - default: - return traits_type::eof(); - } - - if (newPos > mSize) - return traits_type::eof(); - - mFile.seek(mOrigin+newPos); - - // Clear read pointers so underflow() gets called on the next read attempt. - setg(nullptr, nullptr, nullptr); - - return newPos; - } - - pos_type seekpos(pos_type pos, std::ios_base::openmode mode) override - { - if((mode&std::ios_base::out) || !(mode&std::ios_base::in)) - return traits_type::eof(); - - if ((size_t)pos > mSize) - return traits_type::eof(); - - mFile.seek(mOrigin + pos); - - // Clear read pointers so underflow() gets called on the next read attempt. - setg(nullptr, nullptr, nullptr); - return pos; - } - - }; - - ConstrainedFileStream::ConstrainedFileStream(std::unique_ptr buf) + ConstrainedFileStream::ConstrainedFileStream(std::unique_ptr buf) : std::istream(buf.get()) , mBuf(std::move(buf)) { diff --git a/components/files/constrainedfilestream.hpp b/components/files/constrainedfilestream.hpp index b828f0f6f1..4284705d17 100644 --- a/components/files/constrainedfilestream.hpp +++ b/components/files/constrainedfilestream.hpp @@ -1,6 +1,8 @@ #ifndef OPENMW_CONSTRAINEDFILESTREAM_H #define OPENMW_CONSTRAINEDFILESTREAM_H +#include "constrainedfilestreambuf.hpp" + #include #include #include @@ -10,14 +12,13 @@ namespace Files { /// A file stream constrained to a specific region in the file, specified by the 'start' and 'length' parameters. -class ConstrainedFileStream : public std::istream +class ConstrainedFileStream final : public std::istream { public: - ConstrainedFileStream(std::unique_ptr buf); - virtual ~ConstrainedFileStream() {}; + explicit ConstrainedFileStream(std::unique_ptr buf); private: - std::unique_ptr mBuf; + std::unique_ptr mBuf; }; typedef std::shared_ptr IStreamPtr; diff --git a/components/files/constrainedfilestreambuf.cpp b/components/files/constrainedfilestreambuf.cpp new file mode 100644 index 0000000000..1263523ebe --- /dev/null +++ b/components/files/constrainedfilestreambuf.cpp @@ -0,0 +1,83 @@ +#include "constrainedfilestreambuf.hpp" + +#include +#include + +namespace Files +{ + ConstrainedFileStreamBuf::ConstrainedFileStreamBuf(const std::string& fname, std::size_t start, std::size_t length) + : mOrigin(start) + { + mFile.open(fname.c_str()); + mSize = length != std::numeric_limits::max() ? length : mFile.size () - start; + + if (start != 0) + mFile.seek(start); + + setg(nullptr, nullptr, nullptr); + } + + std::streambuf::int_type ConstrainedFileStreamBuf::underflow() + { + if (gptr() == egptr()) + { + const std::size_t toRead = std::min((mOrigin + mSize) - (mFile.tell()), sizeof(mBuffer)); + // Read in the next chunk of data, and set the read pointers on success + // Failure will throw exception in LowLevelFile + const std::size_t got = mFile.read(mBuffer, toRead); + setg(&mBuffer[0], &mBuffer[0], &mBuffer[0] + got); + } + if (gptr() == egptr()) + return traits_type::eof(); + + return traits_type::to_int_type(*gptr()); + } + + std::streambuf::pos_type ConstrainedFileStreamBuf::seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode) + { + if ((mode & std::ios_base::out) || !(mode & std::ios_base::in)) + return traits_type::eof(); + + // new file position, relative to mOrigin + size_t newPos; + switch (whence) + { + case std::ios_base::beg: + newPos = offset; + break; + case std::ios_base::cur: + newPos = (mFile.tell() - mOrigin - (egptr() - gptr())) + offset; + break; + case std::ios_base::end: + newPos = mSize + offset; + break; + default: + return traits_type::eof(); + } + + if (newPos > mSize) + return traits_type::eof(); + + mFile.seek(mOrigin + newPos); + + // Clear read pointers so underflow() gets called on the next read attempt. + setg(nullptr, nullptr, nullptr); + + return newPos; + } + + std::streambuf::pos_type ConstrainedFileStreamBuf::seekpos(pos_type pos, std::ios_base::openmode mode) + { + if ((mode & std::ios_base::out) || !(mode & std::ios_base::in)) + return traits_type::eof(); + + if (static_cast(pos) > mSize) + return traits_type::eof(); + + mFile.seek(mOrigin + pos); + + // Clear read pointers so underflow() gets called on the next read attempt. + setg(nullptr, nullptr, nullptr); + return pos; + } +} diff --git a/components/files/constrainedfilestreambuf.hpp b/components/files/constrainedfilestreambuf.hpp new file mode 100644 index 0000000000..46be98c905 --- /dev/null +++ b/components/files/constrainedfilestreambuf.hpp @@ -0,0 +1,30 @@ +#ifndef OPENMW_CONSTRAINEDFILESTREAMBUF_H +#define OPENMW_CONSTRAINEDFILESTREAMBUF_H + +#include "lowlevelfile.hpp" + +#include + +namespace Files +{ + /// A file streambuf constrained to a specific region in the file, specified by the 'start' and 'length' parameters. + class ConstrainedFileStreamBuf final : public std::streambuf + { + public: + ConstrainedFileStreamBuf(const std::string& fname, std::size_t start, std::size_t length); + + int_type underflow() final; + + pos_type seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode) final; + + pos_type seekpos(pos_type pos, std::ios_base::openmode mode) final; + + private: + std::size_t mOrigin; + std::size_t mSize; + LowLevelFile mFile; + char mBuffer[8192]{0}; + }; +} + +#endif