#ifndef __CATCAM_STREAM_H__ #define __CATCAM_STREAM_H__ #include #include #include #include #include "util/endian.h" #include "util/crc.h" template< class _Elem, class _Traits> class basic_CatCAMStreambuf : public std::basic_streambuf<_Elem, _Traits> { public: static const unsigned char header[]; static const unsigned int header_size; static const unsigned int capsule_head_size; static const unsigned int capsule_tail_size; static const unsigned int capsule_size; protected: typedef std::basic_streambuf<_Elem, _Traits> super_t; typedef std::streamsize streamsize; typedef typename super_t::int_type int_type; typedef unsigned char v_u8_t; typedef unsigned short v_u16_t; const bool mode_out; std::ostream *out; std::istream *in; unsigned int buffer_size; _Elem *buffer; unsigned int sequence_num; bool sequence_num_lock; using super_t::pbase; using super_t::pptr; using super_t::epptr; using super_t::pbump; using super_t::setp; int_type overflow(int_type c = _Traits::eof()){ // エンコードを担当 //std::cerr << "overflow()" << std::endl; v_u8_t sequence_num_u8(sequence_num); memcpy(&(buffer[header_size]), &sequence_num_u8, sizeof(v_u8_t)); v_u16_t crc_u16(CRC16::crc16( (unsigned char *)&buffer[header_size], buffer_size - capsule_tail_size - header_size)); v_u16_t crc_u16_le( le_char2_2_num(*(char *)&crc_u16)); memcpy(&(buffer[buffer_size - capsule_tail_size]), &crc_u16_le, sizeof(v_u16_t)); #if DEBUG > 3 std::cerr << sequence_num_u8 << std::endl; std::cerr << crc_u16 << "=>" << crc_u16_le << std::endl; std::cerr << "--write--" << std::endl; std::cerr << std::hex; for(int i = 0; i < buffer_size; i++){ std::cerr << std::setfill('0') << std::setw(2) << (unsigned int)((unsigned char)buffer[i]) << ' '; } std::cerr << std::endl << std::dec; #endif // ヘッダ、シーケンス番号、本文、CRC16の順に送信する if(c != _Traits::eof()){ // "$$"があった場合は次の文字を無条件にエスケープする int count(0), written(0); bool previous_is_dollar(false); for(int index(0); index < buffer_size; index++){ count++; if(buffer[index] == '$'){ if(previous_is_dollar){ previous_is_dollar = false; if(!(out->write(buffer + written, count) .write("\\", 1) .good())){ return _Traits::eof(); } written += count; count = 0; }else{ previous_is_dollar = true; } }else{ previous_is_dollar = false; } } if(!(out->write(buffer + written, count))){ return _Traits::eof(); } setp(pbase(), epptr()); *pptr() = _Traits::to_char_type(c); pbump(1); if(!sequence_num_lock){sequence_num++;} return true; } return _Traits::eof(); } using super_t::eback; using super_t::gptr; using super_t::egptr; using super_t::setg; using super_t::gbump; int_type underflow(){ // デコードを担当 //std::cerr << "underflow()" << std::endl; bool header_checked(false); unsigned int buffer_used(0); unsigned int escape_check_index(0); while(in->good()){ #ifdef _WIN32 buffer_used += in->readsome(buffer + buffer_used, buffer_size - buffer_used); #else // MacOSXだとreadsomeの挙動があやしい? in->read(buffer + buffer_used, buffer_size - buffer_used); buffer_used += in->gcount(); #endif if(buffer_used < buffer_size){continue;} // ヘッダ確認 if(!header_checked){ if((unsigned char)buffer[0] != header[0]){ unsigned int i(1); for(; i < buffer_used; i++){ if((unsigned char)buffer[i] == header[0]){ break; } } if((buffer_used -= i)){ memmove(buffer, buffer + i, buffer_used); } continue; } if(((unsigned char)buffer[1] & 0xF0) != header[1]){ buffer_used -= 2; memmove(buffer, buffer + 2, buffer_used); continue; } } header_checked = true; // エスケープのチェック while(escape_check_index < buffer_size - 1){ if(buffer[escape_check_index++] == '$'){ if(buffer[escape_check_index++] == '$'){ if(escape_check_index < buffer_size){ buffer_used--; memmove(buffer + escape_check_index, buffer + escape_check_index + 1, buffer_used - escape_check_index); } break; } } } if(buffer_used < buffer_size){continue;} header_checked = false; escape_check_index = 0; // CRC確認 v_u16_t crc16_orig(le_char2_2_num( buffer[buffer_size - capsule_tail_size])); v_u16_t crc16_calc(CRC16::crc16( (unsigned char *)&(buffer[header_size]), buffer_size - capsule_tail_size - header_size)); if(crc16_orig != crc16_calc){ #if DEBUG > 3 std::cerr << "! " << std::hex << std::setfill('0') << std::setw(4) << (unsigned int)crc16_orig << " vs " << std::setfill('0') << std::setw(4) << (unsigned int)crc16_calc << std::endl; std::cerr << "--read--" << std::endl; std::cerr << std::hex; for(int i = 0; i < buffer_size; i++){ std::cerr << std::setfill('0') << std::setw(2) << (unsigned int)((unsigned char)buffer[i]) << ' '; } std::cerr << std::endl << std::dec; #endif buffer_used -= 2; memmove(buffer, buffer + 2, buffer_used); continue; } if(!sequence_num_lock){ sequence_num = buffer[header_size]; } setg(eback(), eback(), egptr()); return _Traits::to_int_type(*gptr()); } return _Traits::eof(); } #ifndef _WIN32 /*int_type uflow(){ int_type res(underflow()); if(_Traits::eq_int_type(_Traits::eof(), res)){ return _Traits::eof(); } gbump(1); return res; }*/ #endif public: /** * コンストラクタ * * 出力フィルタとして使用する場合。この時、エンコーダが活性化する。 * @param out 出力ストリーム */ basic_CatCAMStreambuf( std::ostream &_out, const unsigned int &page_size = 32) : mode_out(true), in(NULL), out(&_out), buffer_size(page_size + capsule_size), buffer(new _Elem[buffer_size]), sequence_num(0), sequence_num_lock(false) { setp(buffer + capsule_head_size, buffer + capsule_head_size + page_size); memcpy(buffer, header, header_size); } /** * コンストラクタ * * 入力フィルタとして使用する場合。この時、デコーダが活性化する。 * @param in 入力ストリーム * @param page_size ページサイズ */ basic_CatCAMStreambuf( std::istream &_in, const unsigned int &page_size = 32) : mode_out(false), in(&_in), out(NULL), buffer_size(page_size + capsule_size), buffer(new _Elem[buffer_size]), sequence_num(0), sequence_num_lock(false) { setg(buffer + capsule_head_size, buffer + capsule_head_size + page_size, buffer + capsule_head_size + page_size); } ~basic_CatCAMStreambuf(){ delete [] buffer; } unsigned int &sequence_number(){ return sequence_num; } bool &sequence_number_lock(){ return sequence_num_lock; } }; template< class _Elem, class _Traits> const unsigned char basic_CatCAMStreambuf<_Elem, _Traits>::header[] = {0xF7, 0xE0}; template< class _Elem, class _Traits> const unsigned int basic_CatCAMStreambuf<_Elem, _Traits>::header_size = sizeof(basic_CatCAMStreambuf<_Elem, _Traits>::header); template< class _Elem, class _Traits> const unsigned int basic_CatCAMStreambuf<_Elem, _Traits>::capsule_head_size = basic_CatCAMStreambuf<_Elem, _Traits>::header_size + 1; template< class _Elem, class _Traits> const unsigned int basic_CatCAMStreambuf<_Elem, _Traits>::capsule_tail_size = 2; template< class _Elem, class _Traits> const unsigned int basic_CatCAMStreambuf<_Elem, _Traits>::capsule_size = basic_CatCAMStreambuf<_Elem, _Traits>::capsule_head_size + basic_CatCAMStreambuf<_Elem, _Traits>::capsule_tail_size; typedef basic_CatCAMStreambuf > CatCAMStreambuf; class CatCAMIStream : public std::istream { public: typedef CatCAMStreambuf buf_t; protected: typedef std::istream super_t; buf_t buf; public: CatCAMIStream(std::istream &in, const unsigned int &page_size = 32) : buf(in, page_size), super_t(&buf){} ~CatCAMIStream(){} unsigned int current_sequence() const { return const_cast(buf).sequence_number(); } }; class CatCAMOStream : public std::ostream { public: typedef CatCAMStreambuf buf_t; protected: typedef std::ostream super_t; buf_t buf; public: CatCAMOStream(std::ostream &out, const unsigned int &page_size = 32) : buf(out, page_size), super_t(&buf){} ~CatCAMOStream(){} unsigned int &sequence() { return buf.sequence_number(); } bool &sequence_lock() { return buf.sequence_number_lock(); } }; #endif /* __CATCAM_STREAM_H__ */