Проблемы с написанием пользовательского 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_;
};

Тогда я извлекаю из этой базы новую igzstreambuf а также ogzstreambuf классы для потокового буфера ввода и вывода соответственно. По сути, реализация была сделана по примеру Николая М. Йосуттиса [книга C++ Standard Library, 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) значение, так как:

Внутренне, если member 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() возвращается EOF а также tellp() выходит из строя. Я хочу понять, что я упустил в реализации и что я должен сделать, чтобы улучшить эту реализацию.

1 ответ

Решение

После правильной отладки я сам наконец нашел проблему. Нужно было просто проверить EOF == flush_buffer() вместо !flush_buffer()...

Другие вопросы по тегам