#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]; 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)); } }