为gzip压缩流编写自定义streambuf的问题

时间:2016-06-09 14:01:55

标签: c++ gzip streambuf

我正在实现自己的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类派生输入和输出streambufs。 基本上,实现是通过遵循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;
}

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

  

在内部,如果成员失败返回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()失败。 我想了解我在实施过程中遗漏的内容,以及我应该怎样做才能改善这种实现。

1 个答案:

答案 0 :(得分:0)

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