Я реализую свой собственный класс 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()
терпит неудачу. Я хочу понять, чего я пропустил в реализации, и что мне делать, чтобы улучшить эту реализацию.
Если вы еще не знакомы, то стоит посмотреть на средство повышения io_streams, есть сжатый поток, который можно добавить в конвейер для чтения/записи - это хороший подход. – Nim
@Nim, на самом деле я попытался реализовать что-то похожее на boost_io_streams (с конвейером). Вот еще мой вопрос: [link] (http://stackoverflow.com/questions/37627112/writing-gzipped-output-file-without-extra-disk-space-with-pipe) –
@Nim, BTW, мне нужно ищущий gzipped поток, и, как я знаю, в boost :: iostreams 'filtering_stream' не работает с 'gzip_compressor()' :) –