mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-10-29 10:26:38 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			172 lines
		
	
	
	
		
			3.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			172 lines
		
	
	
	
		
			3.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "constrainedfiledatastream.hpp"
 | |
| #include "lowlevelfile.hpp"
 | |
| 
 | |
| #include <stdexcept>
 | |
| #include <cassert>
 | |
| 
 | |
| #include <libs/platform/stdint.h>
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| class ConstrainedDataStream : public Ogre::DataStream {
 | |
| public:
 | |
| 
 | |
| 	static const size_t sBufferSize = 4096; // somewhat arbitrary though 64KB buffers didn't seem to improve performance any
 | |
| 	static const size_t sBufferThreshold = 1024; // reads larger than this bypass buffering as cost of memcpy outweighs cost of system call
 | |
| 
 | |
|     ConstrainedDataStream(const Ogre::String &fname, size_t start, size_t length)
 | |
| 	{
 | |
| 		mFile.open (fname.c_str ());
 | |
| 		mSize  = length != 0xFFFFFFFF ? length : mFile.size () - start;
 | |
| 
 | |
| 		mPos    = 0;
 | |
| 		mOrigin = start;
 | |
| 		mExtent = start + mSize;
 | |
| 
 | |
| 		mBufferOrigin = 0;
 | |
| 		mBufferExtent = 0;
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	size_t read(void* buf, size_t count)
 | |
| 	{
 | |
| 		assert (mPos <= mSize);
 | |
| 
 | |
| 		uint8_t * out = reinterpret_cast <uint8_t *> (buf);
 | |
| 
 | |
| 		size_t posBeg = mOrigin + mPos;
 | |
| 		size_t posEnd = posBeg + count;
 | |
| 
 | |
| 		if (posEnd > mExtent)
 | |
| 			posEnd = mExtent;
 | |
| 
 | |
| 		size_t posCur = posBeg;
 | |
| 
 | |
| 		while (posCur != posEnd)
 | |
| 		{
 | |
| 			size_t readLeft = posEnd - posCur;
 | |
| 
 | |
| 			if (posCur < mBufferOrigin || posCur >= mBufferExtent)
 | |
| 			{
 | |
| 				if (readLeft >= sBufferThreshold || (posCur == mOrigin && posEnd == mExtent))
 | |
| 				{
 | |
| 					assert (mFile.tell () == mBufferExtent);
 | |
| 
 | |
| 					if (posCur != mBufferExtent)
 | |
| 						mFile.seek (posCur);
 | |
| 
 | |
| 					posCur += mFile.read (out, readLeft);
 | |
| 
 | |
| 					mBufferOrigin = mBufferExtent = posCur;
 | |
| 
 | |
| 					mPos = posCur - mOrigin;
 | |
| 
 | |
| 					return posCur - posBeg;
 | |
| 				}
 | |
| 				else
 | |
| 				{
 | |
| 					size_t newBufferOrigin;
 | |
| 
 | |
| 					if ((posCur < mBufferOrigin) && (mBufferOrigin - posCur < sBufferSize))
 | |
| 						newBufferOrigin = std::max (mOrigin, mBufferOrigin > sBufferSize ? mBufferOrigin - sBufferSize : 0);
 | |
| 					else
 | |
| 						newBufferOrigin = posCur;
 | |
| 
 | |
| 					fill (newBufferOrigin);
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			size_t xfer = std::min (readLeft, mBufferExtent - posCur);
 | |
| 
 | |
| 			memcpy (out, mBuffer + (posCur - mBufferOrigin), xfer);
 | |
| 
 | |
| 			posCur += xfer;
 | |
| 			out += xfer;
 | |
| 		}
 | |
| 
 | |
| 		count = posEnd - posBeg;
 | |
| 		mPos += count;
 | |
| 		return count;
 | |
| 	}
 | |
| 
 | |
|     void skip(long count)
 | |
|     {
 | |
| 		assert (mPos <= mSize);
 | |
| 
 | |
|         if((count >= 0 && (size_t)count <= mSize-mPos) ||
 | |
|            (count < 0 && (size_t)-count <= mPos))
 | |
| 			mPos += count;
 | |
|     }
 | |
| 
 | |
|     void seek(size_t pos)
 | |
|     {
 | |
| 		assert (mPos <= mSize);
 | |
| 
 | |
|         if (pos < mSize)
 | |
| 			mPos = pos;
 | |
|     }
 | |
| 
 | |
|     virtual size_t tell() const
 | |
|     {
 | |
| 		assert (mPos <= mSize);
 | |
| 
 | |
| 		return mPos;
 | |
| 	}
 | |
| 
 | |
|     virtual bool eof() const
 | |
|     {
 | |
| 		assert (mPos <= mSize);
 | |
| 
 | |
| 		return mPos == mSize;
 | |
| 	}
 | |
| 
 | |
|     virtual void close()
 | |
|     {
 | |
| 		mFile.close();
 | |
| 	}
 | |
| 
 | |
| private:
 | |
| 
 | |
| 	void fill (size_t newOrigin)
 | |
| 	{
 | |
| 		assert (mFile.tell () == mBufferExtent);
 | |
| 
 | |
| 		size_t newExtent = newOrigin + sBufferSize;
 | |
| 
 | |
| 		if (newExtent > mExtent)
 | |
| 			newExtent = mExtent;
 | |
| 
 | |
| 		size_t oldExtent = mBufferExtent;
 | |
| 
 | |
| 		if (newOrigin != oldExtent)
 | |
| 			mFile.seek (newOrigin);
 | |
| 
 | |
| 		mBufferOrigin = mBufferExtent = newOrigin;
 | |
| 
 | |
| 		size_t amountRequested = newExtent - newOrigin;
 | |
| 
 | |
| 		size_t amountRead = mFile.read (mBuffer, amountRequested);
 | |
| 
 | |
| 		if (amountRead != amountRequested)
 | |
| 			throw std::runtime_error ("An unexpected condition occurred while reading from a file.");
 | |
| 
 | |
| 		mBufferExtent = newExtent;
 | |
| 	}
 | |
| 
 | |
| 	LowLevelFile mFile;
 | |
| 
 | |
| 	size_t mOrigin;
 | |
| 	size_t mExtent;
 | |
| 	size_t mPos;
 | |
| 
 | |
| 	uint8_t mBuffer [sBufferSize];
 | |
| 	size_t mBufferOrigin;
 | |
| 	size_t mBufferExtent;
 | |
| };
 | |
| 
 | |
| } // end of unnamed namespace
 | |
| 
 | |
| Ogre::DataStreamPtr openConstrainedFileDataStream (char const * filename, size_t offset, size_t length)
 | |
| {
 | |
| 	return Ogre::DataStreamPtr(new ConstrainedDataStream(filename, offset, length));
 | |
| }
 |