2016-06-09 2 views
0

Я реализую свой собственный класс streambuf для записи сжатых выходных файлов. Вот что это будет выглядеть.Проблемы с написанием пользовательского streambuf для gzipped-потоков

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 классов для входных и выходных streambufs соответственно. В основном, реализация была выполнена по примеру Николая М. Йосуттиса [C++ Standard Library, The: A Tutorial and Reference] book.

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

+1

Если вы еще не знакомы, то стоит посмотреть на средство повышения io_streams, есть сжатый поток, который можно добавить в конвейер для чтения/записи - это хороший подход. – Nim

+0

@Nim, на самом деле я попытался реализовать что-то похожее на boost_io_streams (с конвейером). Вот еще мой вопрос: [link] (http://stackoverflow.com/questions/37627112/writing-gzipped-output-file-without-extra-disk-space-with-pipe) –

+0

@Nim, BTW, мне нужно ищущий gzipped поток, и, как я знаю, в boost :: iostreams 'filtering_stream ' не работает с 'gzip_compressor()' :) –

ответ

0

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

Смежные вопросы