#ifndef RING_BUFFER_H
#define RING_BUFFER_H

/*!
  \file
  \brief リングバッファ
  
  \author Satofumi KAMIMURA
  
  $Id: ringBufferTemplate.h 286 2008-10-20 09:40:22Z satofumi $
*/


/*!
  \brief 固定サイズなリングバッファのテンプレート
*/
template<class C> class RingBuffer {
  typedef unsigned long size_type;
  size_type real_size;
  size_type first;
  size_type last;
  C *buffer;
  RingBuffer(void);

public:
  /*!
    \brief バッファサイズ指定のコンストラクタ

    \param size [i] リングバッファサイズ
  */
  RingBuffer(size_type size) : real_size(1), first(0), last(0) {
    while (size > (real_size -1)) {
      real_size <<= 1;
    }
    buffer = new C[real_size];
  }

  ~RingBuffer(void) {
    delete [] buffer;
  }

  /*!
    \brief バッファのデータサイズ

    \retval バッファのデータサイズ
  */
  size_type size(void) {
    return (last >= first) ? last - first : real_size - (first - last);
  }

  /*!
    \brief バッファの空きサイズ

    \retval バッファの空きサイズ
  */
  size_type free_size(void) {
    return real_size -1 - size();
  }

  /*!
    \brief バッファへデータ配置

    \param data [i] データ
    \param size [i] データサイズ
  */
  void put(const C* data, size_type size) {
    size_type free_length = free_size();
    size_type len = (size > free_length) ? free_length : size;

    size_type put_end = (len > (real_size - last)) ? real_size : last + len;
    C *p = &buffer[last];
    C *q = (C *)data;
    for (size_type i = last; i < put_end; ++i) {
      *p++ = *q++;
    }

    C *data_last = (C *)data + len;
    if (q < data_last) {
      p = &buffer[0];
      while (q < data_last) {
	*p++ = *q++;
      }
    }
    last = static_cast<size_type>((p - &buffer[0]) & (real_size -1));
  }

  /*!
    \brief バッファサイズを変更せずにデータ取り出し

    \param data [o] データ
    \param size [i] データサイズ

    \retval 取り出したデータサイズ
  */
  int copy(C* data, size_type size) {
    size_type filled_length = this->size();
    size_type len = (size > filled_length) ? filled_length : size;

    size_type get_end = (len > (real_size - first)) ? real_size : first + len;
    C *p = data;
    C *q = &buffer[first];
    for (size_type i = first; i < get_end; ++i) {
      *p++ = *q++;
    }

    C *data_last = data + len;
    if (p < data_last) {
      q = &buffer[0];
      while (p < data_last) {
	*p++ = *q++;
      }
    }
    return len;
  }

  /*!
    \brief バッファからデータ取り出し

    \param data [o] データ
    \param size [i] データサイズ

    \retval 取り出したデータサイズ
  */
  void get(C* data, size_type size) {
    int len = copy(data, size);
    first = (first + len) & (real_size -1);
  }

  /*!
    \brief バッファのクリア
  */
  void clear(void) {
    first = 0;
    last = 0;
  }
};

#endif /* !RING_BUFFER_H */
