为 gzip 流编写自定义 streambuf 的问题
Problems with writing a custom streambuf for gzipped streams
我正在实现自己的 streambuf class 来编写压缩输出文件。
这是它的样子。
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_;
};
然后我从这个基础派生新的 igzstreambuf
和 ogzstreambuf
classes 相应地用于输入和输出 streambufs。
基本上,实现是按照 Nicolai M. Josuttis [C++ 标准库,教程和参考] 书中的示例完成的。
让我们只看一下 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;
}
所以,问题是 tellp()
的调用是我自己实现的 ogzstream
对象(内部包含 ogzstreambuf
的一个实例)returns -1(EOF)
值,因为:
Internally, if member fail returns true, the function returns -1.
Otherwise, it returns rdbuf()->pubseekoff(0,cur,out)
;
引自cpp。
最后 flush_buffer()
returns 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()
returns EOF
和 tellp()
失败。
我想了解我在实施中遗漏了什么,以及我应该如何改进这种认识。
经过适当的调试,我自己终于找到了问题所在。只需要检查 EOF == flush_buffer()
而不是 !flush_buffer()
...
我正在实现自己的 streambuf class 来编写压缩输出文件。 这是它的样子。
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_;
};
然后我从这个基础派生新的 igzstreambuf
和 ogzstreambuf
classes 相应地用于输入和输出 streambufs。
基本上,实现是按照 Nicolai M. Josuttis [C++ 标准库,教程和参考] 书中的示例完成的。
让我们只看一下 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;
}
所以,问题是 tellp()
的调用是我自己实现的 ogzstream
对象(内部包含 ogzstreambuf
的一个实例)returns -1(EOF)
值,因为:
Internally, if member fail returns true, the function returns -1. Otherwise, it returns
rdbuf()->pubseekoff(0,cur,out)
;
引自cpp。
最后 flush_buffer()
returns 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()
returns EOF
和 tellp()
失败。
我想了解我在实施中遗漏了什么,以及我应该如何改进这种认识。
经过适当的调试,我自己终于找到了问题所在。只需要检查 EOF == flush_buffer()
而不是 !flush_buffer()
...