base64_encode_sink
arbitrary_positional_facade::close()ができました。
class core_access { // これを追加 template<class RepositionalSink, class CharT> static void close_with_flush( RepositionalSink& sink, const CharT* s, std::streamsize n) { return sink.close_with_flush(s, n); } }; template<class Derived, class CharT, std::streamsize MaxBlockSize> class arbitrary_positional_facade { // これを追加 void close() { BOOST_ASSERT(count_ < block_size_); core_access::close_with_flush(derived(), buffer_, count_); } };
これを使うと、base64エンコーダデバイスはこう書けます。
#include <hamigaki/iostreams/arbitrary_positional_facade.hpp> #include <boost/iostreams/detail/closer.hpp> #include <boost/iostreams/write.hpp> #include <boost/integer.hpp> namespace hamigaki { namespace iostreams { template<class Sink> class base64_encode_sink : public arbitrary_positional_facade<base64_encode_sink<Sink>, char, 3> { friend core_access; // 24ビット以上の精度を持つ整数 typedef typename boost::uint_t<24>::fast uint24_t; public: typedef char char_type; struct category : public boost::iostreams::output , public boost::iostreams::device_tag , public boost::iostreams::closable_tag {}; explicit base64_encode_sink(const Sink& sink) : sink_(sink) {} private: Sink sink_; static uint24_t char_to_uint24(char c) { return static_cast<uint24_t>(static_cast<unsigned char>(c) & 0xFF); } // 3バイト -> 4文字 static void encode(char* dst, const char* src) { const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; uint24_t tmp = (char_to_uint24(src[0]) << 16) | (char_to_uint24(src[1]) << 8) | (char_to_uint24(src[2]) ) ; dst[0] = table[(tmp >> 18) & 0x3F]; dst[1] = table[(tmp >> 12) & 0x3F]; dst[2] = table[(tmp >> 6) & 0x3F]; dst[3] = table[(tmp ) & 0x3F]; } std::streamsize write_blocks(const char* s, std::streamsize n) { for (int i = 0; i < n; ++i) { char buf[4]; encode(buf, s); boost::iostreams::write(sink_, buf, sizeof(buf)); s += 3; } return n; } void close_with_flush(const char* s, std::streamsize n) { // 最初に例外が発生したときにtrueにするフラグ bool nothrow = false; // RAIIでclose()の呼び出しを確実に行う boost::iostreams::detail:: external_closer<Sink> close_sink(sink_, BOOST_IOS::out, nothrow); try { if (n != 0) { char src[3] = { 0, 0, 0 }; std::copy(s, s+n, &src[0]); char buf[4]; encode(buf, src); if (n == 1) buf[2] = '='; buf[3] = '='; boost::iostreams::write(sink_, buf, sizeof(buf)); } } catch (...) { nothrow = true; throw; } } }; template<class Sink> inline base64_encode_sink<Sink> base64_encoded(const Sink& sink) { return base64_encode_sink<Sink>(sink); } } } // End namespaces iostreams, hamigaki.
close_with_flush()が少し複雑ですが、それ以外は定義そのままです。
close_with_flush()が複雑なのは、余ったデータの処理とデバイスを閉じる処理の2つを行っているからで、フィルタとして実装すればboost::iostreams::compositeに任せることのできる箇所です。
フィルタ用のarbitrary_positional_facadeも欲しいですね。