为gzipped流编写自定义streambuf的问题

Problems with writing a custom streambuf for gzipped streams

本文关键字:streambuf 问题 自定义 gzipped      更新时间:2023-10-16

我正在实现我自己的streambuf类,用于编写压缩输出文件。这是它的样子。

template <class T>
class gzstreambufbase : public std::streambuf
{
protected:
static const int bufferSize = 8192;
public:
gzstreambufbase();
~gzstreambufbase();
bool close();
bool is_open();
protected:
virtual T* open(const std::string& name, std::ios::openmode mode) = 0;
virtual int sync();
// flush the characters in the buffer
int flush_buffer();
protected:
gzFile             filePtr_;
std::ios::openmode mode_;
bool               opened_;
char               buffer_[bufferSize];
std::string        fileName_;
};

然后,我从这个基础派生出相应的输入流和输出流的新的igzstreambufogzstreambuf类。基本上,实现是按照Nicolai M.Josuttis[C++标准库,the:A Tutorial and Reference]一书中的示例完成的。

让我们只看一下ogzstream的实现。

ogzstreambuf::ogzstreambuf()
{
// initialize data buffer
// one character less to let the bufferSizeth
// character cause a call of overflow()
setp( buffer_, buffer_ + (bufferSize - 1));
}
ogzstreambuf*
ogzstreambuf::open(const std::string& name, std::ios::openmode mode)
{
if (is_open())
return (ogzstreambuf*)0;
mode_ = mode;
fileName_ = name;
filePtr_ = gzopen(fileName_.c_str(), "wb");
CUSTOM_CHECK(0 != filePtr_, ("GZIP_IO_ERROR", strerror(errno)));
opened_ = 1;
return this;
}
std::streampos
ogzstreambuf::seekpos(std::streampos offset, std::ios_base::openmode which)
{
return seekImpl(offset, std::ios_base::beg, which);
}
std::streampos
ogzstreambuf::seekoff(std::streamoff offset, std::ios_base::seekdir way, std::ios_base::openmode which)
{
return seekImpl(offset, way, which);
}
std::streampos
ogzstreambuf::seekImpl(std::streamoff offset, std::ios_base::seekdir way, std::ios_base::openmode which)
{
assert(!fileName_.empty(), "");
assert(LONG_MAX != offset, "");
assert(std::ios_base::out == which, "");
assert( way != std::ios_base::end,
"zlib doesn't support the value SEEK_END in gzseek()." );
if (!flush_buffer())
return std::streampos(EOF);
const long newPos = gzseek(filePtr_, offset,
(way == std::ios_base::beg ? SEEK_SET : SEEK_CUR));
CUSTOM_CHECK((long) offset == newPos, ("GZIP_IO_ERROR", strerror(errno)));
setp(buffer_, buffer_ + (bufferSize - 1));
return offset;
}

因此,问题是对我自己实现的ogzstream对象(内部保存ogzstreambuf的实例)的tellp()调用返回-1(EOF)值,因为:

在内部,如果成员fail返回true,则函数返回-1。否则返回rdbuf()->pubseekoff(0,cur,out)

引用自cpp。最后flush_buffer()返回0,因为pptr() - pbase();等于0:

template <class T>
int gzstreambufbase<T>::flush_buffer()
{
// Separate the writing of the buffer from overflow() and
// sync() operation.
int w = pptr() - pbase();
if ( gzwrite( filePtr_, pbase(), w) != w)
return EOF;
pbump( -w); // reset put pointer acccordingly
return w;
}

结果,pubseekoff()返回EOFtellp()失败。我想了解我在实现过程中错过了什么,以及我应该做些什么来提高这一认识。

经过适当的调试,我自己终于发现了这个问题。只需要检查EOF == flush_buffer()而不是!flush_buffer()。。。