前に作成したbasic_filterbufクラスを使って、Inflateを行うクラスを作ってみました。
InflateについてはRFC1950を参照して下さい。Deflateにより圧縮されたデータを展開する方式です。
zlibを使っていますので、前に説明した方法を使ってzlib.libをリンクしておく必要があります。
また、basic_filterbufクラスを定義したヘッダファイルをfilterbuf.hというファイル名で用意しておく必要があります。
コードは以下のようになります。
ちょっと長いですがご勘弁を。
機能としては、このクラスを使ってDeflateにより圧縮されたデータの書き込みを行うと、それが展開されて出力先に出力される、という機能を持っていることになります。
普通、この手のクラスは書き込みで圧縮され、読み込みで展開されます。それとは逆なので注意してください。
また、このクラスは読み込みについては未実装です。そのため、展開専門です。圧縮されたデータが事前に存在しないとまったく役に立ちません。
ところでこのクラスは読み込み時に何をするべきなんでしょうね。書き込みで展開されるということは読み込みで圧縮するべきなのかな?
しかし関数名がbasic_inflatebufなので、読み込みでも展開するという考えもありですね。
(注意)本コードは正しく動作しません。詳細は「C++ストリームの拡張方法について」を参照して下さい。
#include "filterbuf.h"
#include <vector>
#include <zlib.h>
template <class Elem, class Tr = std::char_traits<Elem> >
class basic_inflatebuf : public basic_filterbuf<Elem, Tr>
{
public:
explicit basic_inflatebuf(std::basic_streambuf<Elem, Tr>* _Buffer, bool _Delete = false)
: basic_filterbuf<Elem, Tr>(_Buffer, _Delete), _read_phase(init_phase), _write_phase(init_phase)
{
}
virtual ~basic_inflatebuf(void)
{
switch (_write_phase) {
case data_phase:
write_end_init();
break;
}
}
protected:
virtual std::basic_streambuf<Elem, Tr>* setbuf(char_type* _Buffer, std::streamsize _Count)
{
// 未実装
return this;
}
virtual pos_type seekoff(off_type _Off, std::ios_base::seekdir _Way, std::ios_base::openmode _Which = std::ios_base::in | std::ios_base::out)
{
return traits_type::eof();
}
virtual pos_type seekpos(pos_type _Sp, std::ios_base::openmode _Which = std::ios_base::in | std::ios_base::out)
{
return traits_type::eof();
}
virtual int sync()
{
switch (_write_phase) {
case data_phase:
if (!flush_write()) {
return traits_type::eof();
}
break;
}
// 出力/入力先もsyncする。
return basic_filterbuf<Elem, Tr>::sync();
}
virtual std::streamsize xsgetn(char_type* _Ptr, std::streamsize _Count)
{
// 未実装
return 0;
}
virtual std::streamsize xsputn(const char_type* _Ptr, std::streamsize _Count)
{
std::streamsize _Result = 0;
switch (_write_phase) {
case init_phase:
if (!write_init()) {
return 0;
}
_write_phase = data_phase;
case data_phase:
_Result = write_data(_Ptr, _Count);
if (_Result == _Count) {
return _Result;
}
if (!write_end_init()) {
return 0;
}
_write_phase = end_phase;
case end_phase:
default:
return _Result;
}
}
private:
bool write_init(void)
{
_write_zstream.zalloc = Z_NULL;
_write_zstream.zfree = Z_NULL;
_write_zstream.opaque = Z_NULL;
_write_zstream.next_in = NULL;
_write_zstream.avail_in = 0;
_write_zstream.next_out = NULL;
_write_zstream.avail_out = 0;
// -MAX_WBITSの意味が不明。しかしこうしないと動かない。
if (inflateInit2(&_write_zstream, -MAX_WBITS) != Z_OK) {
return false;
}
_write_buffer.resize(default_buffer_size);
_write_zstream.next_out = reinterpret_cast<Bytef*>(&_write_buffer[0]);
_write_zstream.avail_out = static_cast<uInt>(_write_buffer.size() * sizeof(buffer_type::value_type) / sizeof(Bytef));
return true;
}
std::streamsize write_data(const char_type* _Ptr, std::streamsize _Count)
{
_write_zstream.next_in = const_cast<Bytef*>(reinterpret_cast<const Bytef*>(_Ptr));
_write_zstream.avail_in = static_cast<uInt>(_Count * sizeof(char_type) / sizeof(Bytef));
do {
int iResult = inflate(&_write_zstream, Z_SYNC_FLUSH);
switch (iResult) {
case Z_OK:
if (_write_zstream.avail_out == 0) {
if (!flush_write()) {
return 0;
}
}
break;
case Z_STREAM_END:
if (!flush_write()) {
return 0;
}
return _Count - static_cast<std::streamsize>(_write_zstream.avail_in * sizeof(Bytef) / sizeof(char_type));
default:
return 0;
}
}while (_write_zstream.avail_in != 0);
return _Count;
}
bool write_end_init(void)
{
int iResult = inflateEnd(&_write_zstream);
if (iResult == Z_OK) {
return true;
}
return false;
}
bool flush_write(void)
{
std::streamsize _Count = static_cast<std::streamsize>(_write_buffer.size() - _write_zstream.avail_out * sizeof(Bytef) / sizeof(char_type));
std::streamsize _Result = basic_filterbuf<Elem, Tr>::xsputn(&_write_buffer[0], _Count);
if (_Count != _Result) {
return false;
}
_write_zstream.next_out = reinterpret_cast<Bytef*>(&_write_buffer[0]);
_write_zstream.avail_out = static_cast<uInt>(_write_buffer.size() * sizeof(char_type) / sizeof(Bytef));
return true;
}
z_stream _write_zstream;
enum phase_type {
init_phase,
data_phase,
end_phase,
};
enum {
default_buffer_size = 2048,
};
phase_type _read_phase;
phase_type _write_phase;
typedef std::vector<char_type> buffer_type;
buffer_type _write_buffer;
};
xsputnをオーバーライドしてそこで主な処理を行っています。
xsputnをオーバーライドする以外にもbasic_streambufを拡張するやり方は色々あるので、これは1つのやり方だと思っておいて下さい。