/**
 * $Id: ReadWriteBuffer.java,v 1.1 2005/04/30 14:40:08 rampil Exp $
 * LOGICAL-PARADOX.ORG
 * Copyright (C)2004 satoshi akabane(akabane@logical-paradox.org)
 *
 */
package org.logical_paradox.common.net;

/**
 * 読み書きバッファ
 * 読み込みと書き込みが同時にできるバッファ
 * @author satoshi akabane@logical-paradox.org
 * @version $Revision: 1.1 $
 */
public class ReadWriteBuffer {
	/** デフォルトのブロックサイズ(4K) */
	public static final int DEFAULT_BLOCK_SIZE = 4096;
	/** バッファ */
	private byte[] buffer;
	/** ブロックサイズ(領域拡張時に追加されるバッファのバイト数) */
	private int blocksize;

	/** 読み込み時のポインタ */
	private int readp = 0;
	/** 書き込み時のポインタ */
	private int writep = 0;

	/**
	 * コンストラクタ
	 */
	public ReadWriteBuffer() {
		blocksize = DEFAULT_BLOCK_SIZE;
		buffer = new byte[blocksize];
	}
	/**
	 * コンストラクタ
	 * @param bs
	 */
	public ReadWriteBuffer(int bs) {
		blocksize = bs;
		buffer = new byte[blocksize];
	}
	/**
	 * バッファに1バイト書き込む
	 * @param b 書き込むデータ
	 */
	public void write(byte b) {
		write((int)b & 0xff);
	}
	/**
	 * バッファに1バイト書き込む
	 * @param i 書き込むデータ
	 */
	public void write(int i) {
		expand();
		buffer[writep++] = (byte)(i & 0xff);
	}
	/**
	 * バッファに配列のデータを書き込む
	 * @param b 書き込むデータ
	 */
	public void write(byte[] b) {
		for(int i = 0; i < b.length; i++) {
			write(b[i]);
		}
	}
	/**
	 * バッファに文字列を書き込む
	 * @param str 書き込む文字列
	 */
	public void write(String str) {
		write(str.getBytes());
	}
	/**
	 * バッファから1バイト読み込んで返す
	 * 読み込むものがない場合，-1を返す．
	 * 読み込み完了と同時に，読み込み用のポインタを1バイト進める
	 * @return バッファから読み込まれたデータ(-1: もうない)
	 */
	public int read() {
		reduce();
		if(readp == writep) {
			return -1;
		} else {
			return (int)buffer[readp++] & 0xff;
		}
	}
	/**
	 * バッファからデータょ読み込んで配列に格納する
	 * 最大で配列のサイズ分を読み込んで返す
	 * @param bf バッファ
	 * @return 実際に格納されたデータ数(単位:バイト)
	 */
	public int read(byte[] bf) {
		int p = 0;
		for(int i = 0; i < bf.length; i++) {
			int d = read();
			if(d < 0) {
				// これ以上読み込むものがなかった場合
				return i;
			} else {
				bf[i] = (byte)(d & 0xff);
			}
		}
		// 配列のサイズ分の読み込みに成功した場合
		return bf.length;
	}
	/**
	 * 領域を拡張する
	 */
	protected void expand() {
		if(writep == buffer.length) {
			// 書き込むスペースがない場合，領域拡張を実行する
			byte[] newBuffer = new byte[buffer.length + blocksize];
			System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
			buffer = newBuffer;
		}
	}
	/**
	 * 未使用領域を縮小してバッファを再構成する
	 */
	protected void reduce() {
		// 3ブロック以上の空き領域ができている場合，領域を縮小する
		if(readp >= blocksize * 3) {
			// 領域縮小
			byte[] newBuffer = new byte[buffer.length - blocksize * 3];
			System.arraycopy(buffer, blocksize * 3, newBuffer, 0, buffer.length - blocksize * 3);
			buffer = newBuffer;
			// read/writeポインタを補正する
			readp -= blocksize * 3;
			writep -= blocksize * 3;
		}
	}
}

