|
|
|
@ -1225,4 +1225,148 @@ public:
@@ -1225,4 +1225,148 @@ public:
|
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/** Wrapper around a FILE* that implements a ring buffer to
|
|
|
|
|
* deserialize from. It guarantees the ability to rewind |
|
|
|
|
* a given number of bytes. */ |
|
|
|
|
class CBufferedFile |
|
|
|
|
{ |
|
|
|
|
private: |
|
|
|
|
FILE *src; // source file
|
|
|
|
|
uint64 nSrcPos; // how many bytes have been read from source
|
|
|
|
|
uint64 nReadPos; // how many bytes have been read from this
|
|
|
|
|
uint64 nReadLimit; // up to which position we're allowed to read
|
|
|
|
|
uint64 nRewind; // how many bytes we guarantee to rewind
|
|
|
|
|
std::vector<char> vchBuf; // the buffer
|
|
|
|
|
|
|
|
|
|
short state; |
|
|
|
|
short exceptmask; |
|
|
|
|
|
|
|
|
|
protected: |
|
|
|
|
void setstate(short bits, const char *psz) { |
|
|
|
|
state |= bits; |
|
|
|
|
if (state & exceptmask) |
|
|
|
|
throw std::ios_base::failure(psz); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// read data from the source to fill the buffer
|
|
|
|
|
bool Fill() { |
|
|
|
|
unsigned int pos = nSrcPos % vchBuf.size(); |
|
|
|
|
unsigned int readNow = vchBuf.size() - pos; |
|
|
|
|
unsigned int nAvail = vchBuf.size() - (nSrcPos - nReadPos) - nRewind; |
|
|
|
|
if (nAvail < readNow) |
|
|
|
|
readNow = nAvail; |
|
|
|
|
if (readNow == 0) |
|
|
|
|
return false; |
|
|
|
|
size_t read = fread((void*)&vchBuf[pos], 1, readNow, src); |
|
|
|
|
if (read == 0) { |
|
|
|
|
setstate(std::ios_base::failbit, feof(src) ? "CBufferedFile::Fill : end of file" : "CBufferedFile::Fill : fread failed"); |
|
|
|
|
return false; |
|
|
|
|
} else { |
|
|
|
|
nSrcPos += read; |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
int nType; |
|
|
|
|
int nVersion; |
|
|
|
|
|
|
|
|
|
CBufferedFile(FILE *fileIn, uint64 nBufSize, uint64 nRewindIn, int nTypeIn, int nVersionIn) : |
|
|
|
|
src(fileIn), nSrcPos(0), nReadPos(0), nReadLimit((uint64)(-1)), nRewind(nRewindIn), vchBuf(nBufSize, 0), |
|
|
|
|
state(0), exceptmask(std::ios_base::badbit | std::ios_base::failbit), nType(nTypeIn), nVersion(nVersionIn) { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// check whether no error occurred
|
|
|
|
|
bool good() const { |
|
|
|
|
return state == 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// check whether we're at the end of the source file
|
|
|
|
|
bool eof() const { |
|
|
|
|
return nReadPos == nSrcPos && feof(src); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// read a number of bytes
|
|
|
|
|
CBufferedFile& read(char *pch, size_t nSize) { |
|
|
|
|
if (nSize + nReadPos > nReadLimit) |
|
|
|
|
throw std::ios_base::failure("Read attempted past buffer limit"); |
|
|
|
|
if (nSize + nRewind > vchBuf.size()) |
|
|
|
|
throw std::ios_base::failure("Read larger than buffer size"); |
|
|
|
|
while (nSize > 0) { |
|
|
|
|
if (nReadPos == nSrcPos) |
|
|
|
|
Fill(); |
|
|
|
|
unsigned int pos = nReadPos % vchBuf.size(); |
|
|
|
|
size_t nNow = nSize; |
|
|
|
|
if (nNow + pos > vchBuf.size()) |
|
|
|
|
nNow = vchBuf.size() - pos; |
|
|
|
|
if (nNow + nReadPos > nSrcPos) |
|
|
|
|
nNow = nSrcPos - nReadPos; |
|
|
|
|
memcpy(pch, &vchBuf[pos], nNow); |
|
|
|
|
nReadPos += nNow; |
|
|
|
|
pch += nNow; |
|
|
|
|
nSize -= nNow; |
|
|
|
|
} |
|
|
|
|
return (*this); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// return the current reading position
|
|
|
|
|
uint64 GetPos() { |
|
|
|
|
return nReadPos; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// rewind to a given reading position
|
|
|
|
|
bool SetPos(uint64 nPos) { |
|
|
|
|
nReadPos = nPos; |
|
|
|
|
if (nReadPos + nRewind < nSrcPos) { |
|
|
|
|
nReadPos = nSrcPos - nRewind; |
|
|
|
|
return false; |
|
|
|
|
} else if (nReadPos > nSrcPos) { |
|
|
|
|
nReadPos = nSrcPos; |
|
|
|
|
return false; |
|
|
|
|
} else { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool Seek(uint64 nPos) { |
|
|
|
|
long nLongPos = nPos; |
|
|
|
|
if (nPos != (uint64)nLongPos) |
|
|
|
|
return false; |
|
|
|
|
if (fseek(src, nLongPos, SEEK_SET)) |
|
|
|
|
return false; |
|
|
|
|
nLongPos = ftell(src); |
|
|
|
|
nSrcPos = nLongPos; |
|
|
|
|
nReadPos = nLongPos; |
|
|
|
|
state = 0; |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// prevent reading beyond a certain position
|
|
|
|
|
// no argument removes the limit
|
|
|
|
|
bool SetLimit(uint64 nPos = (uint64)(-1)) { |
|
|
|
|
if (nPos < nReadPos) |
|
|
|
|
return false; |
|
|
|
|
nReadLimit = nPos; |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
template<typename T> |
|
|
|
|
CBufferedFile& operator>>(T& obj) { |
|
|
|
|
// Unserialize from this stream
|
|
|
|
|
::Unserialize(*this, obj, nType, nVersion); |
|
|
|
|
return (*this); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// search for a given byte in the stream, and remain positioned on it
|
|
|
|
|
void FindByte(char ch) { |
|
|
|
|
while (true) { |
|
|
|
|
if (nReadPos == nSrcPos) |
|
|
|
|
Fill(); |
|
|
|
|
if (vchBuf[nReadPos % vchBuf.size()] == ch) |
|
|
|
|
break; |
|
|
|
|
nReadPos++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
#endif |
|
|
|
|