Base64 encoding/decoding filter
「iostreamの拡張」を見てたら,自分でも何か書きたくなったので「Base64 - Wikipedia」を参考にしながら,Base64にエンコード/デコードするC++のストリームフィルタを作ってみた。
使い方は簡単。base64_(en|de)code_filter を,(出力先|入力元)の stream で初期化して,あとは普通の stream だと思って使えば良い。例えばこんな感じ。
ostringstream oss; { base64_encode_filter os(oss); os << "xoinu_de_gonsu."; } { string s; istringstream iss(oss.str(), ios::binary); base64_decode_filter is(iss); is >> s; cout << "Encoded = " << oss.str() << '\n' << "Decoded = " << s.c_str() << '\n'; }
出力結果:
Encoded = eG9pbnVfZGVfZ29uc3Uu Decoded = xoinu_de_gonsu.
コードは以下の通り。BSDライセンスに基づいてご利用下さい。(例外処理はあまり考慮してませんのであしからず)
class base64_filter_buffer : public std::streambuf { public: base64_filter_buffer(std::ostream& os, bool mime = true) : os_(&os), is_(NULL), mime_(mime), cols_(0) { setp(&buffer_[0], &buffer_[2]); } base64_filter_buffer(std::istream& is) : os_(NULL), is_(&is), mime_(false), cols_(0) {} virtual ~base64_filter_buffer() { overflow(traits_type::eof()); } private: virtual int_type overflow(int_type c) { if (!os_) return traits_type::not_eof(c); char* end = pptr(); if (!traits_type::eq_int_type(c, traits_type::eof())) *end++ = traits_type::to_char_type(c); char out[4]; int length = end - buffer_; if (!length) return traits_type::eof(); encode(buffer_, length, out); os_->write(out, 4); cols_ += 4; if (76 == cols_) { cols_ = 0; if (mime_) os_->write("\n", 1); } setp(&buffer_[0], &buffer_[2]); return traits_type::not_eof(c); } virtual int_type underflow() { if (!is_) return traits_type::eof(); char ch, *it, *end; char chunk[4] = {'\0'}; it = &chunk[0]; end = &chunk[4]; while (is_->good() && it != end) { is_->read(&ch, 1); if ('\n' == ch || ' ' == ch || '\r' == ch) continue; if (!is_->good()) break; *it++ = ch; } while (it != end) *it++ = '='; int length = 0; decode(chunk, buffer_, &length); setg(buffer_, buffer_, buffer_ + length); return length ? traits_type::not_eof(*buffer_) : traits_type::eof(); } private: void encode(const char* in, int length, char out[4]) { static const char base64_table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; switch (length) { case 1: out[0] = base64_table[in[0]>>2]; out[1] = base64_table[(in[0]<<4)&63]; out[2] = '='; out[3] = '='; break; case 2: out[0] = base64_table[in[0]>>2]; out[1] = base64_table[((in[0]<<4)|(in[1]>>4))&63]; out[2] = base64_table[(in[1]<<2)&63]; out[3] = '='; break; case 3: out[0] = base64_table[(in[0]>>2)&63]; out[1] = base64_table[((in[0]<<4)|(in[1]>>4))&63]; out[2] = base64_table[((in[1]<<2)|(in[2]>>6))&63]; out[3] = base64_table[in[2]&63]; break; default: break; } } void decode(const char in[4], char* out, int* length) { *length = 0; int i; char n; for (i = 0; i < 4; ++i) { n = 0; if ('A' <= in[i] && in[i] <= 'Z') n = (in[i]-'A') ; else if ('a' <= in[i] && in[i] <= 'z') n = (in[i]-'a'+26); else if ('0' <= in[i] && in[i] <= '9') n = (in[i]-'0'+52); else if ('+' == in[i]) n = 62; else if ('/' == in[i]) n = 63; else if ('=' == in[i]) break; else throw std::runtime_error("Base64 encoding is invalid."); switch (i) { case 0: out[0] = n << 2; *length = 1; break; case 1: out[0] |= (n >> 4); out[1] = (n << 4); if (out[1]) *length = 2; break; case 2: out[1] |= (n >> 2); out[2] = (n << 6); if (out[2]) *length = 3; break; case 3: out[2] |= n; *length = 3; break; default: break; } } } private: std::ostream* os_; std::istream* is_; int cols_; bool mime_; char buffer_[4]; }; class base64_encode_filter : public std::ostream { public: explicit base64_encode_filter(std::ostream& os) : std::ostream(&buf_), buf_(os) {} private: base64_filter_buffer buf_; }; class base64_decode_filter : public std::istream { public: explicit base64_decode_filter(std::istream& is) : std::istream(&buf_), buf_(is) {} private: base64_filter_buffer buf_; };