/** * Packet.java * * Copyright (C) 2001-2002,2006 佐藤 雅俊 / しいしせねっと * * このプログラムはフリーソフトウェアです。あなたはこれを、フリーソフトウェ * ア財団によって発行された GNU 一般公衆利用許諾契約書(バージョン2か、希 * 望によってはそれ以降のバージョンのうちどれか)の定める条件の下で再頒布 * または改変することができます。 * * このプログラムは有用であることを願って頒布されますが、*全くの無保証* * です。商業可能性の保証や特定の目的への適合性は、言外に示されたものも含 * め全く存在しません。詳しくはGNU 一般公衆利用許諾契約書をご覧ください。 * * あなたはこのプログラムと共に、GNU 一般公衆利用許諾契約書の複製物を一部 * 受け取ったはずです。もし受け取っていなければ、フリーソフトウェア財団ま * で請求してください(宛先は the Free Software Foundation, Inc., 59 * Temple Place, Suite 330, Boston, MA 02111-1307 USA)。 * */ package net.siisise.io; import java.io.*; /** * 効率よくデータを封入するためのクラス。 * ストリーム型で利用する。 * Javaのシリアライゼーションにちょっと似ている。 * FIFOと同様の機能をクライアント/サーバ間で効率よく実現するために開発する。 * ファイル互換の上位バージョンはPacket2として別に開発してみるかもしれない。 * * アクセス方法はファイルシステム以上の概念を持つ。 * * 読み書き2つのスレッドからの同時アクセスまでを保証する。 * スタック系のような任意の位置を読み書きする機能は持たない。 * * 2006/11/9 GPLライセンスを適用 * * Copyright(C) 2001-2002,2006 siisise.net * * @auther Masatoshi Sato */ public class Packet { byte[][] data; private final int buffersize = 64; int read, write; int readp; /** データ長 */ int length; String encode; public InputStream getInputStream() { InputStream in = new InputStream() { public int read() throws IOException { return Packet.this.read(); } public int read(byte[] data, int offset, int length) { return Packet.this.read(data, offset, length); } public int available() { return length(); } }; return in; } public OutputStream getOutputStream() { OutputStream out = new OutputStream() { public void write(int data) { Packet.this.write((byte)data); } public void write(byte[] data, int offset, int length) { Packet.this.write(data, offset, length); } }; return out; } /** * 微妙なクラス * 内部でwrite() を繰り返し呼び出す処理などがあるとデータが逆転するので * パイプにしない方がいい */ public OutputStream getBackOutputStream() { OutputStream out = new OutputStream() { public void write(int data) { byte[] d = new byte[1]; d[0] = (byte)data; Packet.this.bwrite(d); } /** * write(int) を呼び出すと逆順になってしまう */ public void write(byte[] data, int offset, int length) { Packet.this.bwrite(data, offset, length); } }; return out; } public Packet() { data = new byte[buffersize][]; read = 0; readp = 0; write = 0; length = 0; encode = "Shift_JIS"; } public void write(byte dt) { byte[] d; d = new byte[1]; d[0] = dt; write(d); } /** 書き込み */ public void write(byte[] dt) { write(dt,0,dt.length); } /** * 書き込み基本. * dt の中身は変更されない * 長さが 0 の場合も対応している? */ public void write(byte[] dt, int st, int len) { if (read == ((write + 1) & (buffersize - 1))) { // 空きなし byte d2[] = new byte[length]; read(d2); write(d2); } data[write] = new byte[dt.length]; System.arraycopy(dt,0,data[write],st,len); write++; write &= (buffersize - 1); length += dt.length; // 読み許可 } /** * このパケットのデータサイズを返す。 * @deprecated length() */ public int getSize() { return length; } public int length() { return length; } /** * 読みすぎたデータを書き戻したり、ヘッダを付けたり(要LOCK?) */ public void bwrite(byte[] dt, int offset, int len) { byte[] d; if (read == ((write + 1) & (buffersize - 1))) { // 空きなし d = new byte[length]; read(d); write(dt, offset, len); write(d); } else { if (readp > 0) { if (len > readp) { // System.arraycopy(dt, offset + len - d = new byte[len - readp]; System.arraycopy(dt, offset, d, 0, offset + len - readp); System.arraycopy(dt, offset + len - readp, data[read], 0, readp); readp = 0; read --; read &= (buffersize - 1); data[read] = d; length += len; } else { System.arraycopy(dt, offset, data[read], readp - len, len); readp -= len; length += len; } } else { // 2002.9.9 追加修正 d = new byte[len]; System.arraycopy(dt,offset, d, 0, offset + len); read--; read &= (buffersize - 1); data[read] = d; length += len; } } } public void bwrite(byte[] dt) { bwrite(dt, 0, dt.length); } public void bwrite(int dt) { byte[] d = new byte[1]; d[0] = (byte)dt; bwrite(d, 0, 1); } /** * 任意の位置にデータを加える * p がフルサイズより大きいときは、最後に加える。 * 新機能 */ void mwrite(int p, byte[] dt, int st, int len) { byte[] d1, d2; if (p < length()) { d1 = new byte[p]; } else { d1 = new byte[length()]; } read(d1); bwrite(dt,st,len); bwrite(d1); } /** * 中間または最後から取り出す。(未完成) * 読み出したデータはPacketから切り取られ消える。 * test: * p が 0 や - のときは? * dt がnull やサイズ 0 のときは? * Packet が短いときは? * いずれもデータが破壊されることはない */ int mread(int p, byte[] dt) { byte[] d; int len1, len2; d = new byte[p]; len1 = read(d); len2 = read(dt); bwrite(d,0,len1); return len2; } /** * 最後から取る。 * test: * da やpacket のサイズが0でも動作する * offset や len が範囲外では? 無理 */ public int read(byte[] dt, int offset, int len) { int rlen = 0; //int rp = 0; int siz; if (offset >= dt.length && len > 0) { return 0; } if (offset + len > dt.length) { // エラーにする? len = dt.length - offset; } while (length > 0 && (siz = data[read].length - readp) <= len - rlen) { System.arraycopy(data[read], readp, dt, offset + rlen, siz); rlen += siz; length -= siz; read ++; readp = 0; read &= (buffersize - 1); } if (length > 0 && dt.length - rlen > 0) { siz = dt.length - rlen; System.arraycopy(data[read], readp, dt, offset + rlen, siz); length -= siz; rlen += siz; readp += siz; } return rlen; } public int read(byte[] data) { return read(data, 0, data.length); } /** * 1バイト読み込み * -1 はデータなし * 使いたくない */ public int read() { byte[] dt = new byte[1]; int ret; if (read(dt) > 0) { ret = (int)dt[0] & 0xff; } else { ret = -1; } return ret; } public int readInt() { int i; i = read() << 24; i += (read() & 0xff) << 16; i += (read() & 0xff) << 8; i += (read() & 0xff); return i; } public void writeInt(int i) { byte[] dt = new byte[4]; dt[0] = (byte)(i >> 24); dt[1] = (byte)(i >> 16); dt[2] = (byte)(i >> 8); dt[3] = (byte)(i); write(dt); } public void bwriteInt(int i) { byte[] dt = new byte[4]; dt[0] = (byte)(i >> 24); dt[1] = (byte)(i >> 16); dt[2] = (byte)(i >> 8); dt[3] = (byte)(i); bwrite(dt); } public void write(String str) { byte[] dt; try { dt = str.getBytes(encode); writeInt(dt.length); write(dt); } catch (UnsupportedEncodingException e) { } } /** * 文字列読込み */ public String readString() { int len = readInt(); byte[] da = new byte[len]; read(da); try { return new String(da, encode); } catch (IOException e) { return null; } } /** * 文字コードを指定する。 */ public void setEncode(String enc) { if (enc != null) { encode = enc; } } /** * 文字コードの取得。 */ public String getEncode() { return encode; } /** サブパケットの取り出し */ public Packet readPacket() { int len; Packet pac; len = readInt(); if (len <= length()) { byte[] d; pac = new Packet(); d = new byte[len]; read(d); pac.write(d); } else { bwriteInt(len); pac = null; } return pac; } /** パケットの書き込み サイズ情報を付け加えて、そのまま格納する。 サイズ情報が二重にならないよう注意してください。 元のPacketに(最終的には)変更は加えない。 */ void writePacket(Packet p) { byte[] dt; int size; size = p.length(); // サイズの確認 writeInt(size); // サイズ書き込み dt = new byte[size]; p.read(dt); // データ抜き出し p.write(dt); // データ書き戻し write(dt); // データ書き込み } /** 複製を作成する。 */ public Packet copy() { Packet p; byte[] dt; dt = new byte[length()]; read(dt); write(dt); p = new Packet(); p.write(dt); return p; } }