///////////////////////////////////////////////////////////////////////////////
//
//
//   bitwise stream reader/writer は，ストリームを動的にバインドして拡張する
//



#ifndef BSTREAM_H
#define BSTREAM_H


#include "stream.h"
#include <stdio.h>


typedef unsigned long m_ui32;
typedef unsigned short m_ui16;
typedef unsigned char m_ui8;
///////////////////////////////////////////////////////////////////////////////
// このファイル限りの定義 -- 最後に undef している

#define UNEXPECTED_EOF_EXCEPTION()     \
  do {                                 \
    puts("unexpected eof exception");  \
    exit(1);                           \
  } while(0)


#define IO_FAILED_EXCEPTION()    \
  do {                           \
    puts("io failed exception"); \
    exit(1);                     \
  } while (0)

#define NULL_POINTER_EXCEPTION()     \
  do {                               \
    puts("null pointer exceptioin"); \
    exit(1);                         \
  }while (0)

///////////////////////////////////////////////////////////////////////////////
class BitwiseInputStreamReader {

#define BISR_BUFSIZE 0x1000
#define BISR_BUFMASK 0xfff
  unsigned char buf_[BISR_BUFSIZE];
  unsigned int buffered_;
  unsigned int pointer_;
  unsigned int bitpos_;

#define vofs(_o) buf_[(pointer_ + (_o)) & BISR_BUFMASK]

  InputStream *stream_;

  /*! least_bits の読み込みを保証する
   * @param least}_bits 読み込みを保証したいビット数
   * @exception UNEXPECTED_EOF_EXCEPTION  保証できなかった場合
   * @exception ... stream->read() に記述された例外
   */
  void required( unsigned int least_bits ) {
    unsigned int readable_bits = ((buffered_ - pointer_) << 3) - bitpos_;

    if ( least_bits <= readable_bits ) // まだ余裕はあるか？
      return;                          // まだ余裕がある -> return

    unsigned int bufd = buffered_ & BISR_BUFMASK;
    unsigned int pnt  = pointer_ & BISR_BUFMASK;

    if (pnt <= bufd) {
      buffered_ += stream_->read(buf_ + bufd, BISR_BUFSIZE - bufd);
      bufd = buffered_ & BISR_BUFMASK;
    }

    if (bufd < pnt) {
      buffered_ += stream_->read(buf_ + bufd, pnt - bufd);
    }

    // 最後にもう一度チェック  -> UnexpectedEOFException ?
    readable_bits = ((buffered_ - pointer_) << 3) - bitpos_;
    if ( readable_bits < least_bits ) {
      UNEXPECTED_EOF_EXCEPTION();
    }
  }

public:

  //! コンストラクタ  -- InputStream 指定
  BitwiseInputStreamReader(InputStream *stream){
    buffered_ = 0;
    pointer_ = 0;
    bitpos_ = 0;
    stream_ = stream;
  }



  //! デストラクタ
  virtual ~BitwiseInputStreamReader(){
    delete stream_;
  }


  //! ビット読み込み位置はバイト境界に揃っている？
  bool isAligned() {
    return (bitpos_ == 0);
  }

  //! ビット読み込み位置が中途半端の場合，次のバイトの先頭から
  void beAligned() {
    if (bitpos_) {
      ++pointer_;
      bitpos_ = 0;
    }
  }



protected:
  //!  1ビット読む
  m_ui32 get_1bit(unsigned int *pnt, unsigned int *bpos) {
    m_ui32 result = 0;
    unsigned char m = 1 << ((8 - *bpos) - 1);

    required(1);

    
    if (buf_[*pnt & BISR_BUFMASK] & m) {
      result = 1;
    }

    if ( ++(*bpos) == 8 ) {
      *bpos = 0;
      ++*pnt;
    }

    return result;
  }


public:
  /*! 1 ビット読む
   * ただし読み込み位置などは一切変化しない
   */
  m_ui32 show1() {
    unsigned int pnt = pointer_;
    unsigned int bpos = bitpos_;
    return get_1bit(&pnt, &bpos);
  }


  //! 1 ビット読む
  m_ui32 read1() {
    return get_1bit(&pointer_, &bitpos_);
  }




protected:
  //! 任意のビットを読み込む
  m_ui32 get_bits(unsigned int bits, unsigned int *pnt, unsigned int *bpos) {
    m_ui32 mask, mshift, n;
    m_ui32 result = 0;

    required(bits);

    while (bits) {
      if ((*bpos + bits) & 120) { // 120 = 1111000(2)
        n = 8 - *bpos;
      } else {
        n = bits;
      }
      mshift = 8 - (*bpos + n);
      mask = ((1 << n) - 1) << mshift;

      bits -= n;
      if (mshift < bits) {
        result |= ((m_ui32)(buf_[*pnt & BISR_BUFMASK] & mask) << (bits - mshift));
      }else {
        result |= ((m_ui32)(buf_[*pnt & BISR_BUFMASK] & mask) >> (mshift - bits));
      }

      *bpos += n;
      // assert *bpos <= 8
      if (*bpos == 8){
        ++*pnt;
        *bpos = 0;
      }
    }

    return result;
  }


public:
  /*! 任意のビットを読み込む
   * ただし読み込み位置などは一切変化しない
   */
  m_ui32 show(unsigned int bits) {
    unsigned int pnt = pointer_;
    unsigned int bpos = bitpos_;
    return get_bits( bits, &pnt, &bpos );
  }


  //! 任意のビットを読み込む
  m_ui32 read(unsigned int bits) {
    return get_bits(bits, &pointer_, &bitpos_);
  }



  /*! アラインして 8 ビット読む
   * 読み込み位置はアラインされる以外変化しない
   */
  m_ui32 show8() {
    beAligned();
    required(8);
    return (m_ui32)buf_[pointer_ & BISR_BUFMASK];
  }


  //! アラインして 8 ビット読む
  m_ui32 read8() {
    m_ui32 result = show8();
    ++pointer_;
    return result;
  }



  /*! アラインして 16 ビット読み込む
   * 読み込み位置はアラインされる以外一切変化しない
   */
  m_ui32 show16() {
    beAligned();
    required(16);
    return (vofs(0) << 8) | vofs(1);
  }


  //! アラインして16ビット読み込む
  m_ui32 read16() {
    m_ui32 result = show16();
    pointer_ += 2;
    return result;
  }



  /*! アラインして32ビット読む
   * 読み込み位置はアラインされる以外一切変化しない
   */
  m_ui32 show32() {
    beAligned();
    required(32);
    return (vofs(0) << 24) | (vofs(1) << 16) | (vofs(2) << 8) | vofs(3);
  }

  //! アラインして32ビット読む
  m_ui32 read32() {
    m_ui32 result = show32();
    pointer_ += 4;
    return result;
  }

};
#undef vofs

#undef BISR_BUFSIZE
#undef BISR_BUFMASK





///////////////////////////////////////////////////////////////////////////////

static inline void _be_zero(void *mem, unsigned int size) {
  unsigned int *im = (unsigned int *)mem;
  while (sizeof(unsigned int) <= size) {
    *im++ = 0;
    size -= sizeof(unsigned int);
  }

  unsigned char *cm = (unsigned char *)im;
  while (size--)
    *cm++ = 0;
}



class BitwiseOutputStreamWriter {
#define BOSW_BUFSIZE  0x1000
#define BOSW_BUFMASK   0xfff
  unsigned char buf_[BOSW_BUFSIZE];
  unsigned int stored_;
  unsigned int pointer_;
  unsigned int bitpos_;

  OutputStream *stream_;


  /*! bits ビット書き込み可能であることを保証
   * @param bits 書き込み可能を保証したいビット数
   * @excetion IOFailedException -- bits ビットの書き込みが保証できなかった場合
   */
  void required(unsigned int bits) {
    unsigned int writable = ((BOSW_BUFSIZE - (pointer_ - stored_)) << 3) - bitpos_;

    if (bits <= writable)
      return; // まだ余裕がある

    unsigned int srd = stored_ & BOSW_BUFMASK;
    unsigned int ptr = pointer_ & BOSW_BUFMASK;
    if (ptr <= srd) {
      unsigned int wt = stream_->write(&buf_[srd], BOSW_BUFSIZE - srd);
      _be_zero(&buf_[srd], wt);
      stored_ += wt;
      srd = stored_ & BOSW_BUFMASK;
      // assert srd == 0
    }

    if (srd < ptr) {
      unsigned int wt = stream_->write(&buf_[srd], ptr - srd);
      _be_zero(&buf_[srd], wt);
      stored_ += wt;
    }

    writable = ((BOSW_BUFSIZE - (pointer_ - stored_)) << 3) - bitpos_;
    if (writable < bits) {
      IO_FAILED_EXCEPTION();
    }
  }

public:



  /*! コンストラクタ
   * @param stream 出力先のストリーム
   * @exception NullPointerException -- stream が 0 の場合
   */
  BitwiseOutputStreamWriter( OutputStream *stream ) {
    _be_zero(buf_, BOSW_BUFSIZE);

    bitpos_ = 0;
    pointer_ = 0;
    stored_ = 0;
    stream_ = stream;

    if (!stream) {
      NULL_POINTER_EXCEPTION();
      return;
    }
  }



  //! デストラクタ
  ~BitwiseOutputStreamWriter() {
    flush();
    delete stream_;
  }



  /*! バッファに残っている値を本当にストリームに書き出してしまう
   * @exception stream.write() に記述された例外
   */
  void flush() {
    if ( bitpos_ ) {
      bitpos_ = 0;
      ++pointer_;
    }
    if (stored_ == pointer_) return;

    unsigned int srd = stored_ & BOSW_BUFMASK;
    unsigned int ptr = pointer_ & BOSW_BUFMASK;
    if (ptr <= srd) {
      unsigned int wt = stream_->write(&buf_[srd], BOSW_BUFSIZE - srd);
      _be_zero(&buf_[srd], wt);
      stored_ += wt;
      srd = stored_ & BOSW_BUFMASK;
      // assert srd == 0
    }

    if (srd < ptr) {
      unsigned int wt = stream_->write(&buf_[srd], ptr - srd);
      _be_zero(&buf_[srd], wt);
      stored_ += wt;
    }
  }



  void beAligned() {
    if (bitpos_) {
      bitpos_ = 0;
      ++pointer_;
    }
  }


  /*! 任意のビット数を書き出す
   * @param v 書き込む値
   * @param bits 書き込むビット数
   * @exception IOException 出力エラー
   */
  void write(m_ui32 v, unsigned int bits) {
    unsigned int bshift, mask, n;
    unsigned int vbpos = 32 - bits;

    required(bits);

    while (bits) {
      if ((bits + bitpos_) & 120)  n = 8 - bitpos_;
                             else  n = bits;

      bshift = 8 - (bitpos_ + n);
      bits -= n;
      mask = ((1 << n) - 1) << bits;
      if (bits < bshift) {
        buf_[pointer_ & BOSW_BUFMASK] |= (unsigned char)((v & mask) << (bshift - bits));
      } else {
        buf_[pointer_ & BOSW_BUFMASK] |= (unsigned char)((v & mask) >> (bits - bshift));
      }

      bitpos_ += n;

      // assert bitpos_ <= 8
      if (bitpos_ == 8) {
        bitpos_ = 0;
        ++pointer_;
      }
    }
  }



  /*! ( 必要ならアラインして ) 8 ビット書き出す
   * @param v 書き出す値 -- 有効なのは下位 8 ビットのみ
   * @exception IOException
   */
  void write8(m_ui32 v) {
    if (bitpos_) {
      bitpos_ = 0;
      ++pointer_;
    }

    required(8);

    buf_[pointer_ & BOSW_BUFMASK] = (unsigned char)(v & 0xff);  ++pointer_;
  }


  /*! ( 必要ならアラインして ) 16 ビット書き出す
   * @param v 書き出す値 -- 有効なのは下位16ビットのみ
   * @exception IOException
   */
  void write16(m_ui32 v) {
    if (bitpos_) {
      bitpos_ = 0;
      ++pointer_;
    }

    required(16);

    buf_[pointer_ & BOSW_BUFMASK] = (unsigned char)((v >> 8) & 0xff);  ++pointer_;
    buf_[pointer_ & BOSW_BUFMASK] = (unsigned char)( v       & 0xff);  ++pointer_;
  }


  /*! ( 必要ならアラインして ) 32 ビット書き出す
   * @exception IOException
   */
  void write32(m_ui32 v) {
    if (bitpos_) {
      bitpos_ = 0;
      ++pointer_;
    }

    required(32);

    buf_[pointer_ & BOSW_BUFMASK] = (unsigned char)((v >>24) & 0xff);  ++pointer_;
    buf_[pointer_ & BOSW_BUFMASK] = (unsigned char)((v >>16) & 0xff);  ++pointer_;
    buf_[pointer_ & BOSW_BUFMASK] = (unsigned char)((v >> 8) & 0xff);  ++pointer_;
    buf_[pointer_ & BOSW_BUFMASK] = (unsigned char)( v       & 0xff);  ++pointer_;
  }

#undef BOSW_BUFSIZE
#undef BOSW_BUFMASK

};





#undef UNEXPECTED_EOF_EXCEPTION
#undef IO_EXCEPTION


#endif // BSTREAM_H


