You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openmw-tes3mp/components/files/constrainedfilestream.cpp

123 lines
3.4 KiB
C++

#include "constrainedfilestream.hpp"
#include <streambuf>
#include "lowlevelfile.hpp"
namespace
{
// somewhat arbitrary though 64KB buffers didn't seem to improve performance any
const size_t sBufferSize = 4096;
}
namespace Files
{
class ConstrainedFileStreamBuf : public std::streambuf
{
size_t mOrigin;
size_t mSize;
LowLevelFile mFile;
char mBuffer[sBufferSize];
public:
ConstrainedFileStreamBuf(const std::string &fname, size_t start, size_t length)
{
mFile.open (fname.c_str ());
mSize = length != 0xFFFFFFFF ? length : mFile.size () - start;
if (start != 0)
mFile.seek(start);
setg(0,0,0);
mOrigin = start;
}
virtual int_type underflow()
{
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());
}
virtual pos_type 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(0, 0, 0);
return newPos;
}
virtual pos_type 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 ((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(0, 0, 0);
return pos;
}
};
ConstrainedFileStream::ConstrainedFileStream(const char *filename, size_t start, size_t length)
: std::istream(new ConstrainedFileStreamBuf(filename, start, length))
{
}
ConstrainedFileStream::~ConstrainedFileStream()
{
delete rdbuf();
}
IStreamPtr openConstrainedFileStream(const char *filename,
size_t start, size_t length)
{
return IStreamPtr(new ConstrainedFileStream(filename, start, length));
}
}